@@ -2983,7 +2983,13 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
} fb (new_call.bindings);
if (*non_constant_p)
- return t;
+ {
+ if (cxx_dialect >= cxx20
+ && ctx->manifestly_const_eval == mce_false
+ && DECL_IMMEDIATE_FUNCTION_P (fun))
+ maybe_promote_function_to_consteval (current_function_decl);
+ return t;
+ }
/* We can't defer instantiating the function any longer. */
if (!DECL_INITIAL (fun)
but since I have to check mce_false, it didn't do anything useful
in practice (that is, it wouldn't escalate anything in my tests).
> > If a function is trivial, can it ever be promoted?
>
> This might be a question for CWG. clang thinks so:
>
> struct A {
> consteval A() = default;
> consteval A(const A&) = default;
> int i;
> };
> struct B : A { }; // B constructors promote to consteval
> B b; // ok, constant-initialization
> B b2(b); // error, immediate invocation isn't constant
Ah, here we elide the calls so there's nothing to promote. And I see
that clang++ gives errors for both b and b2. I left this be for now.
> > In v1, I addressed the delayed instantiation problem by instantiating
> > trees early, so that we can escalate functions right away. That caused
> > a number of problems, and in certain cases, like consteval-prop3.C, it
> > can't work, because we need to wait till EOF to see the definition of
> > the function anyway.
>
> Indeed. I briefly thought that maybe we could just poison consteval
> functions (declared or escalated) so that the back-end complains if we try
> to emit a reference to them, but that wouldn't catch optimized-away
> references.
>
> > Overeager instantiation tends to cause diagnostic
> > problems too.
>
> e.g. constexpr-inst1.C. Though I notice that clang and EDG do the eager
> instantiation for that testcase even in C++11 mode.
>
> Relatedly, for
>
> template <class T> consteval T id (T t) { return t; }
> template <class T> constexpr T f(T t);
> void g(int i) { f(42); }
> template <class T> constexpr T f(T t) { return id(t); }
>
> clang doesn't try to constant-evaluate f(42) because f wasn't defined yet at
> that point (as required by https://eel.is/c++draft/expr.const#5.4), just
> gives an error.
>
> And if f is explicitly declared consteval, we also reject it, so it seems
> surprising to be more permissive with promoted consteval.
Agreed.
> This also doesn't need to be resolved now.
I thought about this and I think the best approach would be to poison
f with an attribute, and give an error if we do escalate to consteval.
Hopefully such an attribute could be used elsewhere, too.
> > Therefore, we have to perform the escalation before gimplifying, but
> > after instantiate_pending_templates. That's not easy because we have
> > no way to walk all the trees. In the v2 patch, I use two vectors: one
> > to store function decls that may become consteval and another to > remember references to immediate-escalating functions. Unfortunately
> > the latter must also stash functions that call immediate-escalating
> > functions. Consider:
> >
> > int g(int i)
> > {
> > f<int>(i); // f is immediate-escalating
> > }
> >
> > where g itself is not immediate-escalating, but we have to make sure
> > that if f gets promoted to consteval, we give an error.
>
> I'm not sure you need the former list: if you immediately promote when you
> see a known immediate invocation, an i-e fn you might want to promote will
> be one that calls other i-e fns, so it'll also be on the second list.
True, I now only use one hash_set.
> > A new option, -fno-immediate-escalation, is provided to suppress
> > escalating functions.
> >
> > v2 also adds a new flag, DECL_ESCALATED_P, so that we don't escalate
> > a function multiple times, and so that we can distinguish between
> > explicitly consteval functions and functions that have been promoted
> > to consteval.
>
> That name suggests to me that it's been promoted, not just that we've
> checked it for promotion; maybe DECL_ESCALATION_{CHECKED,RESOLVED,KNOWN}_P?
Fair enough, renamed to DECL_ESCALATION_CHECKED_P.
> > @@ -55,6 +64,8 @@ enum fold_flags {
> > ff_mce_false = 1 << 1,
> > /* Whether we're being called from cp_fold_immediate. */
> > ff_fold_immediate = 1 << 2,
> > + /* Whether we're escalating immediate-escalating functions. */
> > + ff_escalating = 1 << 3,
>
> If we always escalate when we see a known immediate invocation, this means
> recurse. And maybe we could use at_eof for that?
Yes. Though, at_eof == 1 doesn't imply that all templates have been
instantiated, only that we got to c_parse_final_cleanups. Rather than
keeping the ff_ flag, I've updated at_eof to mean that 2 signals that
all templates have been instantiated, and 3 that we're into cgraph
territory. (Might should use an enum rather than magic numbers.)
> > };
> > using fold_flags_t = int;
> > @@ -428,6 +439,176 @@ lvalue_has_side_effects (tree e)
> > return TREE_SIDE_EFFECTS (e);
> > }
> > +/* Return true if FN is an immediate-escalating function. */
> > +
> > +static bool
> > +immediate_escalating_function_p (tree fn)
> > +{
> > + if (!fn || !flag_immediate_escalation)
> > + return false;
> > +
> > + gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL);
>
> Maybe check DECL_IMMEDIATE_FUNCTION_P early, rather than multiple times
> below...
>
> > + /* An immediate-escalating function is
> > + -- the call operator of a lambda that is not declared with the consteval
> > + specifier */
> > + if (LAMBDA_FUNCTION_P (fn) && !DECL_IMMEDIATE_FUNCTION_P (fn))
> > + return true;
> > + /* -- a defaulted special member function that is not declared with the
> > + consteval specifier */
> > + special_function_kind sfk = special_memfn_p (fn);
> > + if (sfk != sfk_none
> > + && DECL_DEFAULTED_FN (fn)
> > + && !DECL_IMMEDIATE_FUNCTION_P (fn))
> > + return true;
> > + /* -- a function that results from the instantiation of a templated entity
> > + defined with the constexpr specifier. */
> > + return is_instantiation_of_constexpr (fn);
> > +}
> > +
> > +/* Promote FN to an immediate function, including its clones, if it is
> > + an immediate-escalating function. Return true if we did promote;
> > + false otherwise. */
> > +
> > +static bool
> > +maybe_promote_function_to_consteval (tree fn)
> > +{
> > + if (fn
> > + && !DECL_IMMEDIATE_FUNCTION_P (fn)
>
> ...and in all the callers?
Ah, beautiful. And likewise for DECL_ESCALATION_CHECKED_P.
> > +static void
> > +maybe_store_cfun_for_late_checking ()
> > +{
> > + if (!current_function_decl
> > + || !flag_immediate_escalation
> > + || immediate_escalating_function_p (current_function_decl))
> > + return;
> > +
> > + if (deferred_escalating_exprs
> > + /* Don't put duplicates into the vec so that we don't walk
> > + a function multiple times. Most likely the last one
> > + added will be the same. */
> > + && (deferred_escalating_exprs->last () == current_function_decl
> > + || deferred_escalating_exprs->contains (current_function_decl)))
>
> Doing ->contains on a vec is slow, better to use a hash_set.
Sure, we have iterators so we can walk the hash set. Changed.
> > + return;
> > +
> > + vec_safe_push (deferred_escalating_exprs, current_function_decl);
> > +}
> > +
> > +/* Find an immediate-escalating expression or conversion in *TP.
> > + If DATA_ is non-null, this function will promote function to
> > + consteval as it goes; otherwise, we're just looking for what
> > + made a function consteval, for diagnostic purposes. This
> > + function assumes that *TP was instantiated. */
> > +
> > +static tree
> > +find_escalating_expr_r (tree *tp, int *walk_subtrees, void *data)
>
> Can this merge with cp_fold_immediate_r? They seem to be doing essentially
> the same thing.
I did not do it, sorry. I thought it would not make things more readable
as I don't see a whole lot of code to share.
> > @@ -485,6 +666,41 @@ cp_gimplify_arg (tree *arg_p, gimple_seq *pre_p, location_t call_location,
> > }
> > +/* Figure out if DECL should be promoted to consteval and if so, maybe also
> > + promote the function we are in currently. CALL is the CALL_EXPR of DECL.
> > + EVALP is where we may store the result of cxx_constant_value so that we
> > + don't have to evaluate the same tree again in cp_fold_immediate_r. */
> > +
> > +static void
> > +maybe_escalate_decl_and_cfun (tree decl, tree call, tree *evalp)
> > +{
> > + /* Compiler-generated functions don't seem like good candidates for
> > + promoting. */
> > + if (cp_unevaluated_operand || DECL_ARTIFICIAL (decl))
>
> As discussed above, we probably don't want to exclude implicitly-declared
> special member functions.
Done (but removing the DECL_ARTIFICIAL check).
> > @@ -1073,20 +1309,42 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
> > from cp_fold_r and we must let it recurse on the expression with
> > cp_fold. */
> > break;
> > +
> > case PTRMEM_CST:
> > - if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
> > - && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
> > - {
> > - if (!data->pset.add (stmt) && (complain & tf_error))
> > - {
> > - error_at (PTRMEM_CST_LOCATION (stmt),
> > - "taking address of an immediate function %qD",
> > - PTRMEM_CST_MEMBER (stmt));
> > - *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> > - }
> > - return error_mark_node;
> > - }
> > - break;
> > + case ADDR_EXPR:
> > + {
> > + tree decl = (code == PTRMEM_CST
> > + ? PTRMEM_CST_MEMBER (stmt)
> > + : TREE_OPERAND (stmt, 0));
> > + if (TREE_CODE (decl) != FUNCTION_DECL)
> > + break;
> > + if (code == ADDR_EXPR && ADDR_EXPR_DENOTES_CALL_P (stmt))
> > + break;
> > + if (immediate_invocation_p (decl))
> > + {
> > + if (maybe_promote_function_to_consteval (current_function_decl))
> > + break;
> > + if (complain & tf_error)
> > + {
> > + /* ??? Why don't we use data->pset for ADDR_EXPR too? */
>
> Does doing that affect the testsuite? My guess is that it was done for
> PTRMEM_CST to avoid duplicate errors, and isn't needed for ADDR_EXPR, but it
> also shouldn't hurt for ADDR_EXPR.
No visible change in the testsuite, so now we treat them the same.
> > + if (code == ADDR_EXPR || !data->pset.add (stmt))
> > + {
> > + taking_address_of_imm_fn_error (stmt, decl);
> > + *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> > + }
> > + /* If we're giving hard errors, continue the walk rather than
> > + bailing out after the first error. */
> > + break;
> > + }
> > + return error_mark_node;
> > + }
> > + /* Not consteval yet, but could become one, in which case it's invalid
> > + to take its address. */
> > + else if (!(data->flags & ff_fold_immediate)
>
> Why check ff_fold_immediate? Don't we want to do deferred checking of
> immediate invocations in discarded branches?
On reflection, I was papering over a different problem here.
Inserting a new element to a hash table while we're traversing it is
dangerous because a new element might mean calling hash_table::expand
which creates a new table, std::moves the elements, and calls ggc_free
on the old table. Then iterating to the next element is going to crash
because it has been freed.
We were trying to add a new element while being called from
process_pending_immediate_escalating_fns. This should not happen.
The problem was that the DECL_ESCALATION_CHECKED_P flag may not have
been properly set for functions without any CALL_EXPRs in them, and that
immediate_escalating_function_p was not checking it, either.
The code is just
+ else if (immediate_escalating_function_p (decl))
+ remember_escalating_expr (stmt);
now.
The problem triggered in Wfree-nonheap-object-3.C. Unfortunately, I could
not reduce it very far so I don't have a test suitable for the testsuite.
The tricky thing is that the problem only manifests when we expand() a hash
table which is unlikely to happen in a small test and I don't see a --param
I could use.
> > + && immediate_escalating_function_p (decl))
> > + vec_safe_push (deferred_escalating_exprs, stmt);
> > + break;
> > + }
> > /* Expand immediate invocations. */
> > case CALL_EXPR:
> > @@ -1094,33 +1352,54 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
> > if (tree fn = cp_get_callee (stmt))
> > if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn))
> > if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false))
> > - if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
> > - {
> > - stmt = cxx_constant_value (stmt, complain);
> > - if (stmt == error_mark_node)
> > - {
> > - if (complain & tf_error)
> > - *stmt_p = error_mark_node;
> > - return error_mark_node;
> > - }
> > - *stmt_p = stmt;
> > - }
> > - break;
> > -
> > - case ADDR_EXPR:
> > - if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
> > - && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))
> > - && !ADDR_EXPR_DENOTES_CALL_P (stmt))
> > - {
> > - if (complain & tf_error)
> > {
> > - error_at (EXPR_LOCATION (stmt),
> > - "taking address of an immediate function %qD",
> > - TREE_OPERAND (stmt, 0));
> > - *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> > + tree eval = NULL_TREE;
> > + if (data->flags & ff_escalating)
> > + maybe_escalate_decl_and_cfun (fndecl, stmt, &eval);
> > +
> > + /* [expr.const]p16 "An expression or conversion is
> > + immediate-escalating if it is not initially in an immediate
> > + function context and it is either
> > + -- an immediate invocation that is not a constant expression
> > + and is not a subexpression of an immediate invocation."
> > +
> > + If we are in an immediate-escalating function, the
> > + immediate-escalating expression or conversion makes it an
> > + immediate function. So STMT does not need to produce
> > + a constant expression. */
> > + if (immediate_invocation_p (fndecl))
> > + {
> > + tree e = eval ? eval : cxx_constant_value (stmt, tf_none);
> > + if (e == error_mark_node)
> > + {
> > + if (maybe_promote_function_to_consteval
> > + (current_function_decl))
> > + break;
> > + if (complain & tf_error)
> > + {
> > + auto_diagnostic_group d;
> > + location_t loc = cp_expr_loc_or_input_loc (stmt);
> > + error_at (loc, "call to consteval function %qE is "
> > + "not a constant expression", stmt);
> > + /* Explain why it's not a constant expression. */
> > + *stmt_p = cxx_constant_value (stmt, complain);
> > + maybe_explain_promoted_consteval (loc, fndecl);
> > + /* Don't return error_mark_node, it would stop our
> > + tree walk. */
> > + break;
> > + }
> > + return error_mark_node;
> > + }
> > + /* We've evaluated the consteval function call. */
> > + *stmt_p = e;
> > + }
> > + /* We've encountered a function call that may turn out to be
> > + consteval later. Store its caller so that we can ensure
> > + that the call is a constant expression. */
> > + else if (!(data->flags & ff_fold_immediate)
>
> Likewise.
Also fixed.
> It also seems odd that the ADDR_EXPR case calls vec_safe_push
> (deferred_escalating_exprs, while the CALL_EXPR case calls
> maybe_store_cfun_for_late_checking, why the different handling?
maybe_store_cfun_for_late_checking saves current_function_decl
so that we can check:
void g (int i) {
fn (i); // error if fn promotes to consteval
}
> > + && immediate_escalating_function_p (fndecl))
> > + maybe_store_cfun_for_late_checking ();
> > }
> > - return error_mark_node;
> > - }
> > break;
> > default:
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index e3fb2299d93..02c50ac6a11 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -5685,6 +5685,8 @@ trees_out::lang_decl_bools (tree t)
> > WB (lang->u.fn.has_dependent_explicit_spec_p);
> > WB (lang->u.fn.immediate_fn_p);
> > WB (lang->u.fn.maybe_deleted);
> > + /* We do not stream lang->u.implicit_constexpr nor
> > + lang->u.escalated_p. */
>
> Won't this mean not getting maybe_explain_promoted_consteval for imported
> functions?
I suppose so. Let's just stream it, then.
Bootstrapped/regtested on x86_64-pc-linux-gnu and powerpc64le-unknown-linux-gnu,
ok for trunk?
-- >8 --
This patch implements P2564, described at <wg21.link/p2564>, whereby
certain functions are promoted to consteval. For example:
consteval int id(int i) { return i; }
template <typename T>
constexpr int f(T t)
{
return t + id(t); // id causes f<int> to be promoted to consteval
}
void g(int i)
{
f (3);
}
now compiles. Previously the code was ill-formed: we would complain
that 't' in 'f' is not a constant expression. Since 'f' is now
consteval, it means that the call to id(t) is in an immediate context,
so doesn't have to produce a constant -- this is how we allow consteval
functions composition. But making 'f<int>' consteval also means that
the call to 'f' in 'g' must yield a constant; failure to do so results
in an error. I made the effort to have cc1plus explain to us what's
going on. For example, calling f(i) produces this neat diagnostic:
w.C:11:11: error: call to consteval function 'f<int>(i)' is not a constant expression
11 | f (i);
| ~~^~~
w.C:11:11: error: 'i' is not a constant expression
w.C:6:22: note: 'constexpr int f(T) [with T = int]' was promoted to an immediate function because its body contains an immediate-escalating expression 'id(t)'
6 | return t + id(t); // id causes f<int> to be promoted to consteval
| ~~^~~
which hopefully makes it clear what's going on.
Implementing this proposal has been tricky. One problem was delayed
instantiation: instantiating a function can set off a domino effect
where one call promotes a function to consteval but that then means
that another function should also be promoted, etc.
In v1, I addressed the delayed instantiation problem by instantiating
trees early, so that we can escalate functions right away. That caused
a number of problems, and in certain cases, like consteval-prop3.C, it
can't work, because we need to wait till EOF to see the definition of
the function anyway. Overeager instantiation tends to cause diagnostic
problems too.
In v2, I attempted to move the escalation to the gimplifier, at which
point all templates have been instantiated. That attempt flopped,
however, because once we've gimplified a function, its body is discarded
and as a consequence, you can no longer evaluate a call to that function
which is required for escalating, which needs to decide if a call is
a constant expression or not.
Therefore, we have to perform the escalation before gimplifying, but
after instantiate_pending_templates. That's not easy because we have
no way to walk all the trees. In the v2 patch, I use two vectors: one
to store function decls that may become consteval, and another to
remember references to immediate-escalating functions. Unfortunately
the latter must also stash functions that call immediate-escalating
functions. Consider:
int g(int i)
{
f<int>(i); // f is immediate-escalating
}
where g itself is not immediate-escalating, but we have to make sure
that if f gets promoted to consteval, we give an error.
A new option, -fno-immediate-escalation, is provided to suppress
escalating functions.
v2 also adds a new flag, DECL_ESCALATED_P, so that we don't escalate
a function multiple times, and so that we can distinguish between
explicitly consteval functions and functions that have been promoted
to consteval.
In v3, I removed one of the new vectors and changed the other one
to a hash set. This version also contains numerous cleanups.
PR c++/107687
PR c++/110997
gcc/c-family/ChangeLog:
* c-cppbuiltin.cc (c_cpp_builtins): Update __cpp_consteval.
* c.opt (fimmediate-escalation): New option.
gcc/cp/ChangeLog:
* call.cc (in_immediate_context): No longer static.
* constexpr.cc (cxx_eval_call_expression): Adjust assert.
* cp-gimplify.cc (deferred_escalating_exprs): New vec.
(remember_escalating_expr): New.
(enum fold_flags): Remove ff_fold_immediate.
(immediate_escalating_function_p): New.
(promote_function_to_consteval): New.
(maybe_promote_function_to_consteval): New.
(maybe_store_cfun_for_late_checking): New.
(find_escalating_expr_r): New.
(maybe_explain_promoted_consteval): New.
(maybe_escalate_decl_and_cfun): New.
(cp_gimplify_expr) <case CALL_EXPR>: Assert we've handled all
immediate invocations.
(taking_address_of_imm_fn_error): New.
(cp_fold_immediate_r): Merge ADDR_EXPR and PTRMEM_CST cases. Implement
P2564 - promoting functions to consteval.
<case CALL_EXPR>: Implement P2564 - promoting functions to consteval.
(cp_fold_immediate): Return true if any errors were emitted.
(maybe_store_immediate_escalating_fn): New.
(process_pending_immediate_escalating_fns): New.
(check_immediate_escalating_refs): New.
* cp-tree.h (struct lang_decl_fn): Add escalated_p bit-field.
(DECL_ESCALATION_CHECKED_P): New.
(immediate_invocation_p): Declare.
(check_immediate_escalating_refs): Likewise.
(maybe_store_immediate_escalating_fn): Likewise.
(process_pending_immediate_escalating_fns): Likewise.
* decl.cc (finish_function): Call maybe_store_immediate_escalating_fn.
* decl2.cc (c_parse_final_cleanups): Set at_eof to 2 after all
templates have been instantiated; and to 3 at the end of the function.
Call process_pending_immediate_escalating_fns and
check_immediate_escalating_refs.
* error.cc (dump_template_bindings): Check at_eof against an updated
value.
* module.cc (trees_out::lang_decl_bools): Stream escalated_p.
(trees_in::lang_decl_bools): Likewise.
* pt.cc (push_tinst_level_loc): Set at_eof to 3, not 2.
* typeck.cc (cp_build_addr_expr_1): Don't check
DECL_IMMEDIATE_FUNCTION_P.
gcc/ChangeLog:
* doc/invoke.texi: Document -fno-immediate-escalation.
libstdc++-v3/ChangeLog:
* testsuite/18_support/comparisons/categories/zero_neg.cc: Add
dg-prune-output.
* testsuite/std/format/string_neg.cc: Add dg-error.
gcc/testsuite/ChangeLog:
* g++.dg/cpp23/consteval-if10.C: Remove dg-error.
* g++.dg/cpp23/consteval-if2.C: Likewise.
* g++.dg/cpp23/feat-cxx2b.C: Adjust expected value of __cpp_consteval.
* g++.dg/cpp26/feat-cxx26.C: Likewise.
* g++.dg/cpp2a/consteval-memfn1.C: Add dg-error.
* g++.dg/cpp2a/consteval11.C: Likewise.
* g++.dg/cpp2a/consteval3.C: Adjust dg-error.
* g++.dg/cpp2a/consteval34.C: Add dg-error.
* g++.dg/cpp2a/consteval9.C: Likewise.
* g++.dg/cpp2a/feat-cxx2a.C: Adjust expected value of __cpp_consteval.
* g++.dg/cpp2a/spaceship-synth9.C: Adjust dg-error.
* g++.dg/cpp2a/consteval-prop1.C: New test.
* g++.dg/cpp2a/consteval-prop10.C: New test.
* g++.dg/cpp2a/consteval-prop11.C: New test.
* g++.dg/cpp2a/consteval-prop12.C: New test.
* g++.dg/cpp2a/consteval-prop13.C: New test.
* g++.dg/cpp2a/consteval-prop14.C: New test.
* g++.dg/cpp2a/consteval-prop15.C: New test.
* g++.dg/cpp2a/consteval-prop16.C: New test.
* g++.dg/cpp2a/consteval-prop17.C: New test.
* g++.dg/cpp2a/consteval-prop18.C: New test.
* g++.dg/cpp2a/consteval-prop19.C: New test.
* g++.dg/cpp2a/consteval-prop2.C: New test.
* g++.dg/cpp2a/consteval-prop3.C: New test.
* g++.dg/cpp2a/consteval-prop4.C: New test.
* g++.dg/cpp2a/consteval-prop5.C: New test.
* g++.dg/cpp2a/consteval-prop6.C: New test.
* g++.dg/cpp2a/consteval-prop7.C: New test.
* g++.dg/cpp2a/consteval-prop8.C: New test.
* g++.dg/cpp2a/consteval-prop9.C: New test.
---
gcc/c-family/c-cppbuiltin.cc | 2 +-
gcc/c-family/c.opt | 4 +
gcc/cp/call.cc | 2 +-
gcc/cp/constexpr.cc | 4 +-
gcc/cp/cp-gimplify.cc | 438 ++++++++++++++++--
gcc/cp/cp-tree.h | 18 +-
gcc/cp/decl.cc | 5 +-
gcc/cp/decl2.cc | 20 +-
gcc/cp/error.cc | 2 +-
gcc/cp/module.cc | 4 +
gcc/cp/pt.cc | 2 +-
gcc/cp/typeck.cc | 6 +-
gcc/doc/invoke.texi | 34 ++
gcc/testsuite/g++.dg/cpp23/consteval-if10.C | 7 +-
gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 14 +-
gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +-
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 4 +-
gcc/testsuite/g++.dg/cpp2a/consteval-memfn1.C | 3 +
gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C | 169 +++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C | 41 ++
gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C | 49 ++
gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C | 30 ++
gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C | 23 +
gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C | 78 ++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C | 107 +++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C | 73 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C | 17 +
gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C | 20 +
gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C | 7 +
gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C | 90 ++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C | 27 ++
gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C | 30 ++
gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C | 27 ++
gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C | 59 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C | 76 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C | 82 ++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C | 67 +++
gcc/testsuite/g++.dg/cpp2a/consteval11.C | 18 +
gcc/testsuite/g++.dg/cpp2a/consteval3.C | 4 +-
gcc/testsuite/g++.dg/cpp2a/consteval34.C | 8 +
gcc/testsuite/g++.dg/cpp2a/consteval36.C | 26 +-
gcc/testsuite/g++.dg/cpp2a/consteval9.C | 2 +
gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C | 4 +-
gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C | 2 +-
.../comparisons/categories/zero_neg.cc | 1 +
.../testsuite/std/format/string_neg.cc | 2 +-
46 files changed, 1618 insertions(+), 94 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop18.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop19.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
@@ -1058,7 +1058,7 @@ c_cpp_builtins (cpp_reader *pfile)
cpp_define (pfile, "__cpp_constexpr=202002L");
cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L");
cpp_define (pfile, "__cpp_conditional_explicit=201806L");
- cpp_define (pfile, "__cpp_consteval=201811L");
+ cpp_define (pfile, "__cpp_consteval=202211L");
cpp_define (pfile, "__cpp_constinit=201907L");
cpp_define (pfile, "__cpp_deduction_guides=201907L");
cpp_define (pfile, "__cpp_nontype_template_args=201911L");
@@ -1878,6 +1878,10 @@ fhuge-objects
C++ ObjC++ WarnRemoved
No longer supported.
+fimmediate-escalation
+C++ ObjC++ Var(flag_immediate_escalation) Init(1)
+Implement P2564 for consteval propagation.
+
fimplement-inlines
C++ ObjC++ Var(flag_implement_inlines) Init(1)
Export functions even if they can be inlined.
@@ -9708,7 +9708,7 @@ in_immediate_context ()
/* Return true if a call to FN with number of arguments NARGS
is an immediate invocation. */
-static bool
+bool
immediate_invocation_p (tree fn)
{
return (TREE_CODE (fn) == FUNCTION_DECL
@@ -3128,11 +3128,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
/* OK */;
else if (!DECL_SAVED_TREE (fun))
{
- /* When at_eof >= 2, cgraph has started throwing away
+ /* When at_eof >= 3, cgraph has started throwing away
DECL_SAVED_TREE, so fail quietly. FIXME we get here because of
late code generation for VEC_INIT_EXPR, which needs to be
completely reconsidered. */
- gcc_assert (at_eof >= 2 && ctx->quiet);
+ gcc_assert (at_eof >= 3 && ctx->quiet);
*non_constant_p = true;
}
else if (tree copy = get_fundef_copy (new_call.fundef))
@@ -43,6 +43,21 @@ along with GCC; see the file COPYING3. If not see
#include "omp-general.h"
#include "opts.h"
+/* Keep track of forward references to immediate-escalating functions in
+ case they become consteval. This vector contains ADDR_EXPRs and
+ PTRMEM_CSTs; it also stores FUNCTION_DECLs that had an escalating
+ function call in them, to check that they can be evaluated to a constant,
+ and immediate-escalating functions that may become consteval. */
+static GTY(()) hash_set<tree> *deferred_escalating_exprs;
+
+static void
+remember_escalating_expr (tree t)
+{
+ if (!deferred_escalating_exprs)
+ deferred_escalating_exprs = hash_set<tree>::create_ggc (37);
+ deferred_escalating_exprs->add (t);
+}
+
/* Flags for cp_fold and cp_fold_r. */
enum fold_flags {
@@ -53,8 +68,6 @@ enum fold_flags {
definitely not in a manifestly constant-evaluated
context. */
ff_mce_false = 1 << 1,
- /* Whether we're being called from cp_fold_immediate. */
- ff_fold_immediate = 1 << 2,
};
using fold_flags_t = int;
@@ -428,6 +441,173 @@ lvalue_has_side_effects (tree e)
return TREE_SIDE_EFFECTS (e);
}
+/* Return true if FN is an immediate-escalating function. */
+
+static bool
+immediate_escalating_function_p (tree fn)
+{
+ if (!fn || !flag_immediate_escalation)
+ return false;
+
+ gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+ if (DECL_IMMEDIATE_FUNCTION_P (fn) || DECL_ESCALATION_CHECKED_P (fn))
+ return false;
+
+ /* An immediate-escalating function is
+ -- the call operator of a lambda that is not declared with the consteval
+ specifier */
+ if (LAMBDA_FUNCTION_P (fn))
+ return true;
+ /* -- a defaulted special member function that is not declared with the
+ consteval specifier */
+ special_function_kind sfk = special_memfn_p (fn);
+ if (sfk != sfk_none && DECL_DEFAULTED_FN (fn))
+ return true;
+ /* -- a function that results from the instantiation of a templated entity
+ defined with the constexpr specifier. */
+ return is_instantiation_of_constexpr (fn);
+}
+
+/* Promote FN to an immediate function, including its clones. */
+
+static void
+promote_function_to_consteval (tree fn)
+{
+ SET_DECL_IMMEDIATE_FUNCTION_P (fn);
+ DECL_ESCALATION_CHECKED_P (fn) = true;
+ tree clone;
+ FOR_EACH_CLONE (clone, fn)
+ {
+ SET_DECL_IMMEDIATE_FUNCTION_P (clone);
+ DECL_ESCALATION_CHECKED_P (clone) = true;
+ }
+}
+
+/* Promote FN to an immediate function, including its clones, if it is
+ an immediate-escalating function. Return true if we did promote;
+ false otherwise. */
+
+static bool
+maybe_promote_function_to_consteval (tree fn)
+{
+ if (immediate_escalating_function_p (fn))
+ {
+ promote_function_to_consteval (fn);
+ return true;
+ }
+
+ return false;
+}
+
+/* Remember that the current function declaration contains a call to
+ a function that might be promoted to consteval later. */
+
+static void
+maybe_store_cfun_for_late_checking ()
+{
+ if (flag_immediate_escalation
+ && current_function_decl
+ && !immediate_escalating_function_p (current_function_decl))
+ remember_escalating_expr (current_function_decl);
+}
+
+/* Find an immediate-escalating expression or conversion in *TP.
+ If DATA_ is non-null, this function will promote function to
+ consteval as it goes; otherwise, we're just looking for what
+ made a function consteval, for diagnostic purposes. This
+ function assumes that *TP was instantiated. */
+
+static tree
+find_escalating_expr_r (tree *tp, int *walk_subtrees, void *data)
+{
+ /* The function whose body we're traversing. Used to promote the current
+ function to consteval. */
+ tree caller = data ? *static_cast<tree *>(data) : NULL_TREE;
+ tree t = *tp;
+ const tree_code code = TREE_CODE (t);
+
+ if (TYPE_P (t) || unevaluated_p (code))
+ {
+bail:
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
+
+ tree decl;
+ tree call;
+
+ switch (code)
+ {
+ case CALL_EXPR:
+ decl = cp_get_callee_fndecl_nofold (t);
+ call = t;
+ break;
+ case ADDR_EXPR:
+ if (TREE_CODE (TREE_OPERAND (t, 0)) != FUNCTION_DECL
+ || ADDR_EXPR_DENOTES_CALL_P (t))
+ goto bail;
+ decl = TREE_OPERAND (t, 0);
+ call = NULL_TREE;
+ break;
+ case PTRMEM_CST:
+ if (TREE_CODE (PTRMEM_CST_MEMBER (t)) != FUNCTION_DECL)
+ goto bail;
+ decl = PTRMEM_CST_MEMBER (t);
+ call = NULL_TREE;
+ break;
+ default:
+ return NULL_TREE;
+ }
+
+ /* Now, voyager, sail thou forth, to seek and find. */
+ if (!decl)
+ goto bail;
+
+ /* Not consteval yet, but could be. Have to look deeper. */
+ if (immediate_escalating_function_p (decl))
+ {
+ /* Set before the actual walk to avoid endless recursion. */
+ DECL_ESCALATION_CHECKED_P (decl) = true;
+ cp_walk_tree (&DECL_SAVED_TREE (decl), find_escalating_expr_r,
+ caller ? &decl : nullptr, nullptr);
+ }
+
+ /* If it turned out to be consteval, maybe promote the caller. */
+ if (DECL_IMMEDIATE_FUNCTION_P (decl)
+ && (!call || cxx_constant_value (call, tf_none) == error_mark_node))
+ {
+ /* We found the escalating expression. */
+ if (caller)
+ promote_function_to_consteval (caller);
+ *walk_subtrees = 0;
+ return t;
+ }
+
+ return NULL_TREE;
+}
+
+/* Maybe say that FN (a function decl with DECL_IMMEDIATE_FUNCTION_P set)
+ was initially not an immediate function, but was promoted to one because
+ its body contained an immediate-escalating expression or conversion. */
+
+static void
+maybe_explain_promoted_consteval (location_t loc, tree fn)
+{
+ if (DECL_ESCALATION_CHECKED_P (fn))
+ {
+ /* See if we can figure out what made the function consteval. */
+ tree x = cp_walk_tree (&DECL_SAVED_TREE (fn), find_escalating_expr_r,
+ NULL_TREE, nullptr);
+ if (x)
+ inform (cp_expr_loc_or_loc (x, loc),
+ "%qD was promoted to an immediate function because its "
+ "body contains an immediate-escalating expression %qE", fn, x);
+ else
+ inform (loc, "%qD was promoted to an immediate function", fn);
+ }
+}
+
/* Gimplify *EXPR_P as rvalue into an expression that can't be modified
by expressions with side-effects in other operands. */
@@ -485,6 +665,40 @@ cp_gimplify_arg (tree *arg_p, gimple_seq *pre_p, location_t call_location,
}
+/* Figure out if DECL should be promoted to consteval and if so, maybe also
+ promote the function we are in currently. CALL is the CALL_EXPR of DECL.
+ EVALP is where we may store the result of cxx_constant_value so that we
+ don't have to evaluate the same tree again in cp_fold_immediate_r. */
+
+static void
+maybe_escalate_decl_and_cfun (tree decl, tree call, tree *evalp)
+{
+ if (cp_unevaluated_operand)
+ return;
+
+ /* What we're calling is not a consteval function but it may become
+ one. This requires recursing; DECL may be promoted to consteval
+ because it contains an escalating expression E, but E itself may
+ have to be promoted first, etc. */
+ if (immediate_escalating_function_p (decl))
+ {
+ cp_walk_tree (&DECL_SAVED_TREE (decl), find_escalating_expr_r,
+ &decl, nullptr);
+ DECL_ESCALATION_CHECKED_P (decl) = true;
+ }
+
+ /* In turn, maybe promote the function we find ourselves in... */
+ if (DECL_IMMEDIATE_FUNCTION_P (decl)
+ /* ...but not if the call to DECL was constant; that is the
+ "an immediate invocation that is not a constant expression"
+ case. We do this here and not in find_escalating_expr_r,
+ because DECL could have already been consteval and we'd
+ never call f_e_e_r. */
+ && (*evalp = cxx_constant_value (call, tf_none),
+ *evalp == error_mark_node))
+ maybe_promote_function_to_consteval (current_function_decl);
+}
+
/* Do C++-specific gimplification. Args are as for gimplify_expr. */
int
@@ -746,7 +960,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
if (ret != GS_ERROR)
{
tree decl = cp_get_callee_fndecl_nofold (*expr_p);
- if (decl && fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
+ if (!decl)
+ break;
+ if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
switch (DECL_FE_FUNCTION_CODE (decl))
{
case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
@@ -771,6 +987,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
default:
break;
}
+ else
+ /* All consteval functions should have been processed by now. */
+ gcc_checking_assert (!immediate_invocation_p (decl));
}
break;
@@ -1031,6 +1250,20 @@ struct cp_genericize_data
bool handle_invisiref_parm_p;
};
+/* Emit an error about taking the address of an immediate function.
+ EXPR is the whole expression; DECL is the immediate function. */
+
+static void
+taking_address_of_imm_fn_error (tree expr, tree decl)
+{
+ auto_diagnostic_group d;
+ const location_t loc = (TREE_CODE (expr) == PTRMEM_CST
+ ? PTRMEM_CST_LOCATION (expr)
+ : EXPR_LOCATION (expr));
+ error_at (loc, "taking address of an immediate function %qD", decl);
+ maybe_explain_promoted_consteval (loc, decl);
+}
+
/* A subroutine of cp_fold_r to handle immediate functions. */
static tree
@@ -1041,31 +1274,51 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
/* The purpose of this is not to emit errors for mce_unknown. */
const tsubst_flags_t complain = (data->flags & ff_mce_false
? tf_error : tf_none);
+ const tree_code code = TREE_CODE (stmt);
/* No need to look into types or unevaluated operands.
NB: This affects cp_fold_r as well. */
- if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt)))
+ if (TYPE_P (stmt) || unevaluated_p (code))
{
*walk_subtrees = 0;
return NULL_TREE;
}
- switch (TREE_CODE (stmt))
+ switch (code)
{
case PTRMEM_CST:
- if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
- && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
- {
- if (!data->pset.add (stmt) && (complain & tf_error))
- {
- error_at (PTRMEM_CST_LOCATION (stmt),
- "taking address of an immediate function %qD",
- PTRMEM_CST_MEMBER (stmt));
- *stmt_p = build_zero_cst (TREE_TYPE (stmt));
- }
- return error_mark_node;
- }
- break;
+ case ADDR_EXPR:
+ {
+ tree decl = (code == PTRMEM_CST
+ ? PTRMEM_CST_MEMBER (stmt)
+ : TREE_OPERAND (stmt, 0));
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ break;
+ if (code == ADDR_EXPR && ADDR_EXPR_DENOTES_CALL_P (stmt))
+ break;
+ if (immediate_invocation_p (decl))
+ {
+ if (maybe_promote_function_to_consteval (current_function_decl))
+ break;
+ if (complain & tf_error)
+ {
+ if (!data->pset.add (stmt))
+ {
+ taking_address_of_imm_fn_error (stmt, decl);
+ *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+ }
+ /* If we're giving hard errors, continue the walk rather than
+ bailing out after the first error. */
+ break;
+ }
+ return error_mark_node;
+ }
+ /* Not consteval yet, but could become one, in which case it's invalid
+ to take its address. */
+ else if (immediate_escalating_function_p (decl))
+ remember_escalating_expr (stmt);
+ break;
+ }
/* Expand immediate invocations. */
case CALL_EXPR:
@@ -1073,33 +1326,54 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
if (tree fn = cp_get_callee (stmt))
if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn))
if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false))
- if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
- {
- stmt = cxx_constant_value (stmt, complain);
- if (stmt == error_mark_node)
- {
- if (complain & tf_error)
- *stmt_p = error_mark_node;
- return error_mark_node;
- }
- *stmt_p = stmt;
- }
- break;
-
- case ADDR_EXPR:
- if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
- && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))
- && !ADDR_EXPR_DENOTES_CALL_P (stmt))
- {
- if (complain & tf_error)
{
- error_at (EXPR_LOCATION (stmt),
- "taking address of an immediate function %qD",
- TREE_OPERAND (stmt, 0));
- *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+ tree eval = NULL_TREE;
+ /* Escalate once all templates have been instantiated. */
+ if (at_eof > 1)
+ maybe_escalate_decl_and_cfun (fndecl, stmt, &eval);
+
+ /* [expr.const]p16 "An expression or conversion is
+ immediate-escalating if it is not initially in an immediate
+ function context and it is either
+ -- an immediate invocation that is not a constant expression
+ and is not a subexpression of an immediate invocation."
+
+ If we are in an immediate-escalating function, the
+ immediate-escalating expression or conversion makes it an
+ immediate function. So STMT does not need to produce
+ a constant expression. */
+ if (immediate_invocation_p (fndecl))
+ {
+ tree e = eval ? eval : cxx_constant_value (stmt, tf_none);
+ if (e == error_mark_node)
+ {
+ if (maybe_promote_function_to_consteval
+ (current_function_decl))
+ break;
+ if (complain & tf_error)
+ {
+ auto_diagnostic_group d;
+ location_t loc = cp_expr_loc_or_input_loc (stmt);
+ error_at (loc, "call to consteval function %qE is "
+ "not a constant expression", stmt);
+ /* Explain why it's not a constant expression. */
+ *stmt_p = cxx_constant_value (stmt, complain);
+ maybe_explain_promoted_consteval (loc, fndecl);
+ /* Don't return error_mark_node, it would stop our
+ tree walk. */
+ break;
+ }
+ return error_mark_node;
+ }
+ /* We've evaluated the consteval function call. */
+ *stmt_p = e;
+ }
+ /* We've encountered a function call that may turn out to be
+ consteval later. Store its caller so that we can ensure
+ that the call is a constant expression. */
+ else if (immediate_escalating_function_p (fndecl))
+ maybe_store_cfun_for_late_checking ();
}
- return error_mark_node;
- }
break;
default:
@@ -1111,7 +1385,8 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
/* A wrapper around cp_fold_immediate_r. Return true if we found
a non-constant immediate function, or taking the address of an
- immediate function. */
+ immediate function. If ESCALATE_P, tell cp_fold_immediate_r to
+ escalate immediate-escalating functions. */
bool
cp_fold_immediate (tree *tp, mce_value manifestly_const_eval)
@@ -1119,12 +1394,14 @@ cp_fold_immediate (tree *tp, mce_value manifestly_const_eval)
if (cxx_dialect <= cxx17)
return false;
- fold_flags_t flags = ff_fold_immediate;
+ fold_flags_t flags = ff_none;
if (manifestly_const_eval == mce_false)
flags |= ff_mce_false;
cp_fold_data data (flags);
- return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
+ int save_errorcount = errorcount;
+ tree r = cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
+ return r != NULL_TREE || errorcount > save_errorcount;
}
/* Perform any pre-gimplification folding of C++ front end trees to
@@ -1302,6 +1579,75 @@ cp_fold_function (tree fndecl)
cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
}
+/* FN is not a consteval function, but may become one. Remember to
+ escalate it after all pending templates have been instantiated. */
+
+void
+maybe_store_immediate_escalating_fn (tree fn)
+{
+ if (cxx_dialect >= cxx20
+ && flag_immediate_escalation
+ && immediate_escalating_function_p (fn))
+ remember_escalating_expr (fn);
+}
+
+/* We've stashed immediate-escalating functions. Now see if they indeed
+ ought to be promoted to consteval. */
+
+void
+process_pending_immediate_escalating_fns ()
+{
+ /* This will be null for -fno-immediate-escalation. */
+ if (!deferred_escalating_exprs)
+ return;
+
+ for (auto e : *deferred_escalating_exprs)
+ if (TREE_CODE (e) == FUNCTION_DECL)
+ {
+ if (!DECL_ESCALATION_CHECKED_P (e))
+ {
+ temp_override<tree> cfd (current_function_decl, e);
+ cp_fold_immediate (&DECL_SAVED_TREE (e), mce_false);
+ }
+ if (DECL_IMMEDIATE_FUNCTION_P (e))
+ deferred_escalating_exprs->remove (e);
+ }
+}
+
+/* We've escalated every function that could have been promoted to
+ consteval. Check that we are not taking the address of a consteval
+ function. */
+
+void
+check_immediate_escalating_refs ()
+{
+ /* This will be null for -fno-immediate-escalation. */
+ if (!deferred_escalating_exprs)
+ return;
+
+ for (auto ref : *deferred_escalating_exprs)
+ {
+ if (TREE_CODE (ref) == FUNCTION_DECL)
+ /* We saw a function call to an immediate-escalating function in
+ the body of REF. Check that it's a constant if it was promoted
+ to consteval. */
+ {
+ temp_override<tree> cfd (current_function_decl, ref);
+ cp_fold_immediate (&DECL_SAVED_TREE (ref), mce_false);
+ }
+ else
+ {
+ tree decl = (TREE_CODE (ref) == PTRMEM_CST
+ ? PTRMEM_CST_MEMBER (ref)
+ : TREE_OPERAND (ref, 0));
+ if (DECL_IMMEDIATE_FUNCTION_P (decl))
+ taking_address_of_imm_fn_error (ref, decl);
+ }
+ }
+
+ deferred_escalating_exprs = nullptr;
+}
+
/* Turn SPACESHIP_EXPR EXPR into GENERIC. */
static tree genericize_spaceship (tree expr)
@@ -2938,8 +2938,9 @@ struct GTY(()) lang_decl_fn {
unsigned maybe_deleted : 1;
unsigned coroutine_p : 1;
unsigned implicit_constexpr : 1;
+ unsigned escalated_p : 1;
- unsigned spare : 9;
+ unsigned spare : 8;
/* 32-bits padding on 64-bit host. */
@@ -3391,6 +3392,14 @@ struct GTY(()) lang_decl {
#define DECL_MAYBE_DELETED(NODE) \
(LANG_DECL_FN_CHECK (NODE)->maybe_deleted)
+/* Nonzero for FUNCTION_DECL means that this function's body has been
+ checked for immediate-escalating expressions and maybe promoted. It
+ does *not* mean the function is consteval. It must not be set in
+ a function that was marked consteval by the user, so that we can
+ distinguish between explicitly consteval functions and promoted consteval
+ functions. */
+#define DECL_ESCALATION_CHECKED_P(NODE) (LANG_DECL_FN_CHECK (NODE)->escalated_p)
+
/* True (in a FUNCTION_DECL) if NODE is a virtual function that is an
invalid overrider for a function from a base class. Once we have
complained about an invalid overrider we avoid complaining about it
@@ -5849,7 +5858,8 @@ extern GTY(()) vec<tree, va_gc> *keyed_classes;
/* Nonzero if we're done parsing and into end-of-file activities.
- Two if we're done with front-end processing. */
+ 2 if all templates have been instantiated.
+ 3 if we're done with front-end processing. */
extern int at_eof;
@@ -6741,6 +6751,7 @@ extern tree perform_direct_initialization_if_possible (tree, tree, bool,
extern vec<tree,va_gc> *resolve_args (vec<tree,va_gc>*, tsubst_flags_t);
extern tree in_charge_arg_for_name (tree);
extern bool in_immediate_context ();
+extern bool immediate_invocation_p (tree);
extern tree build_cxx_call (tree, int, tree *,
tsubst_flags_t,
tree = NULL_TREE);
@@ -8384,6 +8395,9 @@ extern bool simple_empty_class_p (tree, tree, tree_code);
extern tree fold_builtin_source_location (const_tree);
extern tree get_source_location_impl_type ();
extern bool cp_fold_immediate (tree *, mce_value);
+extern void check_immediate_escalating_refs ();
+extern void maybe_store_immediate_escalating_fn (tree);
+extern void process_pending_immediate_escalating_fns ();
/* in name-lookup.cc */
extern tree strip_using_decl (tree);
@@ -18343,7 +18343,10 @@ finish_function (bool inline_p)
if (!processing_template_decl
&& !DECL_IMMEDIATE_FUNCTION_P (fndecl)
&& !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
- cp_fold_function (fndecl);
+ {
+ cp_fold_function (fndecl);
+ maybe_store_immediate_escalating_fn (fndecl);
+ }
/* Set up the named return value optimization, if we can. Candidate
variables are selected in check_return_expr. */
@@ -169,7 +169,9 @@ typedef hash_map<unsigned/*Priority*/, tree/*List*/,
one for init. The fini table is only ever used when !cxa_atexit. */
static GTY(()) priority_map_t *static_init_fini_fns[2];
-/* Nonzero if we're done parsing and into end-of-file activities. */
+/* Nonzero if we're done parsing and into end-of-file activities.
+ 2 if all templates have been instantiated.
+ 3 if we're done with front-end processing. */
int at_eof;
@@ -4981,6 +4983,7 @@ c_parse_final_cleanups (void)
tree decl;
locus_at_end_of_parsing = input_location;
+ /* We're done parsing. */
at_eof = 1;
/* Bad parse errors. Just forget about it. */
@@ -5246,6 +5249,9 @@ c_parse_final_cleanups (void)
reconsider = true;
}
+ /* All templates have been instantiated. */
+ at_eof = 2;
+
void *module_cookie = finish_module_processing (parse_in);
lower_var_init ();
@@ -5288,7 +5294,15 @@ c_parse_final_cleanups (void)
if (static_init_fini_fns[true])
for (auto iter : *static_init_fini_fns[true])
iter.second = nreverse (iter.second);
-
+
+ /* Now we've instantiated all templates. Now we can escalate the functions
+ we squirreled away earlier. */
+ if (cxx_dialect >= cxx20)
+ {
+ process_pending_immediate_escalating_fns ();
+ check_immediate_escalating_refs ();
+ }
+
/* Then, do the Objective-C stuff. This is where all the
Objective-C module stuff gets generated (symtab,
class/protocol/selector lists etc). This must be done after C++
@@ -5370,7 +5384,7 @@ c_parse_final_cleanups (void)
timevar_start (TV_PHASE_PARSING);
/* Indicate that we're done with front end processing. */
- at_eof = 2;
+ at_eof = 3;
}
/* Perform any post compilation-proper cleanups for the C++ front-end.
@@ -478,7 +478,7 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
/* Don't try to do this once cgraph starts throwing away front-end
information. */
- if (at_eof >= 2)
+ if (at_eof >= 3)
return;
FOR_EACH_VEC_SAFE_ELT (typenames, i, t)
@@ -5683,6 +5683,8 @@ trees_out::lang_decl_bools (tree t)
WB (lang->u.fn.has_dependent_explicit_spec_p);
WB (lang->u.fn.immediate_fn_p);
WB (lang->u.fn.maybe_deleted);
+ WB (lang->u.fn.escalated_p);
+ /* We do not stream lang->u.fn.implicit_constexpr. */
goto lds_min;
case lds_decomp: /* lang_decl_decomp. */
@@ -5751,6 +5753,8 @@ trees_in::lang_decl_bools (tree t)
RB (lang->u.fn.has_dependent_explicit_spec_p);
RB (lang->u.fn.immediate_fn_p);
RB (lang->u.fn.maybe_deleted);
+ RB (lang->u.fn.escalated_p);
+ /* We do not stream lang->u.fn.implicit_constexpr. */
goto lds_min;
case lds_decomp: /* lang_decl_decomp. */
@@ -11130,7 +11130,7 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
if (tinst_depth >= max_tinst_depth)
{
/* Tell error.cc not to try to instantiate any templates. */
- at_eof = 2;
+ at_eof = 3;
fatal_error (input_location,
"template instantiation depth exceeds maximum of %d"
" (use %<-ftemplate-depth=%> to increase the maximum)",
@@ -7228,11 +7228,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
complain);
}
- /* For addresses of immediate functions ensure we have EXPR_LOCATION
- set for possible later diagnostics. */
+ /* Ensure we have EXPR_LOCATION set for possible later diagnostics. */
if (TREE_CODE (val) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL
- && DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0)))
+ && TREE_CODE (TREE_OPERAND (val, 0)) == FUNCTION_DECL)
SET_EXPR_LOCATION (val, input_location);
return val;
@@ -219,6 +219,7 @@ in the following sections.
-fno-elide-constructors
-fno-enforce-eh-specs
-fno-gnu-keywords
+-fno-immediate-escalation
-fno-implicit-templates
-fno-implicit-inline-templates
-fno-implement-inlines
@@ -3375,6 +3376,39 @@ word as an identifier. You can use the keyword @code{__typeof__} instead.
This option is implied by the strict ISO C++ dialects: @option{-ansi},
@option{-std=c++98}, @option{-std=c++11}, etc.
+@opindex fno-immediate-escalation
+@opindex fimmediate-escalation
+@item -fno-immediate-escalation
+Do not enable immediate function escalation whereby certain functions
+can be promoted to consteval, as specified in P2564R3. For example:
+
+@example
+consteval int id(int i) @{ return i; @}
+
+constexpr int f(auto t)
+@{
+ return t + id(t); // id causes f<int> to be promoted to consteval
+@}
+
+void g(int i)
+@{
+ f (3);
+@}
+@end example
+
+compiles in C++20: @code{f} is an immediate-escalating function (due to
+the @code{auto} it is a function template and is declared @code{constexpr})
+and @code{id(t)} is an immediate-escalating expression, so @code{f} is
+promoted to @code{consteval}. Consequently, the call to @code{id(t)}
+is in an immediate context, so doesn't have to produce a constant (that
+is the mechanism allowing consteval function composition). However,
+with @option{-fno-immediate-escalation}, @code{f} is not promoted to
+@code{consteval}, and since the call to consteval function @code{id(t)}
+is not a constant expression, the compiler rejects the code.
+
+This option is turned on by default; it is only effective in C++20 mode
+or later.
+
@opindex fimplicit-constexpr
@item -fimplicit-constexpr
Make inline functions implicitly constexpr, if they satisfy the
@@ -2,6 +2,9 @@
// { dg-do compile { target c++20 } }
// { dg-options "" }
+// We used to give errors but the lambdas are now promoted to consteval
+// and are in a immediate function context, so no errors.
+
consteval int foo (int x) { return x; }
constexpr int
@@ -10,7 +13,7 @@ bar (int x)
int r = 0;
if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
- auto y = [=] { foo (x); }; // { dg-error "'x' is not a constant expression" }
+ auto y = [=] { foo (x); };
y ();
}
return r;
@@ -23,7 +26,7 @@ baz (T x)
T r = 0;
if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
- auto y = [=] { foo (x); }; // { dg-error "'x' is not a constant expression" }
+ auto y = [=] { foo (x); };
y ();
}
return r;
@@ -33,7 +33,7 @@ baz (int x)
int r = 0;
if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
- r += foo (x); // { dg-error "'x' is not a constant expression" }
+ r += foo (x); // { dg-error "not a constant expression" }
}
else
{
@@ -45,11 +45,11 @@ baz (int x)
}
else
{
- r += foo (8 * x); // { dg-error "'x' is not a constant expression" }
+ r += foo (8 * x); // { dg-error "is not a constant expression" }
}
if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
- r += foo (32 * x);// { dg-error "'x' is not a constant expression" }
+ r += foo (32 * x);// { dg-error "not a constant expression" }
}
if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
@@ -98,7 +98,7 @@ corge (T x)
T r = 0;
if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
- r += foo (x); // { dg-error "'x' is not a constant expression" }
+ r += foo (x);
}
else
{
@@ -110,11 +110,11 @@ corge (T x)
}
else
{
- r += foo (8 * x); // { dg-error "is not a constant expression" }
+ r += foo (8 * x);
}
if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
- r += foo (32 * x);// { dg-error "is not a constant expression" }
+ r += foo (32 * x);
}
if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
@@ -126,5 +126,5 @@ corge (T x)
int
garply (int x)
{
- return corge (x);
+ return corge (x); // { dg-error "is not a constant expression" }
}
@@ -480,8 +480,8 @@
#ifndef __cpp_consteval
# error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-# error "__cpp_consteval != 201811"
+#elif __cpp_consteval != 202211L
+# error "__cpp_consteval != 202211L"
#endif
#ifndef __cpp_concepts
@@ -480,8 +480,8 @@
#ifndef __cpp_consteval
# error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-# error "__cpp_consteval != 201811"
+#elif __cpp_consteval != 202211L
+# error "__cpp_consteval != 202211L"
#endif
#ifndef __cpp_concepts
@@ -19,10 +19,13 @@ template<class>
void VerifyHash(fixed_string s) {
s.size(0); // { dg-bogus "" }
s.size(-1); // { dg-message "expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
s.size_static(0); // { dg-bogus "" }
s.size_static(-1); // { dg-message "expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
fixed_string::size_static(0); // { dg-bogus "" }
fixed_string::size_static(-1); // { dg-message "expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
s(); // { dg-bogus "" }
}
new file mode 100644
@@ -0,0 +1,169 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Some of these were cribbed from clang's cxx2b-consteval-propagate.cpp.
+
+consteval int id(int i) { return i; }
+
+template <typename T>
+constexpr int
+f0 (T t)
+{
+ // OK, f0<int> promoted to consteval.
+ return id (t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
+}
+
+constexpr auto a0 = f0 (3);
+
+// As a consequence of f0<int> being promoted to an immediate function, we
+// can't take its address.
+auto p0 = &f0<int>; // { dg-error "taking address of an immediate function" }
+
+template <typename T>
+constexpr int
+f1 (T t)
+{
+ // OK, f1<int> promoted to consteval.
+ return t + id (t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
+}
+
+constexpr auto a1 = f1 (3);
+
+// As a consequence of f1<int> being promoted to an immediate function, we
+// can't take its address.
+auto p1 = &f1<int>; // { dg-error "taking address of an immediate function" }
+
+template <typename T>
+constexpr int
+f2 (T)
+{
+ // This produces a constant; f2 *not* promoted to consteval.
+ return id (42);
+}
+
+// ... so we can take its address.
+auto p2 = &f2<int>;
+
+constexpr int
+f3 (int i)
+{
+ // f3 isn't a function template and those don't get upgraded to consteval.
+ return id (i); // { dg-error "not a constant expression" }
+}
+
+auto p3 = &f3;
+
+template<typename T>
+constexpr int
+f4 (T t)
+{
+ auto p = id; // { dg-message "immediate-escalating expression .id." }
+ (void) p;
+ return t;
+}
+
+auto p6 = &f4<int>; // { dg-error "taking address of an immediate function" }
+
+static_assert (f4 (42) == 42);
+
+// Constructors.
+consteval int zero (int)
+{
+ return 0;
+}
+
+struct A {
+ // A::A(auto) promoted to consteval.
+ constexpr A(auto i) { zero (i); }
+};
+
+constexpr void
+f5 (auto i)
+{
+ A a{i};
+}
+
+constexpr void
+f5_nt (int i)
+{
+ A a{i}; // { dg-error "call to consteval function|not a constant" }
+}
+
+void
+f6 ()
+{
+ f5 (0);
+}
+
+struct B {
+ constexpr B(int) { }
+};
+
+B b1(f0<int>((f1<int>(7))));
+
+template<typename T>
+constexpr int cid(T t) { return t; }
+
+auto p4 = &cid<int>;
+auto p5 = &cid<char>;
+
+int g = 7; // { dg-message ".int g. is not const" }
+
+B b2(f0<int>(cid<int>(g))); // { dg-error "call to consteval function|not usable" }
+
+struct C {
+ consteval C (int) {};
+};
+
+constexpr int
+f7 (auto t)
+{
+ C c(t); // { dg-message "immediate-escalating expression .c.C::C\\(t\\)." }
+ return 0;
+}
+
+int i1 = f7 (g); // { dg-error "call to consteval function|not usable" }
+
+struct Y {
+ int y;
+ int x = id (y);
+ consteval Y (int i) : y (id (i)) {}
+};
+
+Y y1(1);
+Y y2(g); // { dg-error "call to consteval function|not usable" }
+
+struct Y2 {
+ int y;
+ int x = id (y);
+ constexpr Y2 (auto i) : y (id (i)) {}
+};
+
+Y2 y3(1);
+Y2 y4(g); // { dg-error "call to consteval function|not usable" }
+
+auto l1 = [](int i) constexpr {
+ int t = id (i);
+ return id (0);
+};
+
+int (*pl1)(int) = l1; // { dg-error "call to consteval function|returns address of immediate function" }
+
+auto l2 = [](int i) {
+ int t = id (i);
+ return id (0);
+};
+
+int (*pl2)(int) = l2; // { dg-error "call to consteval function|returns address of immediate function" }
+
+// Not defined = won't produce a constant expression.
+consteval int undef (); // { dg-warning "used but never defined" }
+
+struct S {
+ int a = [] { return undef (); }();
+};
+
+struct S2 { // { dg-error "used before its definition" }
+ int a = [] (int u = undef ()) {
+ return u;
+ }();
+} s2; // { dg-error "call to consteval function" }
new file mode 100644
@@ -0,0 +1,41 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Test default arguments.
+
+consteval int id (int i) { return i; }
+
+template<typename>
+constexpr int
+f1 (int i = id (42))
+{
+ return i;
+}
+
+int non_const; // { dg-message ".int non_const. is not const" }
+
+template<typename>
+constexpr int
+f2 (int i = id (non_const))
+{
+ return i;
+}
+
+constexpr int
+f3 (auto)
+{
+ return f2<int>(); // { dg-message "contains an immediate-escalating expression .id\\(non_const\\)." }
+}
+
+auto a = &f3<int>; // { dg-error "taking address of an immediate function" }
+
+void
+g (int i)
+{
+ f1<int> (42);
+ f1<int> (i);
+ f1<int> ();
+ f2<int> (42);
+ f2<int> (i);
+ f2<int> (); // { dg-error "call to consteval function .id\\(non_const\\). is not a constant expression" }
+// { dg-error ".non_const. is not usable in a constant expression" "" { target *-*-* } .-1 }
+}
new file mode 100644
@@ -0,0 +1,49 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-fdiagnostics-show-caret" }
+// Test diagnostic.
+
+consteval int id (int i) { return i; }
+constexpr int foo (int i ) { return i; }
+
+constexpr int
+foobar (auto i)
+{
+ return i + id (i);
+ /* { dg-begin-multiline-output "" }
+ return i + id (i);
+ ~~~^~~
+ { dg-end-multiline-output "" } */
+}
+
+void
+g (int x)
+{
+ foobar (x); // { dg-error "10:call to consteval function .foobar<int>\\(x\\). is not a constant expression" }
+// { dg-error ".x. is not a constant expression" "" { target *-*-* } .-1 }
+ /* { dg-begin-multiline-output "" }
+foobar (x);
+ ~~~~~~~^~~
+ { dg-end-multiline-output "" } */
+}
+
+constexpr int
+f2 (auto i)
+{
+ auto p = &id;
+ /* { dg-begin-multiline-output "" }
+ auto p = &id;
+ ^~~
+ { dg-end-multiline-output "" } */
+ return p (i);
+}
+
+void
+g2 (int x)
+{
+ f2 (x); // { dg-error "6:call to consteval function .f2<int>\\(x\\). is not a constant expression|not a constant expression" }
+ /* { dg-begin-multiline-output "" }
+f2 (x);
+ ~~~^~~
+ { dg-end-multiline-output "" } */
+}
new file mode 100644
@@ -0,0 +1,30 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int
+zero (int)
+{
+ return 0;
+}
+
+constexpr int
+f (auto i)
+{
+ return zero (i);
+}
+
+constexpr int
+g (auto)
+{
+ // This call is a constant expression, so don't promote g.
+ return f (42);
+}
+
+void
+do_test ()
+{
+ g (2);
+}
+
+// Must work.
+auto q = &g<int>;
new file mode 100644
@@ -0,0 +1,23 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Verify we don't recurse endlessly while determining whether a function
+// should be propagated to consteval.
+
+consteval int id (int i) { return i; }
+
+constexpr int f2 (auto);
+
+constexpr int
+f1 (auto i)
+{
+ return f2 (i);
+}
+
+constexpr int
+f2 (auto i)
+{
+ return f1 (i);
+}
+
+auto p = &f1<int>;
+auto q = &f2<int>;
new file mode 100644
@@ -0,0 +1,78 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Test more CALL_EXPRs in a function, some of which are escalating.
+
+consteval int id (int i) { return i; }
+constexpr int neg (int i) { return -i; }
+constexpr int foo (auto i) { return id (i); }
+
+constexpr int
+f1 (auto i)
+{
+ auto x = id (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
+ auto y = neg (i);
+ return x + y;
+}
+
+constexpr int
+f2 (auto i)
+{
+ return neg (id (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
+}
+
+constexpr int
+f3 (auto i)
+{
+ auto x = i + neg (neg (neg (id (neg (neg (i)))))); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(neg\\(neg\\(i\\)\\)\\)." }
+ return x;
+}
+
+constexpr int
+f4 (auto i)
+{
+ return i + neg ((id (2 * i) + neg (i)) / 2); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(\\(i \\* 2\\)\\)." }
+}
+
+constexpr int
+f5 (auto i)
+{
+ (void) neg (i);
+ (void) neg (i);
+ (void) neg (i);
+ (void) neg (i);
+ (void) neg (i);
+ (void) neg (i);
+ (void) +id (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id\\(i\\)." }
+ (void) neg (i);
+ return i;
+}
+
+constexpr int
+f6 (auto i)
+{
+ auto x = neg (i + foo (i)); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .foo<int>\\(i\\)." }
+ return x;
+}
+
+void
+g (int i)
+{
+ f1 (i); // { dg-error "call to consteval function .f1<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+ f1 (42);
+ f2 (i); // { dg-error "call to consteval function .f2<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+ f2 (42);
+ f3 (i); // { dg-error "call to consteval function .f3<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+ f3 (42);
+ f4 (i); // { dg-error "call to consteval function .f4<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+ f4 (42);
+ f5 (i); // { dg-error "call to consteval function .f5<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+ f5 (42);
+ f6 (i); // { dg-error "call to consteval function .f6<int>\\(i\\). is not a constant expression" }
+// { dg-error ".i. is not a constant expression" "" { target *-*-* } .-1 }
+ f6 (42);
+}
new file mode 100644
@@ -0,0 +1,107 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wno-c++23-extensions" }
+
+consteval int id (int i) { return i; }
+
+constexpr int
+f1 (auto i)
+{
+ auto p = &id; // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .id." }
+ (void) p;
+ return i;
+}
+
+constexpr int
+f2 (auto i)
+{
+ return f1 (i);
+}
+
+constexpr int
+f3 (auto i)
+{
+ return f2 (i);
+}
+
+constexpr int
+f4 (auto i)
+{
+ return f3 (i);
+}
+
+constexpr int
+f5 (auto i)
+{
+ return f4 (i); // { dg-message "promoted to an immediate function because its body contains an immediate-escalating expression .f4<int>\\(i\\)." }
+}
+
+constexpr int
+f6 (auto)
+{
+ // This call is a constant expression, so don't promote f6.
+ return f4 (42);
+}
+
+constexpr int
+f7 (auto i)
+{
+ if consteval {
+ auto p = &id;
+ (void) p;
+ }
+ return i;
+}
+
+constexpr int
+f8 (auto i)
+{
+ if not consteval {
+ (void) 0;
+ } else {
+ auto p = &id;
+ (void) p;
+ }
+ return i;
+}
+
+constexpr int
+f9 (auto i)
+{
+ if consteval {
+ return id(i);
+ }
+ return i;
+}
+
+constexpr int
+f10 (auto i)
+{
+ if not consteval {
+ (void) 0;
+ } else {
+ return id(i);
+ }
+ return i;
+}
+
+void
+g (int non_const)
+{
+ f1 (42);
+ f1 (non_const); // { dg-error "call to consteval function .f1<int>\\(non_const\\). is not a constant expression" }
+// { dg-error ".non_const. is not a constant expression" "" { target *-*-* } .-1 }
+ f5 (42);
+ f5 (non_const); // { dg-error "call to consteval function .f5<int>\\(non_const\\). is not a constant expression" }
+// { dg-error ".non_const. is not a constant expression" "" { target *-*-* } .-1 }
+ f6 (42);
+ f6 (non_const);
+ f7 (42);
+ f7 (non_const);
+ f8 (42);
+ f8 (non_const);
+ f9 (42);
+ f9 (non_const);
+ f10 (42);
+ f10 (non_const);
+}
new file mode 100644
@@ -0,0 +1,73 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Test unevaluated operands.
+
+consteval int id (int i) { return i; }
+
+constexpr int
+f1 (auto i)
+{
+ // Unevaluated operand -> don't promote.
+ auto p = sizeof (&id);
+ (void) p;
+ return i;
+}
+
+constexpr int
+f2 (auto i)
+{
+ // Unevaluated operand -> don't promote.
+ auto p = noexcept (id);
+ (void) p;
+ return i;
+}
+
+constexpr int
+f3 (auto i)
+{
+ // Unevaluated operand -> don't promote.
+ auto p = noexcept (id (i));
+ (void) p;
+ return i;
+}
+
+constexpr int
+f4 (auto i)
+{
+ // Unevaluated operand -> don't promote.
+ decltype(id) p;
+ (void) p;
+ return i;
+}
+
+constexpr int
+f5 (auto i)
+{
+ // Unevaluated operand -> don't promote.
+ __extension__ auto p = alignof (id (i));
+ (void) p;
+ return i;
+}
+
+constexpr int
+f6 (auto i) requires requires { id (i); }
+{
+ return i;
+}
+
+void
+g (int non_const)
+{
+ f1 (42);
+ f1 (non_const);
+ f2 (42);
+ f2 (non_const);
+ f3 (42);
+ f3 (non_const);
+ f4 (42);
+ f4 (non_const);
+ f5 (42);
+ f5 (non_const);
+ f6 (42);
+ f6 (non_const);
+}
new file mode 100644
@@ -0,0 +1,17 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-fno-immediate-escalation" }
+
+consteval int id(int i) { return i; }
+
+constexpr int
+f (auto i)
+{
+ return id (i); // { dg-error "not a constant expression" }
+}
+
+int
+g ()
+{
+ return f (42);
+}
new file mode 100644
@@ -0,0 +1,20 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int id(int i) { return i; }
+
+constexpr int
+f (auto t)
+{
+ return t + id (t);
+}
+
+constexpr int
+f2 (auto t)
+{
+ return t + f(t); // { dg-message "immediate-escalating expression .f<int>\\(t\\)." }
+}
+
+int z; // { dg-message "not const" }
+auto y1 = f2 (42);
+auto y2 = f2 (z); // { dg-error "value of .z. is not usable in a constant expression|call to consteval function" }
new file mode 100644
@@ -0,0 +1,7 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int g(int p) { return p; }
+template<typename T> constexpr auto f(T) { return g; }
+int r = f(1)(2); // proposed ok
+int s = f(1)(2) + r; // { dg-error "call to consteval function|returns address of immediate function" }
new file mode 100644
@@ -0,0 +1,90 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Testcase from P2564R3.
+
+consteval int id(int i) { return i; }
+constexpr char id(char c) { return c; }
+
+template<class T>
+constexpr int f(T t) {
+ return t + id(t); // { dg-message "immediate-escalating expression .id\\(t\\)." }
+}
+
+auto a = &f<char>; // OK, f<char> is not an immediate function
+auto b = &f<int>; // { dg-error "taking address of an immediate function" }
+
+static_assert(f(3) == 6); // OK
+
+template<class T>
+constexpr int g(T t) { // g<int> is not an immediate function
+ return t + id(42); // because id(42) is already a constant
+}
+
+template<class T, class F>
+constexpr bool is_not(T t, F f) {
+ return not f(t);
+}
+
+consteval bool is_even(int i) { return i % 2 == 0; }
+
+static_assert(is_not(5, is_even)); // OK
+
+int x = 0;
+
+template<class T>
+constexpr T h(T t = id(x)) { // h<int> is not an immediate function
+ return t;
+}
+
+template<class T>
+constexpr T hh() { // hh<int> is an immediate function
+ return h<T>(); // { dg-error "the value of .x. is not usable in a constant expression" }
+// { dg-message "immediate-escalating expression .id\\(x\\)." "" { target *-*-* } .-1 }
+}
+
+int i = hh<int>(); // { dg-error "call to consteval function|called in a constant expression" }
+ // error: hh<int>() is an immediate-escalating expression
+ // outside of an immediate-escalating function
+struct A {
+ int x;
+ int y = id(x);
+};
+
+// [expr.const]#example-9 says:
+// k<int> is not an immediate function because A(42) is a
+// constant expression and thus not immediate-escalating
+// In the evaluation of A(42), the member x has just been initialized
+// to constant 42. And A(42) is constant-evaluated because "An aggregate
+// initialization is an immediate invocation if it evaluates a default
+// member initializer that has a subexpression that is an
+// immediate-escalating expression."
+template<class T>
+constexpr int k(int) {
+ return A(42).y;
+}
+
+int
+test (int i)
+{
+ int r = g (42) + g(i);
+ int t = k<int>(42)
+ + k<int>(i); // { dg-bogus "call to|constant" "" { xfail *-*-* } }
+ return r + t;
+}
+
+// Just like above, but make the call to id(x) actually a constant.
+struct A2 {
+ static constexpr int x = 42;
+ int y = id(x);
+};
+
+template<class T>
+constexpr int k2(int) {
+ return A2(42).y;
+}
+
+int
+test2 (int i)
+{
+ return k2<int>(42) + k2<int>(i);
+}
new file mode 100644
@@ -0,0 +1,27 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// Cribbed from clang's cxx2b-consteval-propagate.cpp.
+
+consteval int id(int i) { return i; }
+
+template <typename T>
+constexpr int f(T t);
+
+auto a1 = &f<char>;
+auto b1 = &f<int>;
+
+template <typename T>
+constexpr int f(T t) {
+ return id(0);
+}
+
+template <typename T>
+constexpr int f2(T);
+
+auto a2 = &f2<char>; // { dg-error "taking address" }
+auto b2 = &f2<int>; // { dg-error "taking address" }
+
+template <typename T>
+constexpr int f2(T t) {
+ return id(t);
+}
new file mode 100644
@@ -0,0 +1,30 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// From clang's cxx2b-consteval-propagate.cpp. This test ICEd when I worked on
+// P2564.
+
+consteval int f (int);
+
+struct S {
+ int a = 0;
+ int b = f (a);
+};
+
+constexpr bool
+g (auto i)
+{
+ S s{i};
+ return s.b == 2 *i;
+}
+
+consteval int
+f (int i)
+{
+ return 2 * i;
+}
+
+void
+test ()
+{
+ static_assert(g(42));
+}
new file mode 100644
@@ -0,0 +1,27 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int f (int i) { return i; }
+
+struct S {
+ int x = f(42);
+};
+
+constexpr S
+immediate (auto)
+{
+ return S{};
+}
+
+void
+g ()
+{
+ immediate (0);
+}
+
+consteval void
+test ()
+{
+ constexpr S s = immediate(0);
+ static_assert(s.x == 42);
+}
new file mode 100644
@@ -0,0 +1,59 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// From cxx2b-consteval-propagate.cpp.
+
+void side_effect();
+
+consteval int
+f (int x)
+{
+ if (!x)
+ side_effect(); // { dg-error "call to non-.constexpr. function" }
+ return x;
+}
+
+struct SS {
+ int y = f(1);
+ int x = f(0);
+ SS();
+};
+SS::SS(){} // { dg-error "call to consteval function" }
+
+consteval int
+f2 (int x)
+{
+ if (!__builtin_is_constant_evaluated ())
+ side_effect();
+ return x;
+}
+
+struct S2 {
+ int x = f2(0);
+ constexpr S2();
+};
+
+constexpr S2::S2(){}
+S2 s = {};
+constinit S2 s2 = {};
+
+struct S3 {
+ int x = f2(0);
+ S3();
+};
+S3::S3(){}
+
+consteval int undef (int x); // { dg-warning "never defined" }
+
+struct X {
+ int a = sizeof(undef(0));
+ int x = undef(0);
+
+ X() = default; // { dg-error "modification of .x. is not a constant expression" }
+};
+
+void
+test ()
+{
+ [[maybe_unused]] X x; // { dg-error "call to consteval function" }
+// { dg-message "promoted to an immediate function" "" { target *-*-* } .-1 }
+}
new file mode 100644
@@ -0,0 +1,76 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// The problem here was that while parsing, we first process calling
+// 'f' from 'g' but only when instantiating 'f<int>' do we promote 'f'
+// to consteval. When the var we're initializing is marked constexpr,
+// store_init_value detects the problem that we're calling a consteval
+// function with non-const argument.
+
+consteval int id(int i) { return i; }
+
+// Don't let the instantiations confuse us, e.g. instantiating a fn
+// prior to entering 'g'.
+template <typename T>
+constexpr int f1(T t) { return id (t); }
+
+template <typename T>
+constexpr int f2(T t) { return id (t); }
+
+template <typename T>
+constexpr int f3(T t) { return id (t); }
+
+template <typename T>
+constexpr int f4(T t) { return id (t); }
+
+template <typename T>
+constexpr int f5(T t) { return id (t); }
+
+template <typename T>
+constexpr int f6(T t) { return id (t); }
+
+template <typename T>
+constexpr int f7(T t) { return id (t); }
+
+template <typename T>
+constexpr int f8(T t) { return id (t); }
+
+template <typename T>
+constexpr int f9(T t) { return id (t); }
+
+template <typename T>
+constexpr int f10(T t) { return id (t); }
+
+template <typename T>
+constexpr int g1(T t) { auto p = id; return p (t); }
+
+int non_const;
+
+auto a1 = f1 (non_const); // { dg-error "call to consteval function|not usable" }
+constexpr auto a2 = f2 (non_const); // { dg-error "not a constant|not usable" }
+auto a3 = f3 (42);
+constexpr auto a4 = f4 (42);
+
+void
+g ()
+{
+ auto a5 = f5 (non_const); // { dg-error "not a constant|not usable" }
+ constexpr auto a6 = f6 (non_const); // { dg-error "not usable" }
+ auto a7 = f7 (42);
+ constexpr auto a8 = f8 (42);
+ (void) f9 (non_const); // { dg-error "not a constant|not usable" }
+ (void) f10 (42);
+ (void) g1 (non_const); // { dg-error "not a constant|not usable" }
+}
+
+struct S {
+ int y;
+ int x = id (y);
+ // Promoted to consteval.
+ template<typename T>
+ constexpr S(T t) : y (id (t)) {}
+};
+
+S s1(1);
+S s2(non_const); // { dg-error "call to consteval function|not usable" }
+constexpr S s3(1);
+constexpr S s4(non_const); // { dg-error "not usable" }
new file mode 100644
@@ -0,0 +1,82 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-options "-Wno-c++23-extensions" }
+
+consteval int zero (int)
+{
+ return 0;
+}
+
+struct A {
+ // A::A(auto) promoted to consteval.
+ constexpr A(auto i) { zero (i); }
+};
+
+// 'f1<int>' is an immediate function because its body contains a call to an
+// immediate constructor 'A<int>' and that call is not a constant expression
+constexpr void
+f1 (auto i)
+{
+ A a{i};
+}
+
+// 'f2<int>' is an immediate function because its body contains a call to an
+// immediate constructor 'A<int>' and that call is not a constant expression
+constexpr void
+f2 (auto i)
+{
+ A a{i};
+}
+
+void
+f3 (int i)
+{
+ A a{i}; // { dg-error "not a constant expression" }
+}
+
+inline void
+f7 (int i)
+{
+ A a{i}; // { dg-error "not a constant expression" }
+}
+
+constexpr void
+f8 (int i)
+{
+ A a{i}; // { dg-error "not a constant expression" }
+}
+
+/* "An expression or conversion is immediate-escalating if it is not initially
+ in an immediate function context" but this one is, so we do *not* promote
+ f4 to consteval. */
+constexpr void
+f4 (auto i)
+{
+ if consteval {
+ A a{i};
+ }
+}
+
+constexpr void
+f5 (auto i)
+{
+ if not consteval {
+ (void) 0;
+ } else {
+ A a{i};
+ }
+}
+
+void
+f6 (int x)
+{
+ f1 (0);
+ f1 (x); // { dg-error "not a constant expression" }
+ f2 (0);
+ f2 (x); // { dg-error "not a constant expression" }
+ f3 (0);
+ f4 (x);
+ f4 (0);
+ f5 (x);
+ f5 (0);
+}
new file mode 100644
@@ -0,0 +1,67 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int
+zero (int)
+{
+ return 0;
+}
+
+constexpr int
+f1 (auto i)
+{
+ return zero (i);
+}
+
+constexpr int
+f2 (auto i)
+{
+ return f1 (i);
+}
+
+constexpr int
+f3 (auto i)
+{
+ return f2 (i);
+}
+
+constexpr int
+f4 (auto i)
+{
+ return f3 (i);
+}
+
+constexpr int
+f5 (auto i)
+{
+ return f4 (i);
+}
+
+constexpr int
+f6 (auto)
+{
+ // This call is a constant expression, so don't promote f6.
+ return f5 (42);
+}
+
+constexpr int
+f7 (auto)
+{
+ // This call is a constant expression, so don't promote f7.
+ return zero (42);
+}
+
+auto p1 = &f5<int>; // { dg-error "taking address" }
+static auto p2 = &f4<int>; // { dg-error "taking address" }
+auto p3 = &f6<int>;
+static auto p4 = &f6<int>;
+auto p5 = &f7<int>;
+static auto p6 = &f7<int>;
+
+void
+g ()
+{
+ static auto q1 = &f4<int>; // { dg-error "taking address" }
+ static auto q2 = &f6<int>;
+ static auto q3 = &f7<int>;
+}
@@ -7,9 +7,11 @@ constexpr int a = bar (1);
constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" }
constexpr int c = 0 ? bar (3) : 1;
const int d = bar (4); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
const int e = 0 ? bar (5) : 1;
int f = bar (1);
int g = bar (6); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
int h = 0 ? bar (7) : 1;
void
@@ -19,25 +21,35 @@ foo ()
constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" }
constexpr int c = 0 ? bar (3) : 1;
const int d = bar (4); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
const int e = 0 ? bar (5) : 1;
int f = bar (1);
int g = bar (6); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
h += 0 ? bar (8) : 1; // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if (0)
bar (9); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
else
bar (10); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if (1)
bar (11); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
else
bar (12); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if constexpr (0)
bar (13);
else
bar (14); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if constexpr (1)
bar (15); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
else
bar (16);
}
@@ -120,18 +132,24 @@ quux ()
{
if (0)
bar ((T) 2); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
else
bar ((T) 3); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if (1)
bar ((T) 4); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
else
bar ((T) 5); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if constexpr (0)
bar ((T) 6);
else
bar ((T) 7); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
if constexpr (1)
bar ((T) 8); // { dg-message "in 'constexpr' expansion of" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
else
bar ((T) 9);
}
@@ -16,8 +16,8 @@ consteval auto [ b, c ] = S (); // { dg-error "structured binding declaration c
int f5 (consteval int x) { return x; } // { dg-error "a parameter cannot be declared 'consteval'" }
consteval int f6 (int x) { return x; }
int d = 6; // { dg-message "'int d' is not const" }
-int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" }
-constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" }
+int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression|call to consteval function" }
+constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression|call to consteval function" }
constexpr int f = f7 (5);
using fnptr = int (int);
fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" }
@@ -6,6 +6,7 @@ constexpr int
foo (bool b)
{
return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
}
static_assert (foo (false) == 2);
@@ -21,13 +22,20 @@ void
g ()
{
__extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
int a2[sizeof (bar(3))];
int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
__extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
__extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
}
@@ -6,17 +6,17 @@ consteval int id (int i) { return i; }
void
g (int i)
{
- 1 ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? 1 : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((i ? 1 : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? i : 1), id (i), 1); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? 1 : i), id (i), 1); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((i ? -i : i), id (i), 1); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? 1 : id (i)), id (42), 1); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? 1 : id (42)), id (i)); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? 1 : id (42)), id (i), 1); // { dg-error "'i' is not a constant expression" }
- id (i) ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((1 ? 1 : id (i)), id (i)); // { dg-error "'i' is not a constant expression" }
- 1 ? id (i) : ((1 ? 1 : id (i)), id (i)); // { dg-error "'i' is not a constant expression" }
- 1 ? 1 : ((id (i) ? 1 : 1), id (i)); // { dg-error "'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((i ? 1 : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? i : 1), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : i), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((i ? -i : i), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : id (i)), id (42), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : id (42)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : id (42)), id (i), 1); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ id (i) ? 1 : ((1 ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((1 ? 1 : id (i)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? id (i) : ((1 ? 1 : id (i)), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
+ 1 ? 1 : ((id (i) ? 1 : 1), id (i)); // { dg-error "call to consteval function|'i' is not a constant expression" }
}
@@ -13,6 +13,7 @@ template <int N>
void qux ()
{
int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
}
// This function is not instantiated so NDR.
@@ -30,3 +31,4 @@ baz ()
}
int a = bar (2); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
+// { dg-error "call to consteval function" "" { target *-*-* } .-1 }
@@ -480,8 +480,8 @@
#ifndef __cpp_consteval
# error "__cpp_consteval"
-#elif __cpp_consteval != 201811
-# error "__cpp_consteval != 201811"
+#elif __cpp_consteval != 202211L
+# error "__cpp_consteval != 202211L"
#endif
#ifndef __cpp_concepts
@@ -22,6 +22,6 @@ struct Z: Y<int>
int main()
{
X<char>() == X<char>(); // { dg-error "no match" }
- X<int> x; x == x; // { dg-error "x' is not usable in a constant expression" }
+ X<int> x; x == x; // { dg-error "x' is not usable in a constant expression|call to consteval function" }
Y<int>() == Y<int>(); // { dg-warning "nodiscard" }
}
@@ -52,3 +52,4 @@ test01()
// { dg-prune-output "reinterpret_cast.* is not a constant expression" }
// { dg-prune-output "cast from 'void.' is not allowed" }
+// { dg-prune-output "not a constant expression" }
@@ -2,5 +2,5 @@
#include <format>
-auto s = std::format(" {9} ");
+auto s = std::format(" {9} "); // { dg-error "call to consteval function" }
// { dg-error "invalid.arg.id" "" { target *-*-* } 0 }