diff options
author | Marek Polacek <polacek@redhat.com> | 2022-11-02 13:11:02 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2022-11-16 16:29:09 -0500 |
commit | c85f8dbb173f45053f6d8849d27adc98d9668769 (patch) | |
tree | 5709041b56579f5b413fcd128f85d39accfd9554 /gcc/cp | |
parent | dbdce6adb748b95be219f2f5fb97f844a0f9b840 (diff) | |
download | gcc-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.cc | 299 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/method.cc | 8 |
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) |