void
explain_invalid_constexpr_fn (tree fun)
{
static hash_set<tree> *diagnosed;
tree body;
+ /* In C++23, a function marked 'constexpr' may not actually be a constant
+ expression. We haven't diagnosed the problem yet: -Winvalid-constexpr
+ wasn't enabled. The function was called, so diagnose why it cannot be
+ used in a constant expression. */
+ if (warn_invalid_constexpr == 0 && DECL_DECLARED_CONSTEXPR_P (fun))
+ /* Go on. */;
/* Only diagnose defaulted functions, lambdas, or instantiations. */
- if (!DECL_DEFAULTED_FN (fun)
- && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
- && !is_instantiation_of_constexpr (fun))
+ else if (!DECL_DEFAULTED_FN (fun)
+ && !LAMBDA_TYPE_P (CP_DECL_CONTEXT (fun))
+ && !is_instantiation_of_constexpr (fun))
{
inform (DECL_SOURCE_LOCATION (fun), "%qD declared here", fun);
return;
@@ -5612,11 +5663,12 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
}
/* Complain about R, a VAR_DECL, not being usable in a constant expression.
+ FUNDEF_P is true if we're checking a constexpr function body.
Shared between potential_constant_expression and
cxx_eval_constant_expression. */
static void
-non_const_var_error (location_t loc, tree r)
+non_const_var_error (location_t loc, tree r, bool fundef_p)
{
auto_diagnostic_group d;
tree type = TREE_TYPE (r);
@@ -5625,20 +5677,21 @@ non_const_var_error (location_t loc, tree r)
|| DECL_NAME (r) == heap_vec_uninit_identifier
|| DECL_NAME (r) == heap_vec_identifier)
{
- error_at (loc, "the content of uninitialized storage is not usable "
- "in a constant expression");
- inform (DECL_SOURCE_LOCATION (r), "allocated here");
+ if (constexpr_error (loc, fundef_p, "the content of uninitialized "
+ "storage is not usable in a constant expression"))
+ inform (DECL_SOURCE_LOCATION (r), "allocated here");
return;
}
if (DECL_NAME (r) == heap_deleted_identifier)
{
- error_at (loc, "use of allocated storage after deallocation in a "
- "constant expression");
- inform (DECL_SOURCE_LOCATION (r), "allocated here");
+ if (constexpr_error (loc, fundef_p, "use of allocated storage after "
+ "deallocation in a constant expression"))
+ inform (DECL_SOURCE_LOCATION (r), "allocated here");
return;
}
- error_at (loc, "the value of %qD is not usable in a constant "
- "expression", r);
+ if (!constexpr_error (loc, fundef_p, "the value of %qD is not usable in "
+ "a constant expression", r))
+ return;
/* Avoid error cascade. */
if (DECL_INITIAL (r) == error_mark_node)
return;
@@ -6697,15 +6750,17 @@ lookup_placeholder (const constexpr_ctx *ctx, value_cat
lval, tree type)
return ob;
}
-/* Complain about an attempt to evaluate inline assembly. */
+/* Complain about an attempt to evaluate inline assembly. If FUNDEF_P is
+ true, we're checking a constexpr function body. */
static void
-inline_asm_in_constexpr_error (location_t loc)
+inline_asm_in_constexpr_error (location_t loc, bool fundef_p)
{
auto_diagnostic_group d;
- error_at (loc, "inline assembly is not a constant expression");
- inform (loc, "only unevaluated inline assembly is allowed in a "
- "%<constexpr%> function in C++20");
+ if (constexpr_error (loc, fundef_p, "inline assembly is not a "
+ "constant expression"))
+ inform (loc, "only unevaluated inline assembly is allowed in a "
+ "%<constexpr%> function in C++20");
}
/* We're getting the constant value of DECL in a manifestly constant-evaluated
@@ -6983,7 +7038,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx,
tree t,
if (DECL_P (r))
{
if (!ctx->quiet)
- non_const_var_error (loc, r);
+ non_const_var_error (loc, r, /*fundef_p*/false);
*non_constant_p = true;
}
break;
@@ -7874,7 +7929,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx,
tree t,
case ASM_EXPR:
if (!ctx->quiet)
- inline_asm_in_constexpr_error (loc);
+ inline_asm_in_constexpr_error (loc, /*constexpr_fundef_p*/false);
*non_constant_p = true;
return t;
@@ -8759,7 +8814,8 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
diagnostic as appropriate under control of FLAGS. If WANT_RVAL is true,
an lvalue-rvalue conversion is implied. If NOW is true, we want to
consider the expression in the current context, independent of constexpr
- substitution.
+ substitution. If FUNDEF_P is true, we're checking a constexpr function body
+ and hard errors should not be reported by constexpr_error.
C++0x [expr.const] used to say
@@ -8776,10 +8832,12 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
static bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool
now,
- tsubst_flags_t flags, tree *jump_target)
+ bool fundef_p, tsubst_flags_t flags,
+ tree *jump_target)
{
#define RECUR(T,RV) \
- potential_constant_expression_1 ((T), (RV), strict, now, flags, jump_target)
+ potential_constant_expression_1 ((T), (RV), strict, now, fundef_p, flags, \
+ jump_target)
enum { any = false, rval = true };
int i;
@@ -8801,8 +8859,9 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (TREE_THIS_VOLATILE (t) && want_rval)
{
if (flags & tf_error)
- error_at (loc, "lvalue-to-rvalue conversion of a volatile lvalue "
- "%qE with type %qT", t, TREE_TYPE (t));
+ constexpr_error (loc, fundef_p, "lvalue-to-rvalue conversion of "
+ "a volatile lvalue %qE with type %qT", t,
+ TREE_TYPE (t));
return false;
}
if (CONSTANT_CLASS_P (t))
@@ -8861,7 +8920,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
/* An empty class has no data to read. */
return true;
if (flags & tf_error)
- error ("%qE is not a constant expression", t);
+ constexpr_error (input_location, fundef_p,
+ "%qE is not a constant expression", t);
return false;
}
return true;
@@ -8910,7 +8970,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
{
/* fold_call_expr can't do anything with IFN calls. */
if (flags & tf_error)
- error_at (loc, "call to internal function %qE", t);
+ constexpr_error (loc, fundef_p,
+ "call to internal function %qE", t);
return false;
}
}
@@ -8940,12 +9001,11 @@ potential_constant_expression_1 (tree t, bool
want_rval, bool strict, bool now,
|| !is_std_construct_at (current_function_decl))
&& !cxx_dynamic_cast_fn_p (fun))
{
- if (flags & tf_error)
- {
- error_at (loc, "call to non-%<constexpr%> function %qD",
- fun);
- explain_invalid_constexpr_fn (fun);
- }
+ if ((flags & tf_error)
+ && constexpr_error (loc, fundef_p,
+ "call to non-%<constexpr%> "
+ "function %qD", fun))
+ explain_invalid_constexpr_fn (fun);
return false;
}
/* A call to a non-static member function takes the address
@@ -8962,8 +9022,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
constexpr substitution might not use the value. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rval, strict,
- sub_now, flags,
- jump_target))
+ sub_now, fundef_p,
+ flags, jump_target))
return false;
i = 1;
}
@@ -8997,7 +9057,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
substitution might not use the value of the argument. */
bool sub_now = false;
if (!potential_constant_expression_1 (x, rv, strict,
- sub_now, flags, jump_target))
+ sub_now, fundef_p, flags,
+ jump_target))
return false;
}
return true;
@@ -9035,9 +9096,10 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (flags & tf_error)
{
tree cap = DECL_CAPTURED_VARIABLE (t);
- error ("lambda capture of %qE is not a constant expression",
- cap);
- if (decl_constant_var_p (cap))
+ if (constexpr_error (input_location, fundef_p,
+ "lambda capture of %qE is not a "
+ "constant expression", cap)
+ && decl_constant_var_p (cap))
inform (input_location, "because it is used as a glvalue");
}
return false;
@@ -9060,8 +9122,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
&& COMPLETE_TYPE_P (TREE_TYPE (t))
&& !is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
{
- if (flags & tf_error)
- non_const_var_error (loc, t);
+ if (flags & tf_error)
+ non_const_var_error (loc, t, fundef_p);
return false;
}
return true;
@@ -9070,7 +9132,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (REINTERPRET_CAST_P (t))
{
if (flags & tf_error)
- error_at (loc, "%<reinterpret_cast%> is not a constant expression");
+ constexpr_error (loc, fundef_p, "%<reinterpret_cast%> is not a "
+ "constant expression");
return false;
}
/* FALLTHRU */
@@ -9092,8 +9155,9 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
&& !integer_zerop (from))
{
if (flags & tf_error)
- error_at (loc,
- "%<reinterpret_cast%> from integer to pointer");
+ constexpr_error (loc, fundef_p,
+ "%<reinterpret_cast%> from integer to "
+ "pointer");
return false;
}
}
@@ -9165,7 +9229,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (!var_in_maybe_constexpr_fn (x))
{
if (flags & tf_error)
- error_at (loc, "use of %<this%> in a constant expression");
+ constexpr_error (loc, fundef_p, "use of %<this%> in a "
+ "constant expression");
return false;
}
return true;
@@ -9313,8 +9378,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
/* In C++17 lambdas can be constexpr, don't give up yet. */
return true;
else if (flags & tf_error)
- error_at (loc, "lambda-expression is not a constant expression "
- "before C++17");
+ constexpr_error (loc, fundef_p, "lambda-expression is not a "
+ "constant expression before C++17");
return false;
case NEW_EXPR:
@@ -9325,8 +9390,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
/* In C++20, new-expressions are potentially constant. */
return true;
else if (flags & tf_error)
- error_at (loc, "new-expression is not a constant expression "
- "before C++20");
+ constexpr_error (loc, fundef_p, "new-expression is not a "
+ "constant expression before C++20");
return false;
case DYNAMIC_CAST_EXPR:
@@ -9375,12 +9440,13 @@ potential_constant_expression_1 (tree t, bool
want_rval, bool strict, bool now,
case AT_ENCODE_EXPR:
fail:
if (flags & tf_error)
- error_at (loc, "expression %qE is not a constant expression", t);
+ constexpr_error (loc, fundef_p, "expression %qE is not a constant "
+ "expression", t);
return false;
case ASM_EXPR:
if (flags & tf_error)
- inline_asm_in_constexpr_error (loc);
+ inline_asm_in_constexpr_error (loc, fundef_p);
return false;
case OBJ_TYPE_REF:
@@ -9388,8 +9454,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
/* In C++20 virtual calls can be constexpr, don't give up yet. */
return true;
else if (flags & tf_error)
- error_at (loc,
- "virtual functions cannot be %<constexpr%> before C++20");
+ constexpr_error (loc, fundef_p, "virtual functions cannot be "
+ "%<constexpr%> before C++20");
return false;
case TYPEID_EXPR:
@@ -9404,8 +9470,9 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
&& TYPE_POLYMORPHIC_P (TREE_TYPE (e)))
{
if (flags & tf_error)
- error_at (loc, "%<typeid%> is not a constant expression "
- "because %qE is of polymorphic type", e);
+ constexpr_error (loc, fundef_p, "%<typeid%> is not a "
+ "constant expression because %qE is "
+ "of polymorphic type", e);
return false;
}
return true;
@@ -9465,9 +9532,9 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
constant expression. */
{
if (flags & tf_error)
- error_at (loc,
- "cast to non-integral type %qT in a constant expression",
- TREE_TYPE (t));
+ constexpr_error (loc, fundef_p,
+ "cast to non-integral type %qT in a constant "
+ "expression", TREE_TYPE (t));
return false;
}
/* This might be a conversion from a class to a (potentially) literal
@@ -9523,15 +9590,17 @@ potential_constant_expression_1 (tree t, bool
want_rval, bool strict, bool now,
if (CP_DECL_THREAD_LOCAL_P (tmp) && !DECL_REALLY_EXTERN (tmp))
{
if (flags & tf_error)
- error_at (DECL_SOURCE_LOCATION (tmp), "%qD defined "
- "%<thread_local%> in %<constexpr%> context", tmp);
+ constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p,
+ "%qD defined %<thread_local%> in "
+ "%<constexpr%> context", tmp);
return false;
}
else if (TREE_STATIC (tmp))
{
if (flags & tf_error)
- error_at (DECL_SOURCE_LOCATION (tmp), "%qD defined "
- "%<static%> in %<constexpr%> context", tmp);
+ constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p,
+ "%qD defined %<static%> in %<constexpr%> "
+ "context", tmp);
return false;
}
else if (!check_for_uninitialized_const_var
@@ -9554,9 +9623,10 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (flags & tf_error)
{
auto_diagnostic_group d;
- error_at (loc, "temporary of non-literal type %qT in a "
- "constant expression", TREE_TYPE (t));
- explain_non_literal_class (TREE_TYPE (t));
+ if (constexpr_error (loc, fundef_p,
+ "temporary of non-literal type %qT in a "
+ "constant expression", TREE_TYPE (t)))
+ explain_non_literal_class (TREE_TYPE (t));
}
return false;
}
@@ -9603,7 +9673,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (integer_zerop (denom))
{
if (flags & tf_error)
- error ("division by zero is not a constant expression");
+ constexpr_error (input_location, fundef_p,
+ "division by zero is not a constant expression");
return false;
}
else
@@ -9704,7 +9775,8 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (COND_EXPR_IS_VEC_DELETE (t) && cxx_dialect < cxx20)
{
if (flags & tf_error)
- error_at (loc, "%<delete[]%> is not a constant expression");
+ constexpr_error (loc, fundef_p, "%<delete[]%> is not a "
+ "constant expression");
return false;
}
/* Fall through. */
@@ -9734,7 +9806,7 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
{
tree this_jump_target = tmp;
if (potential_constant_expression_1 (TREE_OPERAND (t, i),
- want_rval, strict, now,
+ want_rval, strict, now, fundef_p,
tf_none, &this_jump_target))
{
if (returns (&this_jump_target))
@@ -9772,9 +9844,11 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
if (flags & tf_error)
{
if (TREE_CODE (t) == IF_STMT)
- error_at (loc, "neither branch of %<if%> is a constant expression");
+ constexpr_error (loc, fundef_p, "neither branch of %<if%> is a "
+ "constant expression");
else
- error_at (loc, "expression %qE is not a constant expression", t);
+ constexpr_error (loc, fundef_p, "expression %qE is not a "
+ "constant expression", t);
}
return false;
@@ -9783,8 +9857,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return true;
if (flags & tf_error)
{
- error_at (loc, "non-constant array initialization");
- diagnose_non_constexpr_vec_init (t);
+ if (constexpr_error (loc, fundef_p, "non-constant array "
+ "initialization"))
+ diagnose_non_constexpr_vec_init (t);
}
return false;
@@ -9813,7 +9888,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return true;
}
if (flags & tf_error)
- error_at (loc, "%<goto%> is not a constant expression");
+ constexpr_error (loc, fundef_p, "%<goto%> is not a constant "
+ "expression");
return false;
}
@@ -9822,8 +9898,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
return true;
else if (flags & tf_error)
- error_at (loc, "label definition in %<constexpr%> function only "
- "available with %<-std=c++2b%> or %<-std=gnu++2b%>");
+ constexpr_error (loc, fundef_p, "label definition in %<constexpr%> "
+ "function only available with %<-std=c++2b%> or "
+ "%<-std=gnu++2b%>");
return false;
case ANNOTATE_EXPR:
@@ -9861,7 +9938,7 @@ potential_constant_expression_1 (tree t, bool want_rval,
bool strict, bool now,
bool
potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool
now,
- tsubst_flags_t flags)
+ bool fundef_p, tsubst_flags_t flags)
{
if (flags & tf_error)
{
@@ -9869,13 +9946,14 @@ potential_constant_expression_1 (tree t, bool
want_rval, bool strict, bool now,
efficiently in some cases (currently only for TRUTH_*_EXPR). If
that fails, replay the check noisily to give errors. */
flags &= ~tf_error;
- if (potential_constant_expression_1 (t, want_rval, strict, now, flags))
+ if (potential_constant_expression_1 (t, want_rval, strict, now, fundef_p,
+ flags))
return true;
flags |= tf_error;
}
tree target = NULL_TREE;
- return potential_constant_expression_1 (t, want_rval, strict, now,
+ return potential_constant_expression_1 (t, want_rval, strict, now, fundef_p,
flags, &target);
}
@@ -9884,7 +9962,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
bool
potential_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, false, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/false,
/*strict*/true,
+ /*now*/false, /*fundef_p*/false,
+ tf_none);
}
/* As above, but require a constant rvalue. */
@@ -9892,7 +9972,9 @@ potential_constant_expression (tree t)
bool
potential_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, false, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/false, /*fundef_p*/false,
+ tf_none);
}
/* Like above, but complain about non-constant expressions. */
@@ -9900,7 +9982,8 @@ potential_rvalue_constant_expression (tree t)
bool
require_potential_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, false,
+ return potential_constant_expression_1 (t, /*want_rval*/false,
/*strict*/true,
+ /*now*/false, /*fundef_p*/false,
tf_warning_or_error);
}
@@ -9909,7 +9992,18 @@ require_potential_constant_expression (tree t)
bool
require_potential_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, false,
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/false, /*fundef_p*/false,
+ tf_warning_or_error);
+}
+
+/* Like require_potential_rvalue_constant_expression, but fundef_p is true. */
+
+bool
+require_potential_rvalue_constant_expression_fncheck (tree t)
+{
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/false, /*fundef_p*/true,
tf_warning_or_error);
}
@@ -9918,7 +10012,8 @@ require_potential_rvalue_constant_expression (tree t)
bool
require_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, true,
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/true, /*fundef_p*/false,
tf_warning_or_error);
}
@@ -9932,7 +10027,9 @@ require_rvalue_constant_expression (tree t)
bool
is_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, true, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/false,
/*strict*/true,
+ /*now*/true, /*fundef_p*/false,
+ tf_none);
}
/* As above, but expect an rvalue. */
@@ -9940,7 +10037,9 @@ is_constant_expression (tree t)
bool
is_rvalue_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, true, true, true, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/true, /*strict*/true,
+ /*now*/true, /*fundef_p*/false,
+ tf_none);
}
/* Like above, but complain about non-constant expressions. */
@@ -9948,7 +10047,8 @@ is_rvalue_constant_expression (tree t)
bool
require_constant_expression (tree t)
{
- return potential_constant_expression_1 (t, false, true, true,
+ return potential_constant_expression_1 (t, /*want_rval*/false,
/*strict*/true,
+ /*now*/true, /*fundef_p*/false,
tf_warning_or_error);
}
@@ -9958,7 +10058,9 @@ require_constant_expression (tree t)
bool
is_static_init_expression (tree t)
{
- return potential_constant_expression_1 (t, false, false, true, tf_none);
+ return potential_constant_expression_1 (t, /*want_rval*/false,
+ /*strict*/false, /*now*/true,
+ /*fundef_p*/false, tf_none);
}
/* Returns true if T is a potential constant expression that is not
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8c9beb86568..12f1e410355 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8450,6 +8450,7 @@ extern bool require_potential_constant_expression (tree);
extern bool require_constant_expression (tree);
extern bool require_rvalue_constant_expression (tree);
extern bool require_potential_rvalue_constant_expression (tree);
+extern bool require_potential_rvalue_constant_expression_fncheck (tree);
extern tree cxx_constant_value (tree, tree = NULL_TREE,
tsubst_flags_t = tf_error);
inline tree cxx_constant_value (tree t, tsubst_flags_t complain)
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index c217d7e5aad..1e962b6e3b1 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1332,7 +1332,7 @@ struct comp_info
&& !potential_rvalue_constant_expression (expr))
{
if (was_constexp)
- require_potential_rvalue_constant_expression (expr);
+ require_potential_rvalue_constant_expression_fncheck (expr);
else
constexp = false;
}
@@ -2670,13 +2670,17 @@ synthesized_method_walk (tree ctype,
special_function_kind sfk, bool const_p,
requirements of a constexpr constructor (7.1.5), the
implicitly-defined default constructor is constexpr.
+ C++20:
The implicitly-defined copy/move assignment operator is constexpr if
- X is a literal type, and
- the assignment operator selected to copy/move each direct base class
subobject is a constexpr function, and
- for each non-static data member of X that is of class type (or array
thereof), the assignment operator selected to copy/move that
- member is a constexpr function. */
+ member is a constexpr function.
+
+ C++23:
+ The implicitly-defined copy/move assignment operator is constexpr. */
if (constexpr_p)
*constexpr_p = (SFK_CTOR_P (sfk)
|| (SFK_ASSIGN_P (sfk) && cxx_dialect >= cxx14)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 057439a004c..271918a237f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -256,7 +256,7 @@ in the following sections.
-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion
@gol
-Weffc++ -Wno-exceptions -Wextra-semi -Wno-inaccessible-base @gol
-Wno-inherited-variadic-ctor -Wno-init-list-lifetime @gol
--Winvalid-imported-macros @gol
+-Winvalid-constexpr -Winvalid-imported-macros @gol
-Wno-invalid-offsetof -Wno-literal-suffix @gol
-Wmismatched-new-delete -Wmismatched-tags @gol
-Wmultiple-inheritance -Wnamespaces -Wnarrowing @gol
@@ -3766,6 +3766,32 @@ the variable declaration statement.
@end itemize
+@item -Winvalid-constexpr
+@opindex Winvalid-constexpr
+@opindex Wno-invalid-constexpr
+
+Warn when a function never produces a constant expression. In C++20
+and earlier, for every @code{constexpr} function and function template,
+there must be at least one set of function arguments in at least one
+instantiation such that an invocation of the function or constructor
+could be an evaluated subexpression of a core constant expression.
+C++23 removed this restriction, so it's possible to have a function
+or a function template marked @code{constexpr} for which no invocation
+satisfies the requirements of a core constant expression.
+
+This warning is enabled as a pedantic warning by default in C++20 and
+earlier. In C++23, @option{-Winvalid-constexpr} can be turned on, in
+which case it will be an ordinary warning. For example:
+
+@smallexample
+void f (int& i);
+constexpr void
+g (int& i)
+@{
+ f(i); // warns by default in C++20, in C++23 only with -Winvalid-constexpr
+@}
+@end smallexample
+
@item -Winvalid-imported-macros
@opindex Winvalid-imported-macros
@opindex Wno-invalid-imported-macros
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
index 30b01091fd1..eabc586385f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C
@@ -7,5 +7,5 @@ struct A
struct B : A
{
- constexpr B(): A() { } // { dg-error "A::A" }
+ constexpr B(): A() { } // { dg-error "A::A" "" { target c++20_down } }
};
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
index 8d352d0bb99..2f9fbfb596a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-default-ctor.C
@@ -7,6 +7,6 @@ struct A {
struct B: A { };
constexpr int f(B b) { return b.i; }
-struct C { C(); }; // { dg-message "" }
-struct D: C { }; // { dg-message "" }
-constexpr int g(D d) { return 42; } // { dg-error "invalid type" }
+struct C { C(); }; // { dg-message "" "" { target c++20_down } }
+struct D: C { }; // { dg-message "" "" { target c++20_down } }
+constexpr int g(D d) { return 42; } // { dg-error "invalid type" "" { target
c++20_down } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index c167bb1d8bc..5eedf42ba36 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -37,7 +37,7 @@ struct base // { dg-message "no .constexpr.
constructor" "" { target { !
struct derived : public base // { dg-message "base class" "" { target { ! implicit_constexpr } } }
{
- constexpr derived(): base() { } // { dg-error "non-.constexpr. function" ""
{ target { ! implicit_constexpr } } }
+ constexpr derived(): base() { } // { dg-error "non-.constexpr. function" "" { target
{ { ! implicit_constexpr } && c++20_down } } }
};
constexpr derived obj; // { dg-error "not literal" "" { target { ! implicit_constexpr } } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
index 1d5c58b4090..48281a47784 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C
@@ -87,7 +87,8 @@ struct resource {
}
};
constexpr resource f(resource d)
-{ return d; } // { dg-error "non-.constexpr." "" { target { !
implicit_constexpr } } }
+{ return d; } // { dg-error "non-.constexpr." "" { target { { !
implicit_constexpr } && c++20_down } } }
+// { dg-error "non-.constexpr." "" { target c++23 } .-2 }
constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { !
implicit_constexpr } } }
// 4.4 floating-point constant expressions
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
index 85dfca4ff1d..3d171822855 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C
@@ -5,7 +5,7 @@ struct A { A(); };
struct B {
friend constexpr int f(B) { return 0; } // OK
- friend constexpr int f(A) { return 0; } // { dg-error "constexpr" }
+ friend constexpr int f(A) { return 0; } // { dg-error "constexpr" "" {
target c++20_down } }
};
template <class T>
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
index 4b0d68bf661..98235719546 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-generated1.C
@@ -9,7 +9,7 @@ int g();
// We should complain about this.
template<> constexpr int A<int>::f()
-{ return g(); } // { dg-error "non-.constexpr." }
+{ return g(); } // { dg-error "non-.constexpr." "" {
target c++20_down } }
// But not about this.
struct B
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
index e934421c2f4..70327fc414a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C
@@ -9,5 +9,5 @@ struct A
struct B
{
A a[1];
- constexpr B() : a() {} // { dg-error "non-constant|non-.constexpr." "" {
target { ! implicit_constexpr } } }
+ constexpr B() : a() {} // { dg-error "non-constant|non-.constexpr." "" { target { {
! implicit_constexpr } && c++20_down } } }
};
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
index bf95b2443c7..7eabd333758 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C
@@ -6,6 +6,6 @@ struct A
A(int);
};
-struct B : A {}; // { dg-message "" }
+struct B : A {}; // { dg-message "" "" { target c++20_down }
}
-constexpr int foo(B) { return 0; } // { dg-error "invalid type" }
+constexpr int foo(B) { return 0; } // { dg-error "invalid type" "" { target
c++20_down } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
index 37255282ded..0c95961c730 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C
@@ -13,6 +13,6 @@ constexpr X X::g(X x) { return x; }
struct Y
{
Y() { }
- constexpr Y f(Y y) { return y; } // { dg-error "constexpr" "" { target { !
implicit_constexpr } } }
- static constexpr Y g(Y y) { return y; } // { dg-error "constexpr" "" {
target { ! implicit_constexpr } } }
+ constexpr Y f(Y y) { return y; } // { dg-error "constexpr" "" { target { { !
implicit_constexpr } && c++20_down } } }
+ static constexpr Y g(Y y) { return y; } // { dg-error "constexpr" "" { target { { !
implicit_constexpr } && c++20_down } } }
};
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
index 793b4c3f5d3..47f7fb05e29 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C
@@ -18,10 +18,10 @@ constexpr int three = one() ? 3 : nonconst_func(0);
constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error
"nonconst_func" }
// Correctly rejected (not sure why).
-constexpr int correct_error() { return nonconst_func(0); } // { dg-error
"nonconst_func" }
+constexpr int correct_error() { return nonconst_func(0); } // { dg-error "nonconst_func"
"" { target c++20_down } }
// Correctly rejected.
constexpr int z = bogus(); // { dg-error "" }
// This is also correctly rejected.
-constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error
"nonconst_func" }
+constexpr int correct_failure() { return 0 ? 3 : nonconst_func(0); } // { dg-error
"nonconst_func" "" { target c++20_down } }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
index 0f68643f145..abbc70368d4 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-non-const-arg.C
@@ -10,7 +10,7 @@ struct B {
int global; // { dg-message "not const" }
struct D : B {
- constexpr D() : B(global) { } // { dg-error "global|argument" }
+ constexpr D() : B(global) { } // { dg-error "global|argument" "" { target
c++20_down } }
};
struct A2 {
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
index d7d244f752d..4e19cd36a9f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
@@ -17,7 +17,7 @@ public:
constexpr static Inner & getInner()
/* I am surprised this is considered a constexpr */
{
- return *((Inner *)4); // { dg-error "reinterpret_cast" }
+ return *((Inner *)4); // { dg-error "reinterpret_cast" "" { target
c++20_down } }
}
};
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr65327.C b/gcc/testsuite/g++.dg/cpp0x/pr65327.C
index e8149953ffd..b3ef57eec5f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/pr65327.C
+++ b/gcc/testsuite/g++.dg/cpp0x/pr65327.C
@@ -14,5 +14,5 @@ foo ()
constexpr volatile int // { dg-warning "deprecated" "" { target c++2a } }
bar ()
{
- return i; // { dg-error "lvalue-to-rvalue conversion of a volatile lvalue" }
+ return i; // { dg-error "lvalue-to-rvalue conversion of a volatile lvalue"
"" { target c++20_down } }
}
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
index e0688fbd38e..e5d53c9817b 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C
@@ -5,7 +5,7 @@ void g();
void h();
constexpr void f(int* p, int* q) {
- if (p != q && *p < 0) // { dg-error "neither branch of 'if' is a constant
expression" }
+ if (p != q && *p < 0) // { dg-error "neither branch of 'if' is a constant expression"
"" { target c++20_down } }
g();
else
h();
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
index ea44daa849e..7b129fcee7a 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285-2.C
@@ -10,7 +10,7 @@ struct B {
int *c = &x->a;
while (*c)
c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
- *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error
"reinterpret_cast" }
+ *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error
"reinterpret_cast" "" { target c++20_down } }
}
};
struct C : A {
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
index 26aab9b6a50..fe0b8570ca2 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
@@ -10,7 +10,7 @@ struct B {
int *c = &x->a;
while (*c)
c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
- *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error
"reinterpret_cast" }
+ *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c); // { dg-error
"reinterpret_cast" "" { target c++20_down } }
}
};
struct C : A {
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
index 5cd46c791af..7afd9d24e98 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
@@ -11,7 +11,7 @@ foo (int x)
case 2:
break;
}
- throw 42; // { dg-error "is not a constant expression" }
+ throw 42; // { dg-error "is not a constant expression" "" { target
c++20_down } }
return 0;
}
@@ -29,7 +29,7 @@ bar (int x)
continue;
break;
}
- throw -42; // { dg-error "is not a constant expression" }
+ throw -42; // { dg-error "is not a constant expression" "" {
target c++20_down } }
}
while (0);
return x;
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
index 8e9d1ea4943..53b5dd50f51 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C
@@ -3,7 +3,7 @@
struct A { A(); };
constexpr int f(int i) {
- static int j = i; // { dg-error "static" }
+ static int j = i; // { dg-error "static" "" { target c++20_down }
}
thread_local int l = i; // { dg-error "thread_local" "" { target
c++20_down } }
goto foo; // { dg-error "goto" "" { target c++20_down } }
foo:
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
index ec10ddd2be8..a410e482664 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-nsdmi7b.C
@@ -21,7 +21,7 @@ bar()
A a = foo();
a.p->n = 5;
return a;
-} // { dg-error "non-.constexpr." }
+} // { dg-error "non-.constexpr." "" { target c++20_down } }
constexpr int
baz()
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
b/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
index 3bbc8ac1b88..35928744686 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C
@@ -7,18 +7,18 @@ constexpr void f1() {
constexpr void f2() {
if (true)
- throw; // { dg-error "not a constant expression" }
+ throw; // { dg-error "not a constant expression" "" { target
c++20_down } }
}
constexpr void f3() {
if (false)
;
else
- throw; // { dg-error "not a constant expression" }
+ throw; // { dg-error "not a constant expression" "" { target
c++20_down } }
}
constexpr void f4() {
- throw; // { dg-error "not a constant expression" }
+ throw; // { dg-error "not a constant expression" "" { target
c++20_down } }
}
constexpr int fun(int n) {
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
new file mode 100644
index 00000000000..48706f7b66e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
@@ -0,0 +1,96 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr -pedantic-errors" }
+
+// No constexpr constructors = not a literal type.
+struct NonLiteral {
+ NonLiteral() {}
+};
+
+// C++23: It is possible to write a constexpr function for which no
+// invocation satisfies the requirements of a core constant expression.
+constexpr NonLiteral
+fn0 (int) // { dg-warning "invalid return type" }
+{
+ return NonLiteral{};
+}
+
+constexpr int
+fn1 (NonLiteral) // { dg-warning "invalid type" }
+{
+ return 42;
+}
+
+// From P2448.
+void f(int& i) {
+ i = 0;
+}
+
+constexpr void g(int& i) {
+ f(i); // { dg-warning "call to" }
+}
+
+// [dcl.constexpr] used to have this.
+constexpr int f(bool b)
+ { return b ? throw 0 : 0; } // OK
+constexpr int f() { return f(true); } // ill-formed, no diagnostic required
+
+struct B {
+ constexpr B(int) : i(0) { }
+ int i;
+};
+
+int global;
+
+struct D : B {
+ constexpr D() : B(global) { } // { dg-warning "not usable" }
+ // ill-formed, no diagnostic required
+ // lvalue-to-rvalue conversion on non-constant global
+};
+
+// If no specialization of the template would satisfy the requirements
+// for a constexpr function when considered as a non-template function,
+// the template is ill-formed, no diagnostic required.
+template<typename>
+constexpr void
+fn2 ()
+{
+ int i = 42;
+ f (i);
+}
+
+void
+fn3 ()
+{
+ fn2<int>();
+}
+
+constexpr volatile int cvi = 10;
+
+constexpr int
+fn4 ()
+{
+ return cvi; // { dg-warning "lvalue-to-rvalue conversion" }
+}
+
+constexpr unsigned int
+fn5 (int *p)
+{
+ unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning
"reinterpret_cast" }
+ return *q;
+}
+
+constexpr int
+fn6 (int i)
+{
+ void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to
pointer" }
+ return 42;
+}
+
+constexpr int
+fn7 (int i)
+{
+ static int s = i; // { dg-warning "static" }
+ return s;
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
new file mode 100644
index 00000000000..a7114bc66cb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
@@ -0,0 +1,53 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr -pedantic-errors" }
+
+// [dcl.constexpr]/4 used to say:
+// The definition of a constexpr constructor whose function-body
+// is not = delete shall additionally satisfy the following requirements:
+// (4.1) for a non-delegating constructor, every constructor selected to
initialize non-static data members and base class subobjects shall be a
constexpr constructor;
+// (4.2) for a delegating constructor, the target constructor shall be a
constexpr constructor.
+
+// This continues to be OK.
+struct Length {
+ constexpr explicit Length(int i = 0) : val(i) { }
+private:
+ int val;
+};
+
+struct X {
+ X() {}
+ X(int i_) : i(i_) {}
+ int i;
+};
+
+struct S {
+ X x;
+ // Calls a non-constexpr constructor X::X(int).
+ constexpr S(int i) : x(i) { } // { dg-warning "call to" }
+ S(int, int) { }
+ // Target constructor isn't constexpr.
+ constexpr S() : S(42, 42) { } // { dg-warning "call to" }
+};
+
+namespace N1 {
+struct X {
+ void x();
+};
+struct Y {
+ X x;
+ constexpr void y() { x.x(); } // { dg-warning "call to" }
+};
+}
+
+void g();
+
+struct A {
+ constexpr A() { g(); } // { dg-warning "call to" }
+};
+
+struct B {
+ constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call
to" }
+ constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to"
}
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
new file mode 100644
index 00000000000..8f003b80190
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
@@ -0,0 +1,24 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// Test that we get a diagnostic even in C++23 if you do call the function.
+
+constexpr unsigned int
+fn0 (const int *p)
+{
+ return *reinterpret_cast<unsigned const int *>(p); // { dg-error
".reinterpret_cast. is not a constant expression" }
+}
+
+constexpr void *
+fn1 (int i)
+{
+ return (void *) 1LL; // { dg-error ".reinterpret_cast." }
+}
+
+void
+g ()
+{
+ constexpr int i = 42;
+ constexpr auto a1 = fn0 (&i);
+ constexpr auto a2 = fn1 (i); // { dg-error "called in a constant expression"
}
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
new file mode 100644
index 00000000000..7997e8e2c3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
@@ -0,0 +1,14 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr" }
+
+constexpr volatile int i = 10;
+
+constexpr int
+bar ()
+{
+ return i; // { dg-warning "lvalue-to-rvalue conversion of a volatile
lvalue" }
+}
+
+constexpr int x = bar (); // { dg-error "called in a constant expression" }
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
new file mode 100644
index 00000000000..f79ff15cbe2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
@@ -0,0 +1,26 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+// The definition of a constexpr destructor whose function-body is not
+// =delete shall additionally satisfy the following requirement:
+// (5.1) for every subobject of class type or (possibly multi-dimensional)
+// array thereof, that class type shall have a constexpr destructor.
+
+struct B {
+ B() { }
+ ~B() { }
+};
+
+struct T : B {
+ constexpr ~T() { } // { dg-warning "call to" "" { target c++20_down } }
+};
+
+struct S {
+ constexpr S() = default; // was error: implicit S() is not
constexpr, now OK
+ ~S() noexcept(false) = default; // OK, despite mismatched exception
specification
+private:
+ int i;
+ S(S&); // OK: private copy constructor
+};
+S::S(S&) = default; // OK: defines copy constructor
diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
new file mode 100644
index 00000000000..6441dabcc70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
@@ -0,0 +1,43 @@
+// PR c++/106649
+// P2448 - Relaxing some constexpr restrictions
+// { dg-do compile { target c++23 } }
+// { dg-options "-Winvalid-constexpr" }
+// A copy/move assignment operator for a class X that is defaulted and
+// not defined as deleted is implicitly defined when it is odr-used,
+// when it is needed for constant evaluation, or when it is explicitly
+// defaulted after its first declaration.
+// The implicitly-defined copy/move assignment operator is constexpr.
+
+struct S {
+ constexpr S() {}
+ S& operator=(const S&) = default; // #1
+ S& operator=(S&&) = default; // #2
+};
+
+struct U {
+ constexpr U& operator=(const U&) = default;
+ constexpr U& operator=(U&&) = default;
+};
+
+/* FIXME: If we only declare #1 and #2, and default them here:
+
+ S& S::operator=(const S&) = default;
+ S& S::operator=(S&&) = default;
+
+then they aren't constexpr. This sounds like a bug:
+<https://gcc.gnu.org/PR107598>. */