From: Marek Polacek <polacek@redhat.com>
To: GCC Patches <gcc-patches@gcc.gnu.org>, Jason Merrill <jason@redhat.com>
Subject: [PATCH] c++: implement P2564, consteval needs to propagate up [PR107687]
Date: Wed, 23 Aug 2023 15:49:04 -0400 [thread overview]
Message-ID: <20230823194904.1925591-1-polacek@redhat.com> (raw)
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:
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. 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. But I realized that we don't
gimplify e.g.
static auto p = &id;
and so we'd never detect taking the address of a consteval function.
Therefore this patch instantiates immediate-escalating functions
beforehand. And as usual, there were a lot of other things to
handle. It's not just calls to consteval functions that we must
detect, we also have to look for id-expressions that denote an
immediate function.
I discovered two crashes:
<https://gcc.gnu.org/PR110997>, ICE-on-valid with NSDMI
<https://gcc.gnu.org/PR111106>, missing ; causes an ICE
which this patch doesn't address, but adds a dg-ice test for the former.
I left one FIXME in the patch because I'm unclear on how to properly fix
the modules problem.
PR c++/107687
gcc/c-family/ChangeLog:
* c-cppbuiltin.cc (c_cpp_builtins): Update __cpp_consteval.
gcc/cp/ChangeLog:
* call.cc (immediate_invocation_p): No longer static.
(immediate_escalating_function_p): New.
(maybe_promote_function_to_consteval): New.
(build_over_call): Set ADDR_EXPR_DENOTES_CALL_P. Maybe promote
current_function_decl to consteval.
* constexpr.cc (instantiate_cx_fn_r): No longer static.
* cp-gimplify.cc (struct find_escalating_expr_t): New.
(find_escalating_expr_r): New.
(maybe_explain_promoted_consteval): New.
(maybe_escalate_decl_and_cfun): New.
(cp_fold_r) <case PTRMEM_CST>: Handle promoting functions to consteval.
<case CALL_EXPR>: New case, handle promoting functions to consteval.
<case ADDR_EXPR>: Handle promoting functions to consteval.
* cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define.
(immediate_invocation_p): Declare.
(immediate_escalating_function_p): Declare.
(maybe_promote_function_to_consteval): Declare.
(instantiate_constexpr_fns): Declare.
* typeck.cc (cp_build_addr_expr_1): SET_EXPR_LOCATION on constexpr
functions as well.
libstdc++-v3/ChangeLog:
* testsuite/20_util/integer_comparisons/greater_equal_neg.cc: Adjust
expected diagnostic.
* testsuite/20_util/integer_comparisons/greater_neg.cc: Likewise.
* testsuite/20_util/integer_comparisons/less_equal_neg.cc: Likewise.
* testsuite/20_util/optional/monadic/or_else_neg.cc: Likewise.
* testsuite/23_containers/array/creation/3_neg.cc: Likewise.
* testsuite/23_containers/span/first_neg.cc: Likewise.
* testsuite/23_containers/span/last_neg.cc: Likewise.
* testsuite/23_containers/span/subspan_neg.cc: Likewise.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/constexpr-inst1.C: Add dg-error.
* 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/feat-cxx2a.C: Likewise.
* 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-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/cp/call.cc | 74 +++++-
gcc/cp/constexpr.cc | 2 +-
gcc/cp/cp-gimplify.cc | 223 ++++++++++++++++--
gcc/cp/cp-tree.h | 9 +
gcc/cp/typeck.cc | 4 +-
gcc/testsuite/g++.dg/cpp0x/constexpr-inst1.C | 5 +-
gcc/testsuite/g++.dg/cpp23/consteval-if10.C | 7 +-
gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 10 +-
gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +-
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 4 +-
gcc/testsuite/g++.dg/cpp2a/consteval-prop1.C | 149 ++++++++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop10.C | 9 +
gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C | 48 ++++
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 | 72 ++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop15.C | 81 +++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C | 73 ++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop17.C | 32 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop2.C | 54 +++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop3.C | 31 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C | 32 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C | 27 +++
gcc/testsuite/g++.dg/cpp2a/consteval-prop6.C | 58 +++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C | 76 ++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop8.C | 71 ++++++
gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C | 67 ++++++
gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C | 4 +-
.../integer_comparisons/greater_equal_neg.cc | 24 +-
.../integer_comparisons/greater_neg.cc | 24 +-
.../integer_comparisons/less_equal_neg.cc | 24 +-
.../20_util/optional/monadic/or_else_neg.cc | 10 +-
.../23_containers/array/creation/3_neg.cc | 14 +-
.../testsuite/23_containers/span/first_neg.cc | 4 +-
.../testsuite/23_containers/span/last_neg.cc | 4 +-
.../23_containers/span/subspan_neg.cc | 12 +-
37 files changed, 1306 insertions(+), 91 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-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
diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index f2b12fd63db..8f8ebc7e4bb 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -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");
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. */
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);
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index da2c3116810..d85fb514015 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -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;
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
@@ -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;
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)
+
/* 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);
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))))
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. :-(
// { 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); }
diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
index 4c0523fe1d0..b8709beba85 100644
--- a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
+++ b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C
@@ -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;
diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
index d1845da9e58..024ac2b2124 100644
--- a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
+++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C
@@ -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" }
}
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index 9e29b01adc1..2b21bd1bc0d 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index 0977d964fe0..b1b9be2d24a 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -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
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
+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;
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" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
new file mode 100644
index 00000000000..b1b7a6acacb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop11.C
@@ -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 "" } */
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
new file mode 100644
index 00000000000..2949ab83af8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop12.C
@@ -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>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
new file mode 100644
index 00000000000..6c20b98a87c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop13.C
@@ -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>;
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
new file mode 100644
index 00000000000..9755e226b17
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop14.C
@@ -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);
+}
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
+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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
new file mode 100644
index 00000000000..7952d495d8b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop16.C
@@ -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);
+}
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" }
+}
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
+}
+
+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
+}
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?
+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-prop4.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
new file mode 100644
index 00000000000..fb0129794b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop4.C
@@ -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));
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
new file mode 100644
index 00000000000..3bd1b9d1674
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop5.C
@@ -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);
+}
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(){}
+
+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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
new file mode 100644
index 00000000000..712b0baef1c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop7.C
@@ -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" }
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.
+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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
new file mode 100644
index 00000000000..9c4a23389ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/consteval-prop9.C
@@ -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>;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 16bc0b85395..fc268d44e1a 100644
--- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -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
diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc
index 1a6bc52e149..2f265a213b4 100644
--- a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_equal_neg.cc
@@ -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 }
diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc
index d7097750307..e21c0ef0ef4 100644
--- a/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/greater_neg.cc
@@ -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 }
diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc
index 3d83f2ee607..881e8cf57f4 100644
--- a/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/less_equal_neg.cc
@@ -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 }
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" }
// { dg-prune-output "static assertion failed" }
diff --git a/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc b/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc
index eeabdcece14..a47499245dc 100644
--- a/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc
@@ -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" }
diff --git a/libstdc++-v3/testsuite/23_containers/span/first_neg.cc b/libstdc++-v3/testsuite/23_containers/span/first_neg.cc
index 8ed68296263..d611638cd28 100644
--- a/libstdc++-v3/testsuite/23_containers/span/first_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/span/first_neg.cc
@@ -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 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
index 9c360d2650e..d133013a9ef 100644
--- a/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/span/last_neg.cc
@@ -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 }
diff --git a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
index 205bafd39dd..2740d198e0e 100644
--- a/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/span/subspan_neg.cc
@@ -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 }
base-commit: 0cfc9c953d0221ec3971a25e6509ebe1041f142e
--
2.41.0
next reply other threads:[~2023-08-23 19:49 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-23 19:49 Marek Polacek [this message]
2023-08-29 19:26 ` Jason Merrill
2023-10-10 17:20 ` [PATCH v2] " Marek Polacek
2023-10-11 20:42 ` Marek Polacek
2023-10-14 4:56 ` Jason Merrill
2023-11-02 15:28 ` [PATCH v3] " Marek Polacek
2023-11-02 15:32 ` Marek Polacek
2023-11-03 17:51 ` Jason Merrill
2023-11-06 22:34 ` [PATCH v4] " Marek Polacek
2023-11-14 2:06 ` Jason Merrill
2023-11-23 16:46 ` [PATCH v5] " Marek Polacek
2023-11-30 23:34 ` Jason Merrill
2023-12-01 23:37 ` [PATCH v6] " Marek Polacek
2023-12-02 0:43 ` Jason Merrill
2023-12-04 20:23 ` [PATCH v7] " Marek Polacek
2023-12-04 21:49 ` Jason Merrill
2023-12-05 0:44 ` [PATCH v8] " Marek Polacek
2023-12-06 11:39 ` Prathamesh Kulkarni
2023-12-06 14:34 ` Marek Polacek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230823194904.1925591-1-polacek@redhat.com \
--to=polacek@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).