aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2022-11-02 13:11:02 -0400
committerMarek Polacek <polacek@redhat.com>2022-11-16 16:29:09 -0500
commitc85f8dbb173f45053f6d8849d27adc98d9668769 (patch)
tree5709041b56579f5b413fcd128f85d39accfd9554 /gcc/cp
parentdbdce6adb748b95be219f2f5fb97f844a0f9b840 (diff)
downloadgcc-c85f8dbb173f45053f6d8849d27adc98d9668769.zip
gcc-c85f8dbb173f45053f6d8849d27adc98d9668769.tar.gz
gcc-c85f8dbb173f45053f6d8849d27adc98d9668769.tar.bz2
c++: P2448 - Relaxing some constexpr restrictions [PR106649]
This patch implements C++23 P2448, which lifts more restrictions on the constexpr keyword. It's effectively going the way of being just a hint (hello, inline!). This gist is relatively simple: in C++23, a constexpr function's return type/parameter type doesn't have to be a literal type; and you can have a constexpr function for which no invocation satisfies the requirements of a core constant expression. For example, void f(int& i); // not constexpr constexpr void g(int& i) { f(i); // unconditionally calls a non-constexpr function } is now OK, even though there isn't an invocation of 'g' that would be a constant expression. Maybe 'f' will be made constexpr soon, or maybe this depends on the version of C++ used, and similar. The patch is unfortunately not that trivial. The important bit is to use the new require_potential_rvalue_constant_expression_fncheck in maybe_save_constexpr_fundef (and where appropriate). It has a new flag that says that we're checking the body of a constexpr function, and in that case it's OK to find constructs that aren't a constant expression. Since it's useful to be able to check for problematic constructs even in C++23, this patch implements a new warning, -Winvalid-constexpr, which is a pedwarn turned on by default in C++20 and earlier, and which can be turned on in C++23 as well, in which case it's an ordinary warning. This I implemented by using the new function constexpr_error, used in p_c_e_1 and friends. (In some cases I believe fundef_p will be always false (= hard error), but it made sense to me to be consistent and use constexpr_error throughout p_c_e_1.) While working on this I think I found a bug, see constexpr-nonlit15.C and <https://gcc.gnu.org/PR107598>. This patch doesn't address that. This patch includes changes to diagnose the problem if the user doesn't use -Winvalid-constexpr and calls a constexpr function that in fact isn't constexpr-ready yet: maybe_save_constexpr_fundef registers the function if warn_invalid_constexpr is 0 and explain_invalid_constexpr_fn then gives the diagnostic. PR c++/106649 gcc/c-family/ChangeLog: * c-cppbuiltin.cc (c_cpp_builtins): Update value of __cpp_constexpr for C++23. * c-opts.cc (c_common_post_options): Set warn_invalid_constexpr depending on cxx_dialect. * c.opt (Winvalid-constexpr): New option. gcc/cp/ChangeLog: * constexpr.cc (constexpr_error): New function. (is_valid_constexpr_fn): Use constexpr_error. (maybe_save_constexpr_fundef): Call require_potential_rvalue_constant_expression_fncheck rather than require_potential_rvalue_constant_expression. Register the function if -Wno-invalid-constexpr was specified. (explain_invalid_constexpr_fn): Don't return early if a function marked 'constexpr' that isn't actually a constant expression was called. (non_const_var_error): Add a bool parameter. Use constexpr_error. (inline_asm_in_constexpr_error): Likewise. (cxx_eval_constant_expression): Adjust calls to non_const_var_error and inline_asm_in_constexpr_error. (potential_constant_expression_1): Add a bool parameter. Use constexpr_error. (require_potential_rvalue_constant_expression_fncheck): New function. * cp-tree.h (require_potential_rvalue_constant_expression_fncheck): Declare. * method.cc (struct comp_info): Call require_potential_rvalue_constant_expression_fncheck rather than require_potential_rvalue_constant_expression. gcc/ChangeLog: * doc/invoke.texi: Document -Winvalid-constexpr. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-ctor2.C: Expect an error in c++20_down only. * g++.dg/cpp0x/constexpr-default-ctor.C: Likewise. * g++.dg/cpp0x/constexpr-diag3.C: Likewise. * g++.dg/cpp0x/constexpr-ex1.C: Likewise. * g++.dg/cpp0x/constexpr-friend.C: Likewise. * g++.dg/cpp0x/constexpr-generated1.C: Likewise. * g++.dg/cpp0x/constexpr-ice5.C: Likewise. * g++.dg/cpp0x/constexpr-ice6.C: Likewise. * g++.dg/cpp0x/constexpr-memfn1.C: Likewise. * g++.dg/cpp0x/constexpr-neg2.C: Likewise. * g++.dg/cpp0x/constexpr-non-const-arg.C: Likewise. * g++.dg/cpp0x/constexpr-reinterpret1.C: Likewise. * g++.dg/cpp0x/pr65327.C: Likewise. * g++.dg/cpp1y/constexpr-105050.C: Likewise. * g++.dg/cpp1y/constexpr-89285-2.C: Likewise. * g++.dg/cpp1y/constexpr-89285.C: Likewise. * g++.dg/cpp1y/constexpr-89785-2.C: Likewise. * g++.dg/cpp1y/constexpr-neg1.C: Likewise. * g++.dg/cpp1y/constexpr-nsdmi7b.C: Likewise. * g++.dg/cpp1y/constexpr-throw.C: Likewise. * g++.dg/cpp23/constexpr-nonlit3.C: Remove dg-error. * g++.dg/cpp23/constexpr-nonlit6.C: Call the test functions. * g++.dg/cpp23/feat-cxx2b.C: Adjust the expected value of __cpp_constexpr. * g++.dg/cpp2a/consteval3.C: Remove dg-error. * g++.dg/cpp2a/constexpr-new7.C: Expect an error in c++20_down only. * g++.dg/cpp2a/constexpr-try5.C: Remove dg-error. * g++.dg/cpp2a/spaceship-constexpr1.C: Expect an error in c++20_down only. * g++.dg/cpp2a/spaceship-eq3.C: Likewise. * g++.dg/diagnostic/constexpr1.C: Remove dg-error. * g++.dg/gomp/pr79664.C: Use -Winvalid-constexpr -pedantic-errors. * g++.dg/ubsan/vptr-4.C: Likewise. * g++.dg/cpp23/constexpr-nonlit10.C: New test. * g++.dg/cpp23/constexpr-nonlit11.C: New test. * g++.dg/cpp23/constexpr-nonlit12.C: New test. * g++.dg/cpp23/constexpr-nonlit13.C: New test. * g++.dg/cpp23/constexpr-nonlit14.C: New test. * g++.dg/cpp23/constexpr-nonlit15.C: New test. * g++.dg/cpp23/constexpr-nonlit16.C: New test. * g++.dg/cpp23/constexpr-nonlit8.C: New test. * g++.dg/cpp23/constexpr-nonlit9.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/constexpr.cc299
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/method.cc8
3 files changed, 208 insertions, 100 deletions
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index e665839..a390cf9 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -139,6 +139,42 @@ ensure_literal_type_for_constexpr_object (tree decl)
return decl;
}
+/* Issue a diagnostic with text GMSGID for constructs that are invalid in
+ constexpr functions. CONSTEXPR_FUNDEF_P is true if we're checking
+ a constexpr function body; if so, don't report hard errors and issue
+ a pedwarn pre-C++23, or a warning in C++23, if requested by
+ -Winvalid-constexpr. Otherwise, we're not in the context where we are
+ checking if a function can be marked 'constexpr', so give a hard error. */
+
+ATTRIBUTE_GCC_DIAG(3,4)
+static bool
+constexpr_error (location_t location, bool constexpr_fundef_p,
+ const char *gmsgid, ...)
+{
+ diagnostic_info diagnostic;
+ va_list ap;
+ rich_location richloc (line_table, location);
+ va_start (ap, gmsgid);
+ bool ret;
+ if (!constexpr_fundef_p)
+ {
+ /* Report an error that cannot be suppressed. */
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
+ ret = diagnostic_report_diagnostic (global_dc, &diagnostic);
+ }
+ else if (warn_invalid_constexpr)
+ {
+ diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
+ cxx_dialect < cxx23 ? DK_PEDWARN : DK_WARNING);
+ diagnostic.option_index = OPT_Winvalid_constexpr;
+ ret = diagnostic_report_diagnostic (global_dc, &diagnostic);
+ }
+ else
+ ret = false;
+ va_end (ap);
+ return ret;
+}
+
struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
{
static hashval_t hash (const constexpr_fundef *);
@@ -208,9 +244,11 @@ is_valid_constexpr_fn (tree fun, bool complain)
if (complain)
{
auto_diagnostic_group d;
- error ("invalid type for parameter %d of %<constexpr%> "
- "function %q+#D", DECL_PARM_INDEX (parm), fun);
- explain_non_literal_class (TREE_TYPE (parm));
+ if (constexpr_error (input_location, /*constexpr_fundef_p*/true,
+ "invalid type for parameter %d of "
+ "%<constexpr%> function %q+#D",
+ DECL_PARM_INDEX (parm), fun))
+ explain_non_literal_class (TREE_TYPE (parm));
}
}
}
@@ -242,9 +280,10 @@ is_valid_constexpr_fn (tree fun, bool complain)
if (complain)
{
auto_diagnostic_group d;
- error ("invalid return type %qT of %<constexpr%> function %q+D",
- rettype, fun);
- explain_non_literal_class (rettype);
+ if (constexpr_error (input_location, /*constexpr_fundef_p*/true,
+ "invalid return type %qT of %<constexpr%> "
+ "function %q+D", rettype, fun))
+ explain_non_literal_class (rettype);
}
}
@@ -918,7 +957,7 @@ maybe_save_constexpr_fundef (tree fun)
bool potential = potential_rvalue_constant_expression (massaged);
if (!potential && complain)
- require_potential_rvalue_constant_expression (massaged);
+ require_potential_rvalue_constant_expression_fncheck (massaged);
if (DECL_CONSTRUCTOR_P (fun) && potential
&& !DECL_DEFAULTED_FN (fun))
@@ -933,11 +972,16 @@ maybe_save_constexpr_fundef (tree fun)
massaged = DECL_SAVED_TREE (fun);
potential = potential_rvalue_constant_expression (massaged);
if (!potential && complain)
- require_potential_rvalue_constant_expression (massaged);
+ require_potential_rvalue_constant_expression_fncheck (massaged);
}
}
- if (!potential && complain)
+ if (!potential && complain
+ /* If -Wno-invalid-constexpr was specified, we haven't complained
+ about non-constant expressions yet. Register the function and
+ complain in explain_invalid_constexpr_fn if the function is
+ called. */
+ && warn_invalid_constexpr != 0)
return;
if (implicit)
@@ -996,19 +1040,27 @@ register_constexpr_fundef (const constexpr_fundef &value)
**slot = value;
}
-/* FUN is a non-constexpr function called in a context that requires a
- constant expression. If it comes from a constexpr template, explain why
- the instantiation isn't constexpr. */
+/* FUN is a non-constexpr (or, with -Wno-invalid-constexpr, a constexpr
+ function called in a context that requires a constant expression).
+ If it comes from a constexpr template, explain why the instantiation
+ isn't constexpr. Otherwise, explain why the function cannot be used
+ in a constexpr context. */
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 +5664,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 +5678,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 +6751,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 +7039,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 +7930,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 +8815,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 +8833,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 +8860,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 +8921,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 +8971,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 +9002,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 +9023,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 +9058,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 +9097,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 +9123,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 +9133,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 +9156,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 +9230,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 +9379,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 +9391,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 +9441,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 +9455,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 +9471,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 +9533,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 +9591,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 +9624,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 +9674,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 +9776,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 +9807,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 +9845,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 +9858,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 +9889,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 +9899,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 +9939,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 +9947,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 +9963,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 +9973,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 +9983,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 +9993,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 +10013,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 +10028,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 +10038,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 +10048,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 +10059,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 07f96ea..811a834 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 c217d7e..1e962b6 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)