On 8/23/23 15:49, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>
> -- >8 --
>
> This patch implements P2564, described at <wg21.link/p2564>, whereby
> certain functions are promoted to consteval. For example:
Great, thanks!
> 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:
>
> q.C: In function 'void g(int)':
> q.C:11:5: error: call to consteval function 'f<int>(i)' is not a constant expression
> 11 | f (i);
> | ~~^~~
> q.C:6:16: 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);
> | ~~^~~
>
> 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.
What I expected was that we would instantiate when we see a call, i.e.
immediate_invocation_p would instantiate its argument if it's an
immediate-escalating function. But...
> I previously thought that I could get past that by implementing the propagation in
> cp_gimplify_expr at which point we have already instantiated everything
> via instantiate_pending_templates.
...this sounds like a clever way to avoid the problems we tend to see
with eager instantiation (e.g. constexpr-inst1.C). It still seems
promising to me.
> But I realized that we don't gimplify e.g.
>
> static auto p = &id;
Indeed, just cp_fully_fold_init them, at least until we build the global
init function.
> and so we'd never detect taking the address of a consteval function.
> Therefore this patch instantiates immediate-escalating functions
> beforehand.
Relatedly, in one of the testcases you have
> +template <typename T>
> +constexpr int f2(T);
> +
> +// ??? clang++ emits
> +// error: immediate function 'f2<char>' used before it is defined
> +// error: immediate function 'f2<int>' used before it is defined
> +// but at this point we don't know that f2 will be updated to consteval?
> +auto a2 = &f2<char>;
> +auto b2 = &f2<int>;
> +
> +template <typename T>
> +constexpr int f2(T t) {
> + return id(t);
This is a case where we can't immediately resolve the question anyway,
we need to remember where we've seen bare references to
immediate-escalating functions outside of other immediate-escalating (or
already immediate) functions, and complain later if they became
consteval when instantiated.
I don't see why this is a significant obstacle to doing escalation late.
> * cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define.
Is this flag just for rejecting consteval-prop10.C? I think we should
not try to reject it. This came up previously in the context of my
patch for CWG2631, at which point I argued to CWG that it should be
allowed, but nobody responded. I'll ping that thread now, but in the
mean time let's not go out of our way to reject this testcase that seems
to me pretty clearly allowed by the current wording: that expression
invokes an immediate function, so it's an immediate invocation.
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index 673ec91d60e..31f78b71fe1 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -9735,7 +9735,9 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref)
>
> /* Return true if in an immediate function context, or an unevaluated operand,
> or a default argument/member initializer, or a subexpression of an immediate
> - invocation. */
> + invocation.
> + ??? P2564 says that "a subexpression of a manifestly constant-evaluated
> + expression or conversion" is also an immediate function context. */
Yes, that was to allow the consteval-prop2.C
static_assert(is_not(5, is_even));
Since the operand of static_assert is manifestly constant-evaluated,
it's OK that we name consteval is_even there.
We currently use this function to trigger evaluating immediate
evaluations, and later (through immediate_invocation_p) to trigger
errors, and other constant-evaluation happens between those two, so in
most cases it's OK that this function does not identify that situation.
But there's another example in the paper, that this patch doesn't handle:
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; // error
Here f(1) looks like an immediate invocation but is not itself constant,
and GCC with this patch tries to immediately evaluate f(1) and gives an
error. But since it's in a manifestly constant-evaluated expression, it
isn't actually an immediate invocation, and giving an error is wrong.
Maybe we want to stop doing immediate evaluation in build_over_call (and
bot_replace) at all, and instead build the call as normal and perhaps
error later in cp_fold_r? If I just disable the immediate_invocation_p
blocks in build_over_call the testcase above works as the comments
indicate. Maybe removing the bot_replace code would also help with 110997?
> +/* Return true if FN is an immediate-escalating function. */
> +
> +bool
> +immediate_escalating_function_p (tree fn)
> +{
> + if (!fn)
> + return false;
> +
> + gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL);
I think we might want to have a flag for this, like I did with
-fnew-ttp-matching. Say, -fimmediate-escalation?
> + /* [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 CALL does not need to produce a constant
> + expression. ??? It's ugly to call cxx_constant_value twice in
> + some cases. */
> + if (cxx_constant_value (call, obj_arg, tf_none) == error_mark_node
> + && maybe_promote_function_to_consteval (current_function_decl))
> + return orig_call;
> call = cxx_constant_value (call, obj_arg, complain);
As discussed above, I think promoting or complaining here is now wrong
because we might be in a manifestly constant-evaluated expression. At
this point I think there's probably no point even trying to do
evaluation here, we can just wait until cp_fold_r (or the later
escalation time?).
> diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> index 206e791fcfd..902c9d54741 100644
> --- a/gcc/cp/cp-gimplify.cc
> +++ b/gcc/cp/cp-gimplify.cc
> > +/* 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,
> + or NULL_TREE, if we're taking the address of a function. This function
> + will likely instantiate DECL. */
> +
> +static void
> +maybe_escalate_decl_and_cfun (tree decl, tree call = NULL_TREE)
> +{
> + if (cxx_dialect <= cxx17 || cp_unevaluated_operand)
> + return;
Return early if cfun is not immediate-escalating?
> + /* Compiler-generated functions don't seem like good candidates for
> + promoting. */
> + if (DECL_ARTIFICIAL (decl) || DECL_ABSTRACT_P (decl))
Why DECL_ABSTRACT_P? I think that will be set on pre-cloning constructors.
> + 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 (!DECL_IMMEDIATE_FUNCTION_P (decl)
> + && immediate_escalating_function_p (decl))
> + {
> + /* PSET holds the set of functions we have already perused for
> + immediate-escalating expressions. */
> + static hash_set<tree> pset;
It will also hold pointers to all the function-internal expressions that
we ever walk through, which seems like a problem. IMO it would be
better to have a persistent pset that just tracks the functions, in
addition to the usual tree-walk pset that gets thrown away each time.
That is, if we need to do this walk at all; see the next comment.
> + find_escalating_expr_t data = { decl, &pset };
> + /* We can't delay instantiating any further. We need to see the
> + whole tree to decide whether DECL is consteval.
> + ??? Consider adding a sentinel to instantiate_constexpr_fns so
> + that we don't escalate while we instantiate while we escalate...
> + which seems dodgy.
It doesn't seem that dodgy to me. But if we're doing escalation at
finish_function time rather than trying to defer to EOF we shouldn't
need to do this tree walk at all, only instantiation, and the
instantiation will deal with its own escalation (or not). And if we do
end up deferring to EOF, we shouldn't need to do any instantiation
because as you mentioned in the intro we will have already done
instantiate_pending_templates.
> + FIXME Instantiating a defaulted ctor breaks modules (ICE due to
> + cp_function_chain being null). */
Sounds like this needs more investigation.
Incidentally, defaulted functions aren't instantiated, they're
synthesized. Which instantiate_constexpr_fns deals with, though it
seems a bit odd to do a "walk" over a single node, maybe we should
factor that bit out?
> + /* 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. */
It seems unfortunate to call cxx_constant_value here and then again
immediately in cp_fold_r in the case that call is indeed an immediate
invocation.
> + && (!call || cxx_constant_value (call, tf_none) == error_mark_node))
> + maybe_promote_function_to_consteval (current_function_decl);
> +}
> +
> @@ -1046,27 +1204,64 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
> switch (code)
> {
> case PTRMEM_CST:
> - if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
> - && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
> + if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL)
> {
> - if (!data->pset.add (stmt))
> - error_at (PTRMEM_CST_LOCATION (stmt),
> - "taking address of an immediate function %qD",
> - PTRMEM_CST_MEMBER (stmt));
> - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> - break;
> + tree decl = PTRMEM_CST_MEMBER (stmt);
> + maybe_escalate_decl_and_cfun (decl);
> + /* Taking the address of a consteval function is not permitted. */
> + if (immediate_invocation_p (decl))
> + {
> + if (!data->pset.add (stmt))
> + {
> + error_at (PTRMEM_CST_LOCATION (stmt),
> + "taking address of an immediate function %qD",
> + decl);
> + maybe_explain_promoted_consteval (PTRMEM_CST_LOCATION (stmt),
> + decl);
> + }
> + stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> + }
Can we share most of this code between PTRMEM_CST and ADDR_EXPR?
> + case CALL_EXPR:
> + if (tree fn = CALL_EXPR_FN (stmt))
> + if (TREE_CODE (fn) == ADDR_EXPR
> + && ADDR_EXPR_DENOTES_CALL_P (fn)
> + && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL)
> + {
> + tree decl = TREE_OPERAND (fn, 0);
> + maybe_escalate_decl_and_cfun (decl, stmt);
> + if (immediate_invocation_p (decl))
> + {
> + tree e = cxx_constant_value (stmt, tf_none);
> + if (e == error_mark_node)
> + {
> + const location_t loc = cp_expr_loc_or_input_loc (stmt);
> + error_at (loc, "call to consteval function %qE is not a "
> + "constant expression", stmt);
> + maybe_explain_promoted_consteval (loc, decl);
I think here we also want the usual cxx_constant_value errors explaining
why the call is not constant.
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index eb901683b6d..36d76a98233 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t)
> #define PTRMEM_OK_P(NODE) \
> TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF))
>
> +/* True if this ADDR_EXPR denotes a function call; that is, it's
> + fn() rather than &fn. */
> +#define ADDR_EXPR_DENOTES_CALL_P(NODE) \
> + (ADDR_EXPR_CHECK(NODE)->base.protected_flag)
As mentioned above, I don't think we need this.
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index d5c0c85ed51..03b642fefae 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -7248,7 +7248,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
> 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)))
> + && (DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0))
> + /* A constexpr function may be promoted to consteval. */
> + || DECL_DECLARED_CONSTEXPR_P (TREE_OPERAND (val, 0))))
Hmm, why don't we just do this for all functions?
> SET_EXPR_LOCATION (val, input_location);
>
> return val;
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C
> index 3ce513d6e25..7b3e2db6c8a 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C
> @@ -1,6 +1,9 @@
> +// This used to...
> // Test that we don't uselessly instantiate f<A> immediately while parsing g.
> // Doing so is permitted by the standard, but has no benefit and breaks code
> // unnecessarily.
> +// ...but due to P2564 we actually do instantiate f, because we need to figure
> +// out if it should be promoted to consteval. :-(
This is an example that would work better if we can wait until EOF to do
escalation.
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
> new file mode 100644
> index 00000000000..2ff16b88aa1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C
> @@ -0,0 +1,149 @@
> +// 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
> +f4 (T t)
> +{
> + auto p = id;
> + (void) p;
> + return t;
> +}
> +
> +// ??? We promote f4 to consteval but clang++ doesn't seem to.
> +auto p6 = &f4<int>; // { dg-error "taking address of an immediate function" }
I imagine clang doesn't promote because it optimizes away the unused
reference to id. That seems like a clang bug.
> +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};
> +}
Maybe also a non-template function that gets an error?
> +struct C {
> + // C::C(int) promoted to consteval.
> + consteval C (int) {};
Looks explicitly declared consteval, not promoted. :)
> +struct Y {
> + int y;
> + int x = id (y);
> + consteval Y (int i) : y (id (i)) {}
> +};
> +
> +Y y1(1);
> +Y y2(g); // { dg-error "not usable" }
How about a variant of this where Y(int) is promoted to consteval?
> +auto l1 = [](int i) constexpr {
> + int t = id (i);
> + return id (0);
> +};
> +
> +int (*p)(int) = l1; // { dg-error "returns address of immediate function" }
What if the lambda isn't explicitly constexpr?
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
> new file mode 100644
> index 00000000000..00af77042cb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C
> @@ -0,0 +1,9 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +
> +consteval int foo () { return 42; }
> +// Even though the standard doesn't define "invocation", this is one.
> +// But the user actually wrote '&' so presumably we should error. If
> +// we decide to accept this, move the ADDR_EXPR_DENOTES_CALL_P setting
> +// to build_cxx_call.
> +int bar () { return (*(&foo)) (); } // { dg-error "taking address" }
As above, I think we can/should accept this, so I don't see the purpose
of ADDR_EXPR_DENOTES_CALL_P.
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
> new file mode 100644
> index 00000000000..f52ba07ec6c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C
> @@ -0,0 +1,81 @@
> +// P2564R3
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-Wno-c++23-extensions" }
> +
> +consteval int id (int i) { return 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;
What if we return id(i) in the if consteval?
> + }
> + return i;
> +}
> +
> +constexpr int
> +f8 (auto i)
> +{
> + if not consteval {
> + (void) 0;
> + } else {
> + auto p = &id;
> + (void) p;
And here.
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
> new file mode 100644
> index 00000000000..846e5603fee
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C
> @@ -0,0 +1,32 @@
> +// 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;
> +
> +template<typename>
> +constexpr int
> +f2 (int i = id (non_const))
> +{
> + return i;
> +}
> +
> +void
> +g (int i)
> +{
> + f1<int> (42);
> + f1<int> (i);
> + f1<int> ();
> + f2<int> (42);
> + f2<int> (i);
> + f2<int> (); // { dg-error "not usable" }
> +}
Also test that a function f3(auto) that calls f2<int>() gets promoted?
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
> new file mode 100644
> index 00000000000..a5803736d37
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C
> @@ -0,0 +1,54 @@
> +// 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);
> +}
> +
> +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
> +}
I don't see any uses of g to test the comment?
> +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 "not usable in a constant expression" }
Why is there an error on this line?
> +}
> +
> +int i = hh<int>(); // error: hh<int>() is an immediate-escalating expression
> + // outside of an immediate-escalating function
...and not on this line?
> +struct A {
> + int x;
> + int y = id(x);
> +};
> +
> +template<class T>
> +constexpr int k(int) { // k<int> is not an immediate function because A(42) is a
> + return A(42).y; // constant expression and thus not immediate-escalating
> +}
Needs use(s) of k to test the comment.
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
> new file mode 100644
> index 00000000000..35665304652
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C
> @@ -0,0 +1,31 @@
> +// 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);
> +
> +// ??? clang++ emits
> +// error: immediate function 'f2<char>' used before it is defined
> +// error: immediate function 'f2<int>' used before it is defined
> +// but at this point we don't know that f2 will be updated to consteval?
Right, as mentioned above it seems that we need to keep track of forward
references to immediate-escalating functions in case they become
consteval. If we're promoting early, only ones that aren't defined yet.
> +auto a2 = &f2<char>;
> +auto b2 = &f2<int>;
> +
> +template <typename T>
> +constexpr int f2(T t) {
> + return id(t);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
> new file mode 100644
> index 00000000000..8cc08c7f6d8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C
> @@ -0,0 +1,58 @@
> +// 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(){}
Maybe a dg-message here, assuming the context for the above error
mentions this line?
> +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);
No diagnostic on this line?
> +
> + X() = default; // { dg-error "used before its definition" }
> +};
> +
> +void
> +test ()
> +{
> + [[maybe_unused]] X x;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
> new file mode 100644
> index 00000000000..41c47992ef7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C
> @@ -0,0 +1,71 @@
> +// 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};
> +}
> +
> +// ??? This doesn't give error when inline/constexpr and not called.
Is that still true with the current patch? If so, it should be fixed.
In any case, it should be tested.
> +void
> +f3 (int i)
> +{
> + A a{i}; // { dg-error "not a constant expression" }
> +}
> diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc
> index 16e94864f3b..329f3a6cc33 100644
> --- a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc
> @@ -8,11 +8,11 @@ test01()
> {
> std::optional<int> o;
> o.or_else([&] { return o; }); // OK
> - o.or_else([] { return std::optional<short>(); }); // { dg-error "here" }
> - o.or_else([] { return 1; }); // { dg-error "here" }
> - std::move(o).or_else([] { return std::optional<short>(); }); // { dg-error "here" }
> - std::move(o).or_else([] { return 1; }); // { dg-error "here" }
> -}
> + o.or_else([] { return std::optional<short>(); });
> + o.or_else([] { return 1; });
> + std::move(o).or_else([] { return std::optional<short>(); });
> + std::move(o).or_else([] { return 1; });
> +} // { dg-error "here" }
This seems like a significant regression in source location quality.
Likewise for several other libstdc++ tests.
Jason
@@ -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");
@@ -9735,7 +9735,9 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref)
/* Return true if in an immediate function context, or an unevaluated operand,
or a default argument/member initializer, or a subexpression of an immediate
- invocation. */
+ invocation.
+ ??? P2564 says that "a subexpression of a manifestly constant-evaluated
+ expression or conversion" is also an immediate function context. */
bool
in_immediate_context ()
@@ -9754,7 +9756,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
@@ -9762,6 +9764,54 @@ immediate_invocation_p (tree fn)
&& !in_immediate_context ());
}
+/* Return true if FN is an immediate-escalating function. */
+
+bool
+immediate_escalating_function_p (tree fn)
+{
+ if (!fn)
+ return false;
+
+ gcc_checking_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+ /* 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. */
+
+bool
+maybe_promote_function_to_consteval (tree fn)
+{
+ if (fn
+ && !DECL_IMMEDIATE_FUNCTION_P (fn)
+ && immediate_escalating_function_p (fn))
+ {
+ SET_DECL_IMMEDIATE_FUNCTION_P (fn);
+ tree clone;
+ FOR_EACH_CLONE (clone, fn)
+ SET_DECL_IMMEDIATE_FUNCTION_P (clone);
+ return true;
+ }
+
+ return false;
+}
+
/* Subroutine of the various build_*_call functions. Overload resolution
has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a
@@ -10460,6 +10510,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
UNAVAILABLE_DEPRECATED_SUPPRESS);
fn = build_addr_func (fn, complain);
+ /* We're actually invoking the function. (Immediate functions get an
+ & when invoking it even though the user didn't use &.) */
+ ADDR_EXPR_DENOTES_CALL_P (fn) = true;
+
if (fn == error_mark_node)
return error_mark_node;
}
@@ -10484,6 +10538,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0));
if (immediate_invocation_p (fndecl))
{
+ tree orig_call = call;
tree obj_arg = NULL_TREE;
/* Undo convert_from_reference called by build_cxx_call. */
if (REFERENCE_REF_P (call))
@@ -10508,6 +10563,21 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
obj_arg = TREE_OPERAND (addr, 0);
}
}
+
+ /* [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 CALL does not need to produce a constant
+ expression. ??? It's ugly to call cxx_constant_value twice in
+ some cases. */
+ if (cxx_constant_value (call, obj_arg, tf_none) == error_mark_node
+ && maybe_promote_function_to_consteval (current_function_decl))
+ return orig_call;
call = cxx_constant_value (call, obj_arg, complain);
if (obj_arg && !error_operand_p (call))
call = cp_build_init_expr (obj_arg, call);
@@ -8247,7 +8247,7 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/)
return NULL_TREE;
}
-static void
+void
instantiate_constexpr_fns (tree t)
{
location_t loc = input_location;
@@ -426,6 +426,113 @@ lvalue_has_side_effects (tree e)
return TREE_SIDE_EFFECTS (e);
}
+struct find_escalating_expr_t {
+ /* The function whose body we're traversing. Used to promote the current
+ function to consteval. */
+ tree caller;
+ /* To avoid walking same trees multiple times. */
+ hash_set<tree> *pset;
+};
+
+/* 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_)
+{
+ auto data = static_cast<find_escalating_expr_t *>(data_);
+ 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)
+ 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 || (data->pset && data->pset->add (decl)))
+ goto bail;
+
+ /* Not consteval yet, but could be. Have to look deeper. */
+ if (!DECL_IMMEDIATE_FUNCTION_P (decl)
+ && immediate_escalating_function_p (decl)
+ /* If we're not escalating, just looking for the escalating
+ expression, we don't need to recurse. */
+ && !data->pset)
+ {
+ find_escalating_expr_t d = { data->caller ? decl : NULL_TREE,
+ data->pset };
+ cp_walk_tree (&DECL_SAVED_TREE (decl), find_escalating_expr_r, &d,
+ 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. */
+ maybe_promote_function_to_consteval (data->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)
+{
+ /* A function cannot be marked both constexpr and consteval
+ except when we've promoted the former to the latter. */
+ if (is_instantiation_of_constexpr (fn))
+ {
+ /* See if we can figure out what made the function consteval. */
+ find_escalating_expr_t data = { NULL_TREE, nullptr };
+ tree x = cp_walk_tree (&DECL_SAVED_TREE (fn), find_escalating_expr_r,
+ &data, 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. */
@@ -1017,6 +1124,57 @@ maybe_replace_decl (tree *tp, tree decl, tree replacement)
return true;
}
+/* 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,
+ or NULL_TREE, if we're taking the address of a function. This function
+ will likely instantiate DECL. */
+
+static void
+maybe_escalate_decl_and_cfun (tree decl, tree call = NULL_TREE)
+{
+ if (cxx_dialect <= cxx17 || cp_unevaluated_operand)
+ return;
+
+ /* Compiler-generated functions don't seem like good candidates for
+ promoting. */
+ if (DECL_ARTIFICIAL (decl) || DECL_ABSTRACT_P (decl))
+ 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 (!DECL_IMMEDIATE_FUNCTION_P (decl)
+ && immediate_escalating_function_p (decl))
+ {
+ /* PSET holds the set of functions we have already perused for
+ immediate-escalating expressions. */
+ static hash_set<tree> pset;
+ find_escalating_expr_t data = { decl, &pset };
+ /* We can't delay instantiating any further. We need to see the
+ whole tree to decide whether DECL is consteval.
+ ??? Consider adding a sentinel to instantiate_constexpr_fns so
+ that we don't escalate while we instantiate while we escalate...
+ which seems dodgy.
+ FIXME Instantiating a defaulted ctor breaks modules (ICE due to
+ cp_function_chain being null). */
+ if (!(modules_p () && DECL_DEFAULTED_FN (decl)))
+ instantiate_constexpr_fns (decl);
+ cp_walk_tree (&DECL_SAVED_TREE (decl), find_escalating_expr_r,
+ &data, nullptr);
+ }
+
+ /* 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. */
+ && (!call || cxx_constant_value (call, tf_none) == error_mark_node))
+ maybe_promote_function_to_consteval (current_function_decl);
+}
+
/* Genericization context. */
struct cp_genericize_data
@@ -1046,27 +1204,64 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
switch (code)
{
case PTRMEM_CST:
- if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
- && DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
+ if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL)
{
- if (!data->pset.add (stmt))
- error_at (PTRMEM_CST_LOCATION (stmt),
- "taking address of an immediate function %qD",
- PTRMEM_CST_MEMBER (stmt));
- stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
- break;
+ tree decl = PTRMEM_CST_MEMBER (stmt);
+ maybe_escalate_decl_and_cfun (decl);
+ /* Taking the address of a consteval function is not permitted. */
+ if (immediate_invocation_p (decl))
+ {
+ if (!data->pset.add (stmt))
+ {
+ error_at (PTRMEM_CST_LOCATION (stmt),
+ "taking address of an immediate function %qD",
+ decl);
+ maybe_explain_promoted_consteval (PTRMEM_CST_LOCATION (stmt),
+ decl);
+ }
+ stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+ }
}
break;
+ case CALL_EXPR:
+ if (tree fn = CALL_EXPR_FN (stmt))
+ if (TREE_CODE (fn) == ADDR_EXPR
+ && ADDR_EXPR_DENOTES_CALL_P (fn)
+ && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL)
+ {
+ tree decl = TREE_OPERAND (fn, 0);
+ maybe_escalate_decl_and_cfun (decl, stmt);
+ if (immediate_invocation_p (decl))
+ {
+ tree e = cxx_constant_value (stmt, tf_none);
+ if (e == error_mark_node)
+ {
+ const location_t loc = cp_expr_loc_or_input_loc (stmt);
+ error_at (loc, "call to consteval function %qE is not a "
+ "constant expression", stmt);
+ maybe_explain_promoted_consteval (loc, decl);
+ }
+ *stmt_p = stmt = e;
+ }
+ }
+ 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))
{
- error_at (EXPR_LOCATION (stmt),
- "taking address of an immediate function %qD",
- TREE_OPERAND (stmt, 0));
- stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
- break;
+ tree decl = TREE_OPERAND (stmt, 0);
+ maybe_escalate_decl_and_cfun (decl);
+ /* Taking the address of a consteval function is not permitted. */
+ if (immediate_invocation_p (decl))
+ {
+ const location_t loc = cp_expr_loc_or_input_loc (stmt);
+ error_at (loc, "taking address of an immediate function %qD",
+ decl);
+ maybe_explain_promoted_consteval (loc, decl);
+ stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
+ }
}
break;
@@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t)
#define PTRMEM_OK_P(NODE) \
TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF))
+/* True if this ADDR_EXPR denotes a function call; that is, it's
+ fn() rather than &fn. */
+#define ADDR_EXPR_DENOTES_CALL_P(NODE) \
+ (ADDR_EXPR_CHECK(NODE)->base.protected_flag)
+
/* Get the POINTER_TYPE to the METHOD_TYPE associated with this
pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true,
before using this macro. */
@@ -6713,6 +6718,9 @@ 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 bool immediate_escalating_function_p (tree);
+extern bool maybe_promote_function_to_consteval (tree);
extern tree build_cxx_call (tree, int, tree *,
tsubst_flags_t,
tree = NULL_TREE);
@@ -8576,6 +8584,7 @@ extern bool reduced_constant_expression_p (tree);
extern bool is_instantiation_of_constexpr (tree);
extern bool var_in_constexpr_fn (tree);
extern bool var_in_maybe_constexpr_fn (tree);
+extern void instantiate_constexpr_fns (tree);
extern bool maybe_constexpr_fn (tree);
extern void explain_invalid_constexpr_fn (tree);
extern vec<tree> cx_error_context (void);
@@ -7248,7 +7248,9 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
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)))
+ && (DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (val, 0))
+ /* A constexpr function may be promoted to consteval. */
+ || DECL_DECLARED_CONSTEXPR_P (TREE_OPERAND (val, 0))))
SET_EXPR_LOCATION (val, input_location);
return val;
@@ -1,6 +1,9 @@
+// This used to...
// Test that we don't uselessly instantiate f<A> immediately while parsing g.
// Doing so is permitted by the standard, but has no benefit and breaks code
// unnecessarily.
+// ...but due to P2564 we actually do instantiate f, because we need to figure
+// out if it should be promoted to consteval. :-(
// { dg-do compile { target c++11 } }
@@ -8,7 +11,7 @@
// { dg-additional-options -O }
template <class T>
-constexpr int f (const T* p) { return p->i; }
+constexpr int f (const T* p) { return p->i; } // { dg-error "invalid use of incomplete type" "" { target c++20 } }
constexpr int g(const struct A* p) { return f(p); }
@@ -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;
@@ -65,7 +65,7 @@ qux (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);
}
else
{
@@ -97,7 +97,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
{
@@ -109,11 +109,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 } }
{
@@ -125,5 +125,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
new file mode 100644
@@ -0,0 +1,149 @@
+// 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);
+}
+
+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);
+}
+
+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;
+ (void) p;
+ return t;
+}
+
+// ??? We promote f4 to consteval but clang++ doesn't seem to.
+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};
+}
+
+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;
+
+B b2(f0<int>(cid<int>(g))); // { dg-error "not usable" }
+
+struct C {
+ // C::C(int) promoted to consteval.
+ consteval C (int) {};
+};
+
+constexpr int
+f7 (auto t)
+{
+ C c(t);
+ return 0;
+}
+
+int i1 = f7 (g); // { dg-error "not a constant expression" }
+
+struct Y {
+ int y;
+ int x = id (y);
+ consteval Y (int i) : y (id (i)) {}
+};
+
+Y y1(1);
+Y y2(g); // { dg-error "not usable" }
+
+auto l1 = [](int i) constexpr {
+ int t = id (i);
+ return id (0);
+};
+
+int (*p)(int) = l1; // { dg-error "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 "" }
+ int a = [] (int u = undef ()) {
+ return u;
+ }();
+} s2;
new file mode 100644
@@ -0,0 +1,9 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+
+consteval int foo () { return 42; }
+// Even though the standard doesn't define "invocation", this is one.
+// But the user actually wrote '&' so presumably we should error. If
+// we decide to accept this, move the ADDR_EXPR_DENOTES_CALL_P setting
+// to build_cxx_call.
+int bar () { return (*(&foo)) (); } // { dg-error "taking address" }
new file mode 100644
@@ -0,0 +1,48 @@
+// 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
+f (auto i)
+{
+ return i + id (i);
+ /* { dg-begin-multiline-output "" }
+ return i + id (i);
+ ~~~^~~
+ { dg-end-multiline-output "" } */
+}
+
+void
+g (int x)
+{
+ f (x); // { dg-error "5:call to consteval function .f<int>\\(x\\). is not a constant expression" }
+ /* { dg-begin-multiline-output "" }
+ f (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" }
+ /* { 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,72 @@
+// 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\\(\\(2 \\* i\\)\\)." }
+}
+
+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" }
+ f1 (42);
+ f2 (i); // { dg-error "call to consteval function .f2<int>\\(i\\). is not a constant expression" }
+ f2 (42);
+ f3 (i); // { dg-error "call to consteval function .f3<int>\\(i\\). is not a constant expression" }
+ f3 (42);
+ f4 (i); // { dg-error "call to consteval function .f4<int>\\(i\\). is not a constant expression" }
+ f4 (42);
+ f5 (i); // { dg-error "call to consteval function .f5<int>\\(i\\). is not a constant expression" }
+ f5 (42);
+ f6 (i); // { dg-error "call to consteval function .f6<int>\\(i\\). is not a constant expression" }
+ f6 (42);
+}
new file mode 100644
@@ -0,0 +1,81 @@
+// 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;
+}
+
+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" }
+ f5 (42);
+ f5 (non_const); // { dg-error "call to consteval function .f5<int>\\(non_const\\). is not a constant expression" }
+ f6 (42);
+ f6 (non_const);
+ f7 (42);
+ f7 (non_const);
+ f8 (42);
+ f8 (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,32 @@
+// 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;
+
+template<typename>
+constexpr int
+f2 (int i = id (non_const))
+{
+ return i;
+}
+
+void
+g (int i)
+{
+ f1<int> (42);
+ f1<int> (i);
+ f1<int> ();
+ f2<int> (42);
+ f2<int> (i);
+ f2<int> (); // { dg-error "not usable" }
+}
new file mode 100644
@@ -0,0 +1,54 @@
+// 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);
+}
+
+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 "not usable in a constant expression" }
+}
+
+int i = hh<int>(); // error: hh<int>() is an immediate-escalating expression
+ // outside of an immediate-escalating function
+struct A {
+ int x;
+ int y = id(x);
+};
+
+template<class T>
+constexpr int k(int) { // k<int> is not an immediate function because A(42) is a
+ return A(42).y; // constant expression and thus not immediate-escalating
+}
new file mode 100644
@@ -0,0 +1,31 @@
+// 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);
+
+// ??? clang++ emits
+// error: immediate function 'f2<char>' used before it is defined
+// error: immediate function 'f2<int>' used before it is defined
+// but at this point we don't know that f2 will be updated to consteval?
+auto a2 = &f2<char>;
+auto b2 = &f2<int>;
+
+template <typename T>
+constexpr int f2(T t) {
+ return id(t);
+}
new file mode 100644
@@ -0,0 +1,32 @@
+// P2564R3
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-fchecking" }
+// { dg-ice "PR110997" }
+// 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,58 @@
+// 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(){}
+
+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 "used before its definition" }
+};
+
+void
+test ()
+{
+ [[maybe_unused]] X x;
+}
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 "not a constant" }
+constexpr auto a2 = f2 (non_const); // { dg-error "not usable" }
+auto a3 = f3 (42);
+constexpr auto a4 = f4 (42);
+
+void
+g ()
+{
+ auto a5 = f5 (non_const); // { dg-error "not a constant" }
+ 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" }
+ (void) f10 (42);
+ (void) g1 (non_const); // { dg-error "not a constant" }
+}
+
+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 "not usable" }
+constexpr S s3(1);
+constexpr S s4(non_const); // { dg-error "not usable" }
new file mode 100644
@@ -0,0 +1,71 @@
+// 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};
+}
+
+// ??? This doesn't give error when inline/constexpr and not called.
+void
+f3 (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>;
+}
@@ -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
@@ -20,17 +20,17 @@
#include <utility>
-bool a = std::cmp_greater_equal('1', 49); // { dg-error "constexpr" }
-bool b = std::cmp_greater_equal(50, '2'); // { dg-error "constexpr" }
-bool c = std::cmp_greater_equal(2, L'2'); // { dg-error "constexpr" }
-bool d = std::cmp_greater_equal(L'2', 2); // { dg-error "constexpr" }
-bool e = std::cmp_greater_equal(true, 1); // { dg-error "constexpr" }
-bool f = std::cmp_greater_equal(0, false); // { dg-error "constexpr" }
-bool g = std::cmp_greater_equal(97, u8'a'); // { dg-error "constexpr" }
-bool h = std::cmp_greater_equal(u8'a', 97); // { dg-error "constexpr" }
-bool i = std::cmp_greater_equal(97, u'a'); // { dg-error "constexpr" }
-bool j = std::cmp_greater_equal(u'a', 97); // { dg-error "constexpr" }
-bool k = std::cmp_greater_equal(97, U'a'); // { dg-error "constexpr" }
-bool l = std::cmp_greater_equal(U'a', 97); // { dg-error "constexpr" }
+bool a = std::cmp_greater_equal('1', 49); // { dg-error "required from" }
+bool b = std::cmp_greater_equal(50, '2'); // { dg-error "required from" }
+bool c = std::cmp_greater_equal(2, L'2'); // { dg-error "required from" }
+bool d = std::cmp_greater_equal(L'2', 2); // { dg-error "required from" }
+bool e = std::cmp_greater_equal(true, 1); // { dg-error "required from" }
+bool f = std::cmp_greater_equal(0, false); // { dg-error "required from" }
+bool g = std::cmp_greater_equal(97, u8'a'); // { dg-error "required from" }
+bool h = std::cmp_greater_equal(u8'a', 97); // { dg-error "required from" }
+bool i = std::cmp_greater_equal(97, u'a'); // { dg-error "required from" }
+bool j = std::cmp_greater_equal(u'a', 97); // { dg-error "required from" }
+bool k = std::cmp_greater_equal(97, U'a'); // { dg-error "required from" }
+bool l = std::cmp_greater_equal(U'a', 97); // { dg-error "required from" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
@@ -20,17 +20,17 @@
#include <utility>
-bool a = std::cmp_greater('1', 49); // { dg-error "constexpr" }
-bool b = std::cmp_greater(50, '2'); // { dg-error "constexpr" }
-bool c = std::cmp_greater(2, L'2'); // { dg-error "constexpr" }
-bool d = std::cmp_greater(L'2', 2); // { dg-error "constexpr" }
-bool e = std::cmp_greater(true, 1); // { dg-error "constexpr" }
-bool f = std::cmp_greater(0, false); // { dg-error "constexpr" }
-bool g = std::cmp_greater(97, u8'a'); // { dg-error "constexpr" }
-bool h = std::cmp_greater(u8'a', 97); // { dg-error "constexpr" }
-bool i = std::cmp_greater(97, u'a'); // { dg-error "constexpr" }
-bool j = std::cmp_greater(u'a', 97); // { dg-error "constexpr" }
-bool k = std::cmp_greater(97, U'a'); // { dg-error "constexpr" }
-bool l = std::cmp_greater(U'a', 97); // { dg-error "constexpr" }
+bool a = std::cmp_greater('1', 49); // { dg-error "required from" }
+bool b = std::cmp_greater(50, '2'); // { dg-error "required from" }
+bool c = std::cmp_greater(2, L'2'); // { dg-error "required from" }
+bool d = std::cmp_greater(L'2', 2); // { dg-error "required from" }
+bool e = std::cmp_greater(true, 1); // { dg-error "required from" }
+bool f = std::cmp_greater(0, false); // { dg-error "required from" }
+bool g = std::cmp_greater(97, u8'a'); // { dg-error "required from" }
+bool h = std::cmp_greater(u8'a', 97); // { dg-error "required from" }
+bool i = std::cmp_greater(97, u'a'); // { dg-error "required from" }
+bool j = std::cmp_greater(u'a', 97); // { dg-error "required from" }
+bool k = std::cmp_greater(97, U'a'); // { dg-error "required from" }
+bool l = std::cmp_greater(U'a', 97); // { dg-error "required from" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
@@ -20,17 +20,17 @@
#include <utility>
-bool a = std::cmp_less_equal('1', 49); // { dg-error "constexpr" }
-bool b = std::cmp_less_equal(50, '2'); // { dg-error "constexpr" }
-bool c = std::cmp_less_equal(2, L'2'); // { dg-error "constexpr" }
-bool d = std::cmp_less_equal(L'2', 2); // { dg-error "constexpr" }
-bool e = std::cmp_less_equal(true, 1); // { dg-error "constexpr" }
-bool f = std::cmp_less_equal(0, false); // { dg-error "constexpr" }
-bool g = std::cmp_less_equal(97, u8'a'); // { dg-error "constexpr" }
-bool h = std::cmp_less_equal(u8'a', 97); // { dg-error "constexpr" }
-bool i = std::cmp_less_equal(97, u'a'); // { dg-error "constexpr" }
-bool j = std::cmp_less_equal(u'a', 97); // { dg-error "constexpr" }
-bool k = std::cmp_less_equal(97, U'a'); // { dg-error "constexpr" }
-bool l = std::cmp_less_equal(U'a', 97); // { dg-error "constexpr" }
+bool a = std::cmp_less_equal('1', 49); // { dg-error "required from" }
+bool b = std::cmp_less_equal(50, '2'); // { dg-error "required from" }
+bool c = std::cmp_less_equal(2, L'2'); // { dg-error "required from" }
+bool d = std::cmp_less_equal(L'2', 2); // { dg-error "required from" }
+bool e = std::cmp_less_equal(true, 1); // { dg-error "required from" }
+bool f = std::cmp_less_equal(0, false); // { dg-error "required from" }
+bool g = std::cmp_less_equal(97, u8'a'); // { dg-error "required from" }
+bool h = std::cmp_less_equal(u8'a', 97); // { dg-error "required from" }
+bool i = std::cmp_less_equal(97, u'a'); // { dg-error "required from" }
+bool j = std::cmp_less_equal(u'a', 97); // { dg-error "required from" }
+bool k = std::cmp_less_equal(97, U'a'); // { dg-error "required from" }
+bool l = std::cmp_less_equal(U'a', 97); // { dg-error "required from" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
@@ -8,11 +8,11 @@ test01()
{
std::optional<int> o;
o.or_else([&] { return o; }); // OK
- o.or_else([] { return std::optional<short>(); }); // { dg-error "here" }
- o.or_else([] { return 1; }); // { dg-error "here" }
- std::move(o).or_else([] { return std::optional<short>(); }); // { dg-error "here" }
- std::move(o).or_else([] { return 1; }); // { dg-error "here" }
-}
+ o.or_else([] { return std::optional<short>(); });
+ o.or_else([] { return 1; });
+ std::move(o).or_else([] { return std::optional<short>(); });
+ std::move(o).or_else([] { return 1; });
+} // { dg-error "here" }
// { dg-prune-output "static assertion failed" }
@@ -24,8 +24,8 @@ void
test01()
{
int two_dee[3][4];
- std::to_array(two_dee); // { dg-error "here" }
-}
+ std::to_array(two_dee);
+} // { dg-error "here" }
void
test02()
@@ -34,8 +34,8 @@ test02()
{
int two_dee[3][4];
};
- std::to_array(X{}.two_dee); // { dg-error "here" }
-}
+ std::to_array(X{}.two_dee);
+} // { dg-error "here" }
void
test03()
@@ -47,10 +47,10 @@ test03()
};
MoveOnly mo[2];
- std::to_array(mo); // { dg-error "here" }
+ std::to_array(mo);
const MoveOnly cmo[3];
- std::to_array(std::move(cmo)); // { dg-error "here" }
-}
+ std::to_array(std::move(cmo));
+} // { dg-error "here" }
// { dg-prune-output "static assertion failed" }
@@ -25,6 +25,6 @@ test01()
{
int a[4];
std::span<int, 4> s(a);
- s.first<5>(); // { dg-error "here" }
-}
+ s.first<5>();
+} // { dg-error "here" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
@@ -25,6 +25,6 @@ test01()
{
int a[2];
std::span<int, 2> s(a);
- s.last<3>(); // { dg-error "here" }
-}
+ s.last<3>();
+} // { dg-error "here" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
@@ -25,23 +25,23 @@ test01()
{
int a[4];
std::span<int, 4> s(a);
- s.subspan<5, 0>(); // { dg-error "here" }
-}
+ s.subspan<5, 0>();
+} // { dg-error "here" }
void
test02()
{
int a[4];
std::span<int, 4> s(a);
- s.subspan<3, 5>(); // { dg-error "here" }
-}
+ s.subspan<3, 5>();
+} // { dg-error "here" }
void
test03()
{
int a[4];
std::span<int, 4> s(a);
- s.subspan<3, 2>(); // { dg-error "here" }
-}
+ s.subspan<3, 2>();
+} // { dg-error "here" }
// { dg-error "static assertion failed" "" { target *-*-* } 0 }