* [PATCH] c++: P2448 - Relaxing some constexpr restrictions [PR106649] @ 2022-11-09 20:53 Marek Polacek 2022-11-14 23:00 ` Jason Merrill 0 siblings, 1 reply; 9+ messages in thread From: Marek Polacek @ 2022-11-09 20:53 UTC (permalink / raw) To: Jason Merrill, GCC Patches 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. I also don't love that in C++23, if you don't use -Winvalid-constexpr, and call a constexpr function that in fact isn't constexpr-ready yet, sometimes all you get is an error saying "called in a constant expression" like in constexpr-nonlit12.C. This could be remedied by some tweaks to explain_invalid_constexpr_fn, I reckon (it gives up on !DECL_DEFAULTED_FN). Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? 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. (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/gcc/gcc-command-options/option-summary.rst: Add -Winvalid-constexpr. * doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst: 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-local4.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: Expect an error in c++20_down only. 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. --- gcc/c-family/c-cppbuiltin.cc | 2 +- gcc/c-family/c-opts.cc | 4 + gcc/c-family/c.opt | 4 + gcc/cp/constexpr.cc | 272 ++++++++++++------ gcc/cp/cp-tree.h | 1 + gcc/cp/method.cc | 8 +- .../gcc-command-options/option-summary.rst | 2 +- .../options-controlling-c++-dialect.rst | 28 ++ gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +- .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +- .../g++.dg/cpp0x/constexpr-generated1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 6 +- .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +- .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +- .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +- .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +- gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +- .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +- .../g++.dg/cpp23/constexpr-nonlit10.C | 96 +++++++ .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++ .../g++.dg/cpp23/constexpr-nonlit12.C | 32 +++ .../g++.dg/cpp23/constexpr-nonlit13.C | 14 + .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++ .../g++.dg/cpp23/constexpr-nonlit15.C | 43 +++ .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++ .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +- .../g++.dg/cpp23/constexpr-nonlit6.C | 19 +- .../g++.dg/cpp23/constexpr-nonlit8.C | 96 +++++++ .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 - gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 - .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +- gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +- gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 - gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +- gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +- 49 files changed, 717 insertions(+), 144 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index cdb658f6ac9..08f7f6d5f75 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++23. */ cpp_define (pfile, "__cpp_size_t_suffix=202011L"); cpp_define (pfile, "__cpp_if_consteval=202106L"); - cpp_define (pfile, "__cpp_constexpr=202110L"); + cpp_define (pfile, "__cpp_constexpr=202207L"); cpp_define (pfile, "__cpp_multidimensional_subscript=202110L"); cpp_define (pfile, "__cpp_named_character_escapes=202207L"); cpp_define (pfile, "__cpp_static_call_operator=202207L"); diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 9e0494b2a45..94e92dc56b9 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename) if (flag_sized_deallocation == -1) flag_sized_deallocation = (cxx_dialect >= cxx14); + /* Pedwarn about invalid constexpr functions before C++23. */ + if (warn_invalid_constexpr == -1) + warn_invalid_constexpr = (cxx_dialect < cxx23); + /* char8_t support is implicitly enabled in C++20 and C2X. */ if (flag_char8_t == -1) flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x; diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 63a300ecd7c..3daeab85531 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -817,6 +817,10 @@ Wint-to-pointer-cast C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning Warn when there is a cast to a pointer from an integer of a different size. +Winvalid-constexpr +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning +Warn when a function never produces a constant expression. + Winvalid-offsetof C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning Warn about invalid uses of the \"offsetof\" macro. diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 15b4f2c4a08..5641b72cd30 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,7 +972,7 @@ 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); } } @@ -5612,11 +5651,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 +5665,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 +6738,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 +7026,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 +7917,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 +8802,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 +8820,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 +8847,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 +8908,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 +8958,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 +8989,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 +9010,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 +9045,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 +9084,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 +9110,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 +9120,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 +9143,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 +9217,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 +9366,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 +9378,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 +9428,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 +9442,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 +9458,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; @@ -9467,9 +9522,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 @@ -9525,15 +9580,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 @@ -9556,9 +9613,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; } @@ -9605,7 +9663,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 @@ -9706,7 +9765,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. */ @@ -9736,7 +9796,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)) @@ -9774,9 +9834,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; @@ -9785,8 +9847,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; @@ -9815,7 +9878,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; } @@ -9824,8 +9888,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: @@ -9863,7 +9928,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) { @@ -9871,13 +9936,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); } @@ -9886,7 +9952,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. */ @@ -9894,7 +9962,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. */ @@ -9902,7 +9972,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); } @@ -9911,7 +9982,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); } @@ -9920,7 +10002,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); } @@ -9934,7 +10017,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. */ @@ -9942,7 +10027,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. */ @@ -9950,7 +10037,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); } @@ -9960,7 +10048,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 bbc8be21900..7a74132e4d0 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8452,6 +8452,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/gcc/gcc-command-options/option-summary.rst b/gcc/doc/gcc/gcc-command-options/option-summary.rst index d068f98feac..01123f55069 100644 --- a/gcc/doc/gcc/gcc-command-options/option-summary.rst +++ b/gcc/doc/gcc/gcc-command-options/option-summary.rst @@ -89,7 +89,7 @@ in the following sections. :option:`-Wno-deprecated-enum-enum-conversion` :option:`-Wno-deprecated-enum-float-conversion` |gol| :option:`-Weffc++` :option:`-Wno-exceptions` :option:`-Wextra-semi` :option:`-Wno-inaccessible-base` |gol| :option:`-Wno-inherited-variadic-ctor` :option:`-Wno-init-list-lifetime` |gol| - :option:`-Winvalid-imported-macros` |gol| + :option:`-Winvalid-constexpr` :option:`-Winvalid-imported-macros` |gol| :option:`-Wno-invalid-offsetof` :option:`-Wno-literal-suffix` |gol| :option:`-Wmismatched-new-delete` :option:`-Wmismatched-tags` |gol| :option:`-Wmultiple-inheritance` :option:`-Wnamespaces` :option:`-Wnarrowing` |gol| diff --git a/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst b/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst index 5b05d31aae9..63517184c23 100644 --- a/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst +++ b/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst @@ -989,6 +989,34 @@ In addition, these warning options have meanings only for C++ programs: Default setting; overrides :option:`-Wno-init-list-lifetime`. +.. option:: -Winvalid-constexpr + + .. note:: + + C++ and Objective-C++ only + + Warn when a function never produces a constant expression. In C++20 + and earlier, for every ``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 ``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: + + .. code-block:: c++ + + void f (int& i); + constexpr void + g (int& i) + { + f(i); // warns by default in C++20, in C++23 only with -Winvalid-constexpr + } + .. option:: -Winvalid-imported-macros Verify all imported macro definitions are valid at the end of 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..5d11822acb5 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C @@ -87,7 +87,7 @@ 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 } } } 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..4b27612d45c 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C @@ -15,13 +15,13 @@ constexpr int three = one() ? 3 : nonconst_func(0); // such that the function invocation sub-stitution would produce a // constant expression (5.19), the program is ill-formed; no diagnostic // required. -constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" } +constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } } // 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-local4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C index 647b5dcd7cd..180549e723a 100644 --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C @@ -10,7 +10,7 @@ const A a = 42; constexpr int f() { - const int j = a.i; // { dg-error "'a'" } + const int j = a.i; // { dg-error "'a'" "" { target c++20_down } } return j; } 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..dd15e1552d7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C @@ -0,0 +1,32 @@ +// 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; +} + +void +g () +{ + constexpr int i = 42; + /* The diagnostics differ. fn0 is considered potentially-constant, but fn1 + isn't due to "reinterpret_cast from integer to pointer". So for fn1, + maybe_save_constexpr_fundef doesn't register_constexpr_fundef because + 'potential' is false. Then cxx_eval_call_expression issues the "called + in a constant expression" error, and explain_invalid_constexpr_fn doesn't + explain what the problem is because it has "Only diagnose defaulted + functions, lambdas, or instantiations." (With -Winvalid-constexpr, we + emit more information.) */ + 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>. */ + +constexpr void +g () +{ + S a; + S b; + b = a; + b = S{}; + + U u, v; + u = v; + u = U{}; +} + +static_assert ((g(), true), ""); diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C new file mode 100644 index 00000000000..a6c4d19ffc6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C @@ -0,0 +1,23 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++20 } } +// { dg-options "" } + +template <typename T> +struct Wrapper { + constexpr Wrapper() = default; + constexpr Wrapper(Wrapper const&) = default; + constexpr Wrapper(T const& t) : t(t) { } + + constexpr T get() const { return t; } + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } } +private: + T t; +}; + +struct X { + X(); + bool operator==(X const&) const; +}; + +Wrapper<X> x; diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C index 3b5585dcd84..2238db91157 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C @@ -5,6 +5,6 @@ constexpr int foo () { goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } +lab: return 1; } diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C index fbeb83075b0..5a996cbc5c2 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C @@ -5,7 +5,7 @@ constexpr int foo () { - goto lab; // { dg-error "'goto' is not a constant expression" } + goto lab; // { dg-error "'goto' is not a constant expression" "" { target c++20_down } } lab: return 1; } @@ -13,13 +13,24 @@ lab: constexpr int bar () { - static int a; // { dg-error "'a' defined 'static' in 'constexpr' context" } + static int a; // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++20_down } } return ++a; } constexpr int -baz (int x) +baz () { - thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" } + thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" "" { target c++20_down } } return ++a; } + +// In C++23, we get errors about the non-constant expressions only if we +// actually call the functions in a constexpr context. + +void +test () +{ + constexpr int a = foo (); // { dg-error "constant expression" } + constexpr int b = bar (); // { dg-error "constant expression" } + constexpr int c = baz (); // { dg-error "constant expression" } +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C new file mode 100644 index 00000000000..3fb1b93bd07 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C @@ -0,0 +1,96 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++14 } } +// { dg-options "" } + +// 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" "" { target c++20_down } } +{ + return NonLiteral{}; +} + +constexpr int +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } } +{ + return 42; +} + +// From P2448. +void f(int& i) { + i = 0; +} + +constexpr void g(int& i) { + f(i); // { dg-warning "call to" "" { target c++20_down } } +} + +// [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" "" { target c++20_down } } + // 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" "" { target c++20_down } } +} + +constexpr unsigned int +fn5 (int *p) +{ + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } } + return *q; +} + +constexpr int +fn6 (int i) +{ + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } } + return 42; +} + +constexpr int +fn7 (int i) +{ + static int s = i; // { dg-error "static" "" { target c++20_down } } + return s; +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C new file mode 100644 index 00000000000..228e90f14c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C @@ -0,0 +1,53 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++14 } } +// { dg-options "" } + +// [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" "" { target c++20_down } } + S(int, int) { } + // Target constructor isn't constexpr. + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } } +}; + +namespace N1 { +struct X { + void x(); +}; +struct Y { + X x; + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } } +}; +} + +void g(); + +struct A { + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } } +}; + +struct B { + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index efe97703a98..621c8dc21ea 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -134,8 +134,8 @@ #ifndef __cpp_constexpr # error "__cpp_constexpr" -#elif __cpp_constexpr != 202110 -# error "__cpp_constexpr != 202110" +#elif __cpp_constexpr != 202207 +# error "__cpp_constexpr != 202207" #endif #ifndef __cpp_decltype_auto diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 83463868668..627ab142d5a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -57,7 +57,6 @@ consteval int f13 (int x) { static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } } - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } return x; } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C index bb60a8ee91b..b2c98853882 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept; constexpr bool foo () { - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } *p = 1; ::operator delete (p); return false; @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; }; constexpr bool bar () { - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } auto q = new (p) S (); q->s++; q->~S (); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C index 216634dc56c..eb66105d7c4 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C @@ -6,7 +6,6 @@ constexpr int foo () try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l:; return 0; @@ -22,7 +21,6 @@ constexpr int bar () { int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l:; try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C index dff59271a1e..fb62ecbfdb5 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C @@ -9,7 +9,7 @@ struct A struct B { A a; - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } } + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } } }; constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C index 7a517a8016c..69eaa7b9b20 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C @@ -7,8 +7,8 @@ struct A { struct D { A i; - bool operator==(const D& x) const = default; // { dg-error "A::operator==" } - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" } + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } }; constexpr D d{A()}; diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C index c962a60c847..19242d15ba8 100644 --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C @@ -1,7 +1,5 @@ // { dg-do compile { target c++11 } } constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } } -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 } constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } } -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 } diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C index 582eedb6d6d..f4c30c0b3f4 100644 --- a/gcc/testsuite/g++.dg/gomp/pr79664.C +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C @@ -1,6 +1,6 @@ // PR c++/79664 // { dg-do compile } -// { dg-options "-std=c++14 -fopenmp" } +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" } constexpr int f1 () diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C index a21d3d60a3a..1efd3f77a55 100644 --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C @@ -1,7 +1,7 @@ // Verify that -fsanitize=vptr downcast instrumentation works properly // inside of constexpr. // { dg-do compile } -// { dg-options "-std=c++11 -fsanitize=vptr" } +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" } struct S { constexpr S() : a(0) {} base-commit: e505f7493bed1395d121d2f53137ec11706fa42e -- 2.38.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-09 20:53 [PATCH] c++: P2448 - Relaxing some constexpr restrictions [PR106649] Marek Polacek @ 2022-11-14 23:00 ` Jason Merrill 2022-11-16 0:30 ` [PATCH v2] " Marek Polacek 0 siblings, 1 reply; 9+ messages in thread From: Jason Merrill @ 2022-11-14 23:00 UTC (permalink / raw) To: Marek Polacek, GCC Patches On 11/9/22 10:53, Marek Polacek wrote: > 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. > > I also don't love that in C++23, if you don't use -Winvalid-constexpr, > and call a constexpr function that in fact isn't constexpr-ready yet, > sometimes all you get is an error saying "called in a constant expression" > like in constexpr-nonlit12.C. This could be remedied by some tweaks to > explain_invalid_constexpr_fn, I reckon (it gives up on !DECL_DEFAULTED_FN). And also in maybe_save_constexpr_fn: if -Wno-invalid-constexpr, "complain" should be false so we save the definition for explain_invalid_constexpr_fn to refer to. > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > 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. > (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/gcc/gcc-command-options/option-summary.rst: Add > -Winvalid-constexpr. > * doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst: > 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-local4.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: Expect an error in c++20_down only. > 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. > --- > gcc/c-family/c-cppbuiltin.cc | 2 +- > gcc/c-family/c-opts.cc | 4 + > gcc/c-family/c.opt | 4 + > gcc/cp/constexpr.cc | 272 ++++++++++++------ > gcc/cp/cp-tree.h | 1 + > gcc/cp/method.cc | 8 +- > .../gcc-command-options/option-summary.rst | 2 +- > .../options-controlling-c++-dialect.rst | 28 ++ > gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +- > .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +- > .../g++.dg/cpp0x/constexpr-generated1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 6 +- > .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +- > .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +- > .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +- > .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +- > .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +- > .../g++.dg/cpp23/constexpr-nonlit10.C | 96 +++++++ > .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++ > .../g++.dg/cpp23/constexpr-nonlit12.C | 32 +++ > .../g++.dg/cpp23/constexpr-nonlit13.C | 14 + > .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++ > .../g++.dg/cpp23/constexpr-nonlit15.C | 43 +++ > .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++ > .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +- > .../g++.dg/cpp23/constexpr-nonlit6.C | 19 +- > .../g++.dg/cpp23/constexpr-nonlit8.C | 96 +++++++ > .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++ > gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- > gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 - > gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +- > gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 - > .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +- > gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +- > gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 - > gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +- > gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +- > 49 files changed, 717 insertions(+), 144 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > > diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc > index cdb658f6ac9..08f7f6d5f75 100644 > --- a/gcc/c-family/c-cppbuiltin.cc > +++ b/gcc/c-family/c-cppbuiltin.cc > @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) > /* Set feature test macros for C++23. */ > cpp_define (pfile, "__cpp_size_t_suffix=202011L"); > cpp_define (pfile, "__cpp_if_consteval=202106L"); > - cpp_define (pfile, "__cpp_constexpr=202110L"); > + cpp_define (pfile, "__cpp_constexpr=202207L"); > cpp_define (pfile, "__cpp_multidimensional_subscript=202110L"); > cpp_define (pfile, "__cpp_named_character_escapes=202207L"); > cpp_define (pfile, "__cpp_static_call_operator=202207L"); > diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc > index 9e0494b2a45..94e92dc56b9 100644 > --- a/gcc/c-family/c-opts.cc > +++ b/gcc/c-family/c-opts.cc > @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename) > if (flag_sized_deallocation == -1) > flag_sized_deallocation = (cxx_dialect >= cxx14); > > + /* Pedwarn about invalid constexpr functions before C++23. */ > + if (warn_invalid_constexpr == -1) > + warn_invalid_constexpr = (cxx_dialect < cxx23); > + > /* char8_t support is implicitly enabled in C++20 and C2X. */ > if (flag_char8_t == -1) > flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x; > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > index 63a300ecd7c..3daeab85531 100644 > --- a/gcc/c-family/c.opt > +++ b/gcc/c-family/c.opt > @@ -817,6 +817,10 @@ Wint-to-pointer-cast > C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning > Warn when there is a cast to a pointer from an integer of a different size. > > +Winvalid-constexpr > +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning > +Warn when a function never produces a constant expression. > + > Winvalid-offsetof > C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning > Warn about invalid uses of the \"offsetof\" macro. > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index 15b4f2c4a08..5641b72cd30 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,7 +972,7 @@ 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); > } > } > > @@ -5612,11 +5651,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 +5665,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 +6738,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 +7026,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 +7917,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 +8802,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 +8820,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 +8847,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 +8908,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 +8958,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 +8989,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 +9010,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 +9045,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 +9084,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 +9110,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 +9120,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 +9143,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 +9217,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 +9366,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 +9378,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 +9428,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 +9442,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 +9458,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; > @@ -9467,9 +9522,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 > @@ -9525,15 +9580,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 > @@ -9556,9 +9613,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; > } > @@ -9605,7 +9663,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 > @@ -9706,7 +9765,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. */ > @@ -9736,7 +9796,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)) > @@ -9774,9 +9834,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; > > @@ -9785,8 +9847,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; > > @@ -9815,7 +9878,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; > } > > @@ -9824,8 +9888,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: > @@ -9863,7 +9928,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) > { > @@ -9871,13 +9936,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); > } > > @@ -9886,7 +9952,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. */ > @@ -9894,7 +9962,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. */ > @@ -9902,7 +9972,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); > } > > @@ -9911,7 +9982,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); > } > > @@ -9920,7 +10002,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); > } > > @@ -9934,7 +10017,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. */ > @@ -9942,7 +10027,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. */ > @@ -9950,7 +10037,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); > } > > @@ -9960,7 +10048,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 bbc8be21900..7a74132e4d0 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -8452,6 +8452,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/gcc/gcc-command-options/option-summary.rst b/gcc/doc/gcc/gcc-command-options/option-summary.rst > index d068f98feac..01123f55069 100644 > --- a/gcc/doc/gcc/gcc-command-options/option-summary.rst > +++ b/gcc/doc/gcc/gcc-command-options/option-summary.rst > @@ -89,7 +89,7 @@ in the following sections. > :option:`-Wno-deprecated-enum-enum-conversion` :option:`-Wno-deprecated-enum-float-conversion` |gol| > :option:`-Weffc++` :option:`-Wno-exceptions` :option:`-Wextra-semi` :option:`-Wno-inaccessible-base` |gol| > :option:`-Wno-inherited-variadic-ctor` :option:`-Wno-init-list-lifetime` |gol| > - :option:`-Winvalid-imported-macros` |gol| > + :option:`-Winvalid-constexpr` :option:`-Winvalid-imported-macros` |gol| > :option:`-Wno-invalid-offsetof` :option:`-Wno-literal-suffix` |gol| > :option:`-Wmismatched-new-delete` :option:`-Wmismatched-tags` |gol| > :option:`-Wmultiple-inheritance` :option:`-Wnamespaces` :option:`-Wnarrowing` |gol| > diff --git a/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst b/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst > index 5b05d31aae9..63517184c23 100644 > --- a/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst > +++ b/gcc/doc/gcc/gcc-command-options/options-controlling-c++-dialect.rst > @@ -989,6 +989,34 @@ In addition, these warning options have meanings only for C++ programs: > > Default setting; overrides :option:`-Wno-init-list-lifetime`. > > +.. option:: -Winvalid-constexpr > + > + .. note:: > + > + C++ and Objective-C++ only > + > + Warn when a function never produces a constant expression. In C++20 > + and earlier, for every ``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 ``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: > + > + .. code-block:: c++ > + > + void f (int& i); > + constexpr void > + g (int& i) > + { > + f(i); // warns by default in C++20, in C++23 only with -Winvalid-constexpr > + } > + > .. option:: -Winvalid-imported-macros > > Verify all imported macro definitions are valid at the end of > 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..5d11822acb5 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C > @@ -87,7 +87,7 @@ 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 } } } > 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..4b27612d45c 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C > @@ -15,13 +15,13 @@ constexpr int three = one() ? 3 : nonconst_func(0); > // such that the function invocation sub-stitution would produce a > // constant expression (5.19), the program is ill-formed; no diagnostic > // required. > -constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" } > +constexpr int bogus() { return zero () ? 3 : nonconst_func(0); } // { dg-error "nonconst_func" "" { target c++20_down } } > > // 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-local4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C > index 647b5dcd7cd..180549e723a 100644 > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-local4.C > @@ -10,7 +10,7 @@ const A a = 42; > > constexpr int f() > { > - const int j = a.i; // { dg-error "'a'" } > + const int j = a.i; // { dg-error "'a'" "" { target c++20_down } } > return j; > } > > 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..dd15e1552d7 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C > @@ -0,0 +1,32 @@ > +// 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; > +} > + > +void > +g () > +{ > + constexpr int i = 42; > + /* The diagnostics differ. fn0 is considered potentially-constant, but fn1 > + isn't due to "reinterpret_cast from integer to pointer". So for fn1, > + maybe_save_constexpr_fundef doesn't register_constexpr_fundef because > + 'potential' is false. Then cxx_eval_call_expression issues the "called > + in a constant expression" error, and explain_invalid_constexpr_fn doesn't > + explain what the problem is because it has "Only diagnose defaulted > + functions, lambdas, or instantiations." (With -Winvalid-constexpr, we > + emit more information.) */ > + 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>. */ > + > +constexpr void > +g () > +{ > + S a; > + S b; > + b = a; > + b = S{}; > + > + U u, v; > + u = v; > + u = U{}; > +} > + > +static_assert ((g(), true), ""); > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > new file mode 100644 > index 00000000000..a6c4d19ffc6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > @@ -0,0 +1,23 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++20 } } > +// { dg-options "" } > + > +template <typename T> > +struct Wrapper { > + constexpr Wrapper() = default; > + constexpr Wrapper(Wrapper const&) = default; > + constexpr Wrapper(T const& t) : t(t) { } > + > + constexpr T get() const { return t; } > + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } } > +private: > + T t; > +}; > + > +struct X { > + X(); > + bool operator==(X const&) const; > +}; > + > +Wrapper<X> x; > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > index 3b5585dcd84..2238db91157 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > @@ -5,6 +5,6 @@ constexpr int > foo () > { > goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } > +lab: > return 1; > } > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > index fbeb83075b0..5a996cbc5c2 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > @@ -5,7 +5,7 @@ > constexpr int > foo () > { > - goto lab; // { dg-error "'goto' is not a constant expression" } > + goto lab; // { dg-error "'goto' is not a constant expression" "" { target c++20_down } } > lab: > return 1; > } > @@ -13,13 +13,24 @@ lab: > constexpr int > bar () > { > - static int a; // { dg-error "'a' defined 'static' in 'constexpr' context" } > + static int a; // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++20_down } } > return ++a; > } > > constexpr int > -baz (int x) > +baz () > { > - thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" } > + thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" "" { target c++20_down } } > return ++a; > } > + > +// In C++23, we get errors about the non-constant expressions only if we > +// actually call the functions in a constexpr context. > + > +void > +test () > +{ > + constexpr int a = foo (); // { dg-error "constant expression" } > + constexpr int b = bar (); // { dg-error "constant expression" } > + constexpr int c = baz (); // { dg-error "constant expression" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > new file mode 100644 > index 00000000000..3fb1b93bd07 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > @@ -0,0 +1,96 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++14 } } > +// { dg-options "" } > + > +// 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" "" { target c++20_down } } > +{ > + return NonLiteral{}; > +} > + > +constexpr int > +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } } > +{ > + return 42; > +} > + > +// From P2448. > +void f(int& i) { > + i = 0; > +} > + > +constexpr void g(int& i) { > + f(i); // { dg-warning "call to" "" { target c++20_down } } > +} > + > +// [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" "" { target c++20_down } } > + // 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" "" { target c++20_down } } > +} > + > +constexpr unsigned int > +fn5 (int *p) > +{ > + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } } > + return *q; > +} > + > +constexpr int > +fn6 (int i) > +{ > + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } } > + return 42; > +} > + > +constexpr int > +fn7 (int i) > +{ > + static int s = i; // { dg-error "static" "" { target c++20_down } } > + return s; > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > new file mode 100644 > index 00000000000..228e90f14c1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > @@ -0,0 +1,53 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++14 } } > +// { dg-options "" } > + > +// [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" "" { target c++20_down } } > + S(int, int) { } > + // Target constructor isn't constexpr. > + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } } > +}; > + > +namespace N1 { > +struct X { > + void x(); > +}; > +struct Y { > + X x; > + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } } > +}; > +} > + > +void g(); > + > +struct A { > + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } } > +}; > + > +struct B { > + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } > + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } > +}; > diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > index efe97703a98..621c8dc21ea 100644 > --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > @@ -134,8 +134,8 @@ > > #ifndef __cpp_constexpr > # error "__cpp_constexpr" > -#elif __cpp_constexpr != 202110 > -# error "__cpp_constexpr != 202110" > +#elif __cpp_constexpr != 202207 > +# error "__cpp_constexpr != 202207" > #endif > > #ifndef __cpp_decltype_auto > diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C > index 83463868668..627ab142d5a 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C > @@ -57,7 +57,6 @@ consteval int > f13 (int x) > { > static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } } > - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } > return x; > } > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > index bb60a8ee91b..b2c98853882 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept; > constexpr bool > foo () > { > - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } > + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } > *p = 1; > ::operator delete (p); > return false; > @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; }; > constexpr bool > bar () > { > - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } > + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } > auto q = new (p) S (); > q->s++; > q->~S (); > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > index 216634dc56c..eb66105d7c4 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > @@ -6,7 +6,6 @@ constexpr int foo () > try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } > - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l:; > return 0; > @@ -22,7 +21,6 @@ constexpr int bar () > { > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } > - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l:; > try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > index dff59271a1e..fb62ecbfdb5 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > @@ -9,7 +9,7 @@ struct A > struct B > { > A a; > - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } } > + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } } > }; > > constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > index 7a517a8016c..69eaa7b9b20 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > @@ -7,8 +7,8 @@ struct A { > struct D > { > A i; > - bool operator==(const D& x) const = default; // { dg-error "A::operator==" } > - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" } > + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } > + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } > }; > > constexpr D d{A()}; > diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > index c962a60c847..19242d15ba8 100644 > --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > @@ -1,7 +1,5 @@ > // { dg-do compile { target c++11 } } > > constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } } > -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 } > > constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } } > -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 } > diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C > index 582eedb6d6d..f4c30c0b3f4 100644 > --- a/gcc/testsuite/g++.dg/gomp/pr79664.C > +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C > @@ -1,6 +1,6 @@ > // PR c++/79664 > // { dg-do compile } > -// { dg-options "-std=c++14 -fopenmp" } > +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" } > > constexpr int > f1 () > diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C > index a21d3d60a3a..1efd3f77a55 100644 > --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C > +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C > @@ -1,7 +1,7 @@ > // Verify that -fsanitize=vptr downcast instrumentation works properly > // inside of constexpr. > // { dg-do compile } > -// { dg-options "-std=c++11 -fsanitize=vptr" } > +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" } > > struct S { > constexpr S() : a(0) {} > > base-commit: e505f7493bed1395d121d2f53137ec11706fa42e ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-14 23:00 ` Jason Merrill @ 2022-11-16 0:30 ` Marek Polacek 2022-11-16 13:41 ` Jason Merrill 0 siblings, 1 reply; 9+ messages in thread From: Marek Polacek @ 2022-11-16 0:30 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Mon, Nov 14, 2022 at 06:00:58PM -0500, Jason Merrill wrote: > On 11/9/22 10:53, Marek Polacek wrote: > > 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. > > > > I also don't love that in C++23, if you don't use -Winvalid-constexpr, > > and call a constexpr function that in fact isn't constexpr-ready yet, > > sometimes all you get is an error saying "called in a constant expression" > > like in constexpr-nonlit12.C. This could be remedied by some tweaks to > > explain_invalid_constexpr_fn, I reckon (it gives up on !DECL_DEFAULTED_FN). > > And also in maybe_save_constexpr_fn: if -Wno-invalid-constexpr, "complain" > should be false so we save the definition for explain_invalid_constexpr_fn > to refer to. I did something similar: don't return if warn_invalid_constexpr == 0. I thought I'd just adjust the initializer for 'complain' but due to constexpr-ice4.C we should call is_valid_constexpr_fn even when warn_invalid_constexpr is 0. The only non-testsuite changes are to maybe_save_constexpr_fundef and explain_invalid_constexpr_fn. Updated patch below, now we emit a better diagnostic for constexpr-nonlit12.C even with -std=c++23. And also in C++20 with -Wno-invalid-constexpr. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- 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. --- gcc/c-family/c-cppbuiltin.cc | 2 +- gcc/c-family/c-opts.cc | 4 + gcc/c-family/c.opt | 4 + gcc/cp/constexpr.cc | 298 ++++++++++++------ gcc/cp/cp-tree.h | 1 + gcc/cp/method.cc | 8 +- gcc/doc/invoke.texi | 28 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +- .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 3 +- gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +- .../g++.dg/cpp0x/constexpr-generated1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 4 +- .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +- .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +- .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +- .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +- .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +- .../g++.dg/cpp23/constexpr-nonlit10.C | 96 ++++++ .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++ .../g++.dg/cpp23/constexpr-nonlit12.C | 24 ++ .../g++.dg/cpp23/constexpr-nonlit13.C | 14 + .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++ .../g++.dg/cpp23/constexpr-nonlit15.C | 43 +++ .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++ .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +- .../g++.dg/cpp23/constexpr-nonlit6.C | 13 +- .../g++.dg/cpp23/constexpr-nonlit8.C | 96 ++++++ .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 - gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 - .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +- gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +- gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 - gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +- gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +- 47 files changed, 722 insertions(+), 146 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index f0be0598190..26447d02097 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++23. */ cpp_define (pfile, "__cpp_size_t_suffix=202011L"); cpp_define (pfile, "__cpp_if_consteval=202106L"); - cpp_define (pfile, "__cpp_constexpr=202110L"); + cpp_define (pfile, "__cpp_constexpr=202207L"); cpp_define (pfile, "__cpp_multidimensional_subscript=202211L"); cpp_define (pfile, "__cpp_named_character_escapes=202207L"); cpp_define (pfile, "__cpp_static_call_operator=202207L"); diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index bc1f85e3b85..70745aa4e7c 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename) if (flag_sized_deallocation == -1) flag_sized_deallocation = (cxx_dialect >= cxx14); + /* Pedwarn about invalid constexpr functions before C++23. */ + if (warn_invalid_constexpr == -1) + warn_invalid_constexpr = (cxx_dialect < cxx23); + /* char8_t support is implicitly enabled in C++20 and C2X. */ if (flag_char8_t == -1) flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x; diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 63a300ecd7c..3daeab85531 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -817,6 +817,10 @@ Wint-to-pointer-cast C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning Warn when there is a cast to a pointer from an integer of a different size. +Winvalid-constexpr +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning +Warn when a function never produces a constant expression. + Winvalid-offsetof C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning Warn about invalid uses of the \"offsetof\" macro. diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index e665839f5b1..20d70d0c327 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,26 @@ 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. */ 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>. */ + +constexpr void +g () +{ + S a; + S b; + b = a; + b = S{}; + + U u, v; + u = v; + u = U{}; +} + +static_assert ((g(), true), ""); diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C new file mode 100644 index 00000000000..a6c4d19ffc6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C @@ -0,0 +1,23 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++20 } } +// { dg-options "" } + +template <typename T> +struct Wrapper { + constexpr Wrapper() = default; + constexpr Wrapper(Wrapper const&) = default; + constexpr Wrapper(T const& t) : t(t) { } + + constexpr T get() const { return t; } + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } } +private: + T t; +}; + +struct X { + X(); + bool operator==(X const&) const; +}; + +Wrapper<X> x; diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C index 3b5585dcd84..2238db91157 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C @@ -5,6 +5,6 @@ constexpr int foo () { goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } +lab: return 1; } diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C index fbeb83075b0..a1436938318 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C @@ -18,8 +18,19 @@ bar () } constexpr int -baz (int x) +baz () { thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" } return ++a; } + +// In C++23, we get errors about the non-constant expressions only if we +// actually call the functions in a constexpr context. + +void +test () +{ + constexpr int a = foo (); // { dg-error "constant expression" } + constexpr int b = bar (); // { dg-error "constant expression" } + constexpr int c = baz (); // { dg-error "constant expression" } +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C new file mode 100644 index 00000000000..3fb1b93bd07 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C @@ -0,0 +1,96 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++14 } } +// { dg-options "" } + +// 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" "" { target c++20_down } } +{ + return NonLiteral{}; +} + +constexpr int +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } } +{ + return 42; +} + +// From P2448. +void f(int& i) { + i = 0; +} + +constexpr void g(int& i) { + f(i); // { dg-warning "call to" "" { target c++20_down } } +} + +// [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" "" { target c++20_down } } + // 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" "" { target c++20_down } } +} + +constexpr unsigned int +fn5 (int *p) +{ + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } } + return *q; +} + +constexpr int +fn6 (int i) +{ + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } } + return 42; +} + +constexpr int +fn7 (int i) +{ + static int s = i; // { dg-error "static" "" { target c++20_down } } + return s; +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C new file mode 100644 index 00000000000..228e90f14c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C @@ -0,0 +1,53 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++14 } } +// { dg-options "" } + +// [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" "" { target c++20_down } } + S(int, int) { } + // Target constructor isn't constexpr. + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } } +}; + +namespace N1 { +struct X { + void x(); +}; +struct Y { + X x; + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } } +}; +} + +void g(); + +struct A { + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } } +}; + +struct B { + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index 85e66871fd1..3ce36e45cb7 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -134,8 +134,8 @@ #ifndef __cpp_constexpr # error "__cpp_constexpr" -#elif __cpp_constexpr != 202110 -# error "__cpp_constexpr != 202110" +#elif __cpp_constexpr != 202207 +# error "__cpp_constexpr != 202207" #endif #ifndef __cpp_decltype_auto diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 83463868668..627ab142d5a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -57,7 +57,6 @@ consteval int f13 (int x) { static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } } - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } return x; } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C index bb60a8ee91b..b2c98853882 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept; constexpr bool foo () { - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } *p = 1; ::operator delete (p); return false; @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; }; constexpr bool bar () { - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } auto q = new (p) S (); q->s++; q->~S (); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C index 216634dc56c..eb66105d7c4 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C @@ -6,7 +6,6 @@ constexpr int foo () try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l:; return 0; @@ -22,7 +21,6 @@ constexpr int bar () { int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l:; try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C index dff59271a1e..fb62ecbfdb5 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C @@ -9,7 +9,7 @@ struct A struct B { A a; - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } } + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } } }; constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C index 7a517a8016c..69eaa7b9b20 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C @@ -7,8 +7,8 @@ struct A { struct D { A i; - bool operator==(const D& x) const = default; // { dg-error "A::operator==" } - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" } + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } }; constexpr D d{A()}; diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C index c962a60c847..19242d15ba8 100644 --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C @@ -1,7 +1,5 @@ // { dg-do compile { target c++11 } } constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } } -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 } constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } } -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 } diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C index 582eedb6d6d..f4c30c0b3f4 100644 --- a/gcc/testsuite/g++.dg/gomp/pr79664.C +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C @@ -1,6 +1,6 @@ // PR c++/79664 // { dg-do compile } -// { dg-options "-std=c++14 -fopenmp" } +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" } constexpr int f1 () diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C index a21d3d60a3a..1efd3f77a55 100644 --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C @@ -1,7 +1,7 @@ // Verify that -fsanitize=vptr downcast instrumentation works properly // inside of constexpr. // { dg-do compile } -// { dg-options "-std=c++11 -fsanitize=vptr" } +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" } struct S { constexpr S() : a(0) {} base-commit: ed1797ddf8285f59a50d9c883beb97705279d980 -- 2.38.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-16 0:30 ` [PATCH v2] " Marek Polacek @ 2022-11-16 13:41 ` Jason Merrill 2022-11-16 16:06 ` [PATCH v3] " Marek Polacek 0 siblings, 1 reply; 9+ messages in thread From: Jason Merrill @ 2022-11-16 13:41 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 11/15/22 19:30, Marek Polacek wrote: > On Mon, Nov 14, 2022 at 06:00:58PM -0500, Jason Merrill wrote: >> On 11/9/22 10:53, Marek Polacek wrote: >>> 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. >>> >>> I also don't love that in C++23, if you don't use -Winvalid-constexpr, >>> and call a constexpr function that in fact isn't constexpr-ready yet, >>> sometimes all you get is an error saying "called in a constant expression" >>> like in constexpr-nonlit12.C. This could be remedied by some tweaks to >>> explain_invalid_constexpr_fn, I reckon (it gives up on !DECL_DEFAULTED_FN). >> >> And also in maybe_save_constexpr_fn: if -Wno-invalid-constexpr, "complain" >> should be false so we save the definition for explain_invalid_constexpr_fn >> to refer to. > > I did something similar: don't return if warn_invalid_constexpr == 0. > I thought I'd just adjust the initializer for 'complain' but due to > constexpr-ice4.C we should call is_valid_constexpr_fn even when > warn_invalid_constexpr is 0. The only non-testsuite changes are to > maybe_save_constexpr_fundef and explain_invalid_constexpr_fn. > > Updated patch below, now we emit a better diagnostic for > constexpr-nonlit12.C even with -std=c++23. And also in C++20 with > -Wno-invalid-constexpr. Great. > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > 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. > --- > gcc/c-family/c-cppbuiltin.cc | 2 +- > gcc/c-family/c-opts.cc | 4 + > gcc/c-family/c.opt | 4 + > gcc/cp/constexpr.cc | 298 ++++++++++++------ > gcc/cp/cp-tree.h | 1 + > gcc/cp/method.cc | 8 +- > gcc/doc/invoke.texi | 28 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +- > .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 3 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +- > .../g++.dg/cpp0x/constexpr-generated1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 4 +- > .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +- > .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +- > .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +- > .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +- > .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +- > .../g++.dg/cpp23/constexpr-nonlit10.C | 96 ++++++ > .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++ > .../g++.dg/cpp23/constexpr-nonlit12.C | 24 ++ > .../g++.dg/cpp23/constexpr-nonlit13.C | 14 + > .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++ > .../g++.dg/cpp23/constexpr-nonlit15.C | 43 +++ > .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++ > .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +- > .../g++.dg/cpp23/constexpr-nonlit6.C | 13 +- > .../g++.dg/cpp23/constexpr-nonlit8.C | 96 ++++++ > .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++ > gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- > gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 - > gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +- > gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 - > .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +- > gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +- > gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 - > gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +- > gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +- > 47 files changed, 722 insertions(+), 146 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > > diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc > index f0be0598190..26447d02097 100644 > --- a/gcc/c-family/c-cppbuiltin.cc > +++ b/gcc/c-family/c-cppbuiltin.cc > @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) > /* Set feature test macros for C++23. */ > cpp_define (pfile, "__cpp_size_t_suffix=202011L"); > cpp_define (pfile, "__cpp_if_consteval=202106L"); > - cpp_define (pfile, "__cpp_constexpr=202110L"); > + cpp_define (pfile, "__cpp_constexpr=202207L"); > cpp_define (pfile, "__cpp_multidimensional_subscript=202211L"); > cpp_define (pfile, "__cpp_named_character_escapes=202207L"); > cpp_define (pfile, "__cpp_static_call_operator=202207L"); > diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc > index bc1f85e3b85..70745aa4e7c 100644 > --- a/gcc/c-family/c-opts.cc > +++ b/gcc/c-family/c-opts.cc > @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename) > if (flag_sized_deallocation == -1) > flag_sized_deallocation = (cxx_dialect >= cxx14); > > + /* Pedwarn about invalid constexpr functions before C++23. */ > + if (warn_invalid_constexpr == -1) > + warn_invalid_constexpr = (cxx_dialect < cxx23); > + > /* char8_t support is implicitly enabled in C++20 and C2X. */ > if (flag_char8_t == -1) > flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x; > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > index 63a300ecd7c..3daeab85531 100644 > --- a/gcc/c-family/c.opt > +++ b/gcc/c-family/c.opt > @@ -817,6 +817,10 @@ Wint-to-pointer-cast > C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning > Warn when there is a cast to a pointer from an integer of a different size. > > +Winvalid-constexpr > +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning > +Warn when a function never produces a constant expression. > + > Winvalid-offsetof > C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning > Warn about invalid uses of the \"offsetof\" macro. > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index e665839f5b1..20d70d0c327 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,26 @@ 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. */ The "if it comes from a constexpr template" wording has needed an update for a while now. > 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>. */ As I commented on the PR, I don't think this is actually a bug, so let's omit this FIXME. > +constexpr void > +g () > +{ > + S a; > + S b; > + b = a; > + b = S{}; > + > + U u, v; > + u = v; > + u = U{}; > +} > + > +static_assert ((g(), true), ""); > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > new file mode 100644 > index 00000000000..a6c4d19ffc6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > @@ -0,0 +1,23 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++20 } } > +// { dg-options "" } > + > +template <typename T> > +struct Wrapper { > + constexpr Wrapper() = default; > + constexpr Wrapper(Wrapper const&) = default; > + constexpr Wrapper(T const& t) : t(t) { } > + > + constexpr T get() const { return t; } > + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } } > +private: > + T t; > +}; > + > +struct X { > + X(); > + bool operator==(X const&) const; > +}; > + > +Wrapper<X> x; > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > index 3b5585dcd84..2238db91157 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > @@ -5,6 +5,6 @@ constexpr int > foo () > { > goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } > +lab: > return 1; > } > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > index fbeb83075b0..a1436938318 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > @@ -18,8 +18,19 @@ bar () > } > > constexpr int > -baz (int x) > +baz () > { > thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" } > return ++a; > } > + > +// In C++23, we get errors about the non-constant expressions only if we > +// actually call the functions in a constexpr context. > + > +void > +test () > +{ > + constexpr int a = foo (); // { dg-error "constant expression" } > + constexpr int b = bar (); // { dg-error "constant expression" } > + constexpr int c = baz (); // { dg-error "constant expression" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > new file mode 100644 > index 00000000000..3fb1b93bd07 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > @@ -0,0 +1,96 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++14 } } > +// { dg-options "" } > + > +// 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" "" { target c++20_down } } > +{ > + return NonLiteral{}; > +} > + > +constexpr int > +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } } > +{ > + return 42; > +} > + > +// From P2448. > +void f(int& i) { > + i = 0; > +} > + > +constexpr void g(int& i) { > + f(i); // { dg-warning "call to" "" { target c++20_down } } > +} > + > +// [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" "" { target c++20_down } } > + // 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" "" { target c++20_down } } > +} > + > +constexpr unsigned int > +fn5 (int *p) > +{ > + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } } > + return *q; > +} > + > +constexpr int > +fn6 (int i) > +{ > + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } } > + return 42; > +} > + > +constexpr int > +fn7 (int i) > +{ > + static int s = i; // { dg-error "static" "" { target c++20_down } } > + return s; > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > new file mode 100644 > index 00000000000..228e90f14c1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > @@ -0,0 +1,53 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++14 } } > +// { dg-options "" } > + > +// [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" "" { target c++20_down } } > + S(int, int) { } > + // Target constructor isn't constexpr. > + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } } > +}; > + > +namespace N1 { > +struct X { > + void x(); > +}; > +struct Y { > + X x; > + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } } > +}; > +} > + > +void g(); > + > +struct A { > + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } } > +}; > + > +struct B { > + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } > + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } > +}; > diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > index 85e66871fd1..3ce36e45cb7 100644 > --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > @@ -134,8 +134,8 @@ > > #ifndef __cpp_constexpr > # error "__cpp_constexpr" > -#elif __cpp_constexpr != 202110 > -# error "__cpp_constexpr != 202110" > +#elif __cpp_constexpr != 202207 > +# error "__cpp_constexpr != 202207" > #endif > > #ifndef __cpp_decltype_auto > diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C > index 83463868668..627ab142d5a 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C > @@ -57,7 +57,6 @@ consteval int > f13 (int x) > { > static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } } > - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } > return x; > } > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > index bb60a8ee91b..b2c98853882 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept; > constexpr bool > foo () > { > - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } > + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } > *p = 1; > ::operator delete (p); > return false; > @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; }; > constexpr bool > bar () > { > - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } > + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } > auto q = new (p) S (); > q->s++; > q->~S (); > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > index 216634dc56c..eb66105d7c4 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > @@ -6,7 +6,6 @@ constexpr int foo () > try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } > - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l:; > return 0; > @@ -22,7 +21,6 @@ constexpr int bar () > { > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } > - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l:; > try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > index dff59271a1e..fb62ecbfdb5 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > @@ -9,7 +9,7 @@ struct A > struct B > { > A a; > - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } } > + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } } > }; > > constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > index 7a517a8016c..69eaa7b9b20 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > @@ -7,8 +7,8 @@ struct A { > struct D > { > A i; > - bool operator==(const D& x) const = default; // { dg-error "A::operator==" } > - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" } > + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } > + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } > }; > > constexpr D d{A()}; > diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > index c962a60c847..19242d15ba8 100644 > --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > @@ -1,7 +1,5 @@ > // { dg-do compile { target c++11 } } > > constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } } > -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 } > > constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } } > -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 } > diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C > index 582eedb6d6d..f4c30c0b3f4 100644 > --- a/gcc/testsuite/g++.dg/gomp/pr79664.C > +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C > @@ -1,6 +1,6 @@ > // PR c++/79664 > // { dg-do compile } > -// { dg-options "-std=c++14 -fopenmp" } > +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" } > > constexpr int > f1 () > diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C > index a21d3d60a3a..1efd3f77a55 100644 > --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C > +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C > @@ -1,7 +1,7 @@ > // Verify that -fsanitize=vptr downcast instrumentation works properly > // inside of constexpr. > // { dg-do compile } > -// { dg-options "-std=c++11 -fsanitize=vptr" } > +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" } > > struct S { > constexpr S() : a(0) {} > > base-commit: ed1797ddf8285f59a50d9c883beb97705279d980 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-16 13:41 ` Jason Merrill @ 2022-11-16 16:06 ` Marek Polacek 2022-11-16 20:27 ` Jason Merrill 0 siblings, 1 reply; 9+ messages in thread From: Marek Polacek @ 2022-11-16 16:06 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote: > On 11/15/22 19:30, Marek Polacek wrote: > > @@ -996,19 +1040,26 @@ 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. */ > > The "if it comes from a constexpr template" wording has needed an update for > a while now. Probably ever since r178519. I've added "Otherwise, explain why the function cannot be used in a constexpr context." Is that acceptable? > > --- /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>. */ > > As I commented on the PR, I don't think this is actually a bug, so let's > omit this FIXME. I'm glad I didn't really attempt to "fix" it (the inform message is flawed and should be improved). Thanks for taking a look. Here's a version with the two comments updated. Ok? -- >8 -- 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. --- gcc/c-family/c-cppbuiltin.cc | 2 +- gcc/c-family/c-opts.cc | 4 + gcc/c-family/c.opt | 4 + gcc/cp/constexpr.cc | 299 ++++++++++++------ gcc/cp/cp-tree.h | 1 + gcc/cp/method.cc | 8 +- gcc/doc/invoke.texi | 28 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +- .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +- gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 3 +- gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +- .../g++.dg/cpp0x/constexpr-generated1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +- gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +- gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 4 +- .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +- .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +- gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +- .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +- .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +- .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +- gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +- .../g++.dg/cpp23/constexpr-nonlit10.C | 96 ++++++ .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++ .../g++.dg/cpp23/constexpr-nonlit12.C | 24 ++ .../g++.dg/cpp23/constexpr-nonlit13.C | 14 + .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++ .../g++.dg/cpp23/constexpr-nonlit15.C | 35 ++ .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++ .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +- .../g++.dg/cpp23/constexpr-nonlit6.C | 13 +- .../g++.dg/cpp23/constexpr-nonlit8.C | 96 ++++++ .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 - gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 - .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +- gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +- gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 - gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +- gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +- 47 files changed, 715 insertions(+), 146 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index f0be0598190..26447d02097 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++23. */ cpp_define (pfile, "__cpp_size_t_suffix=202011L"); cpp_define (pfile, "__cpp_if_consteval=202106L"); - cpp_define (pfile, "__cpp_constexpr=202110L"); + cpp_define (pfile, "__cpp_constexpr=202207L"); cpp_define (pfile, "__cpp_multidimensional_subscript=202211L"); cpp_define (pfile, "__cpp_named_character_escapes=202207L"); cpp_define (pfile, "__cpp_static_call_operator=202207L"); diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index bc1f85e3b85..70745aa4e7c 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename) if (flag_sized_deallocation == -1) flag_sized_deallocation = (cxx_dialect >= cxx14); + /* Pedwarn about invalid constexpr functions before C++23. */ + if (warn_invalid_constexpr == -1) + warn_invalid_constexpr = (cxx_dialect < cxx23); + /* char8_t support is implicitly enabled in C++20 and C2X. */ if (flag_char8_t == -1) flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x; diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 63a300ecd7c..3daeab85531 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -817,6 +817,10 @@ Wint-to-pointer-cast C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning Warn when there is a cast to a pointer from an integer of a different size. +Winvalid-constexpr +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning +Warn when a function never produces a constant expression. + Winvalid-offsetof C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning Warn about invalid uses of the \"offsetof\" macro. diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index e665839f5b1..a390cf921f3 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 07f96ea861f..811a8345712 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 96f0b6d08bd..31d031cd25c 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..aa35fa88189 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C @@ -0,0 +1,35 @@ +// 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; + S& operator=(S&&) = default; +}; + +struct U { + constexpr U& operator=(const U&) = default; + constexpr U& operator=(U&&) = default; +}; + +constexpr void +g () +{ + S a; + S b; + b = a; + b = S{}; + + U u, v; + u = v; + u = U{}; +} + +static_assert ((g(), true), ""); diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C new file mode 100644 index 00000000000..a6c4d19ffc6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C @@ -0,0 +1,23 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++20 } } +// { dg-options "" } + +template <typename T> +struct Wrapper { + constexpr Wrapper() = default; + constexpr Wrapper(Wrapper const&) = default; + constexpr Wrapper(T const& t) : t(t) { } + + constexpr T get() const { return t; } + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } } +private: + T t; +}; + +struct X { + X(); + bool operator==(X const&) const; +}; + +Wrapper<X> x; diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C index 3b5585dcd84..2238db91157 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C @@ -5,6 +5,6 @@ constexpr int foo () { goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } +lab: return 1; } diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C index fbeb83075b0..a1436938318 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C @@ -18,8 +18,19 @@ bar () } constexpr int -baz (int x) +baz () { thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" } return ++a; } + +// In C++23, we get errors about the non-constant expressions only if we +// actually call the functions in a constexpr context. + +void +test () +{ + constexpr int a = foo (); // { dg-error "constant expression" } + constexpr int b = bar (); // { dg-error "constant expression" } + constexpr int c = baz (); // { dg-error "constant expression" } +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C new file mode 100644 index 00000000000..3fb1b93bd07 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C @@ -0,0 +1,96 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++14 } } +// { dg-options "" } + +// 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" "" { target c++20_down } } +{ + return NonLiteral{}; +} + +constexpr int +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } } +{ + return 42; +} + +// From P2448. +void f(int& i) { + i = 0; +} + +constexpr void g(int& i) { + f(i); // { dg-warning "call to" "" { target c++20_down } } +} + +// [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" "" { target c++20_down } } + // 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" "" { target c++20_down } } +} + +constexpr unsigned int +fn5 (int *p) +{ + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } } + return *q; +} + +constexpr int +fn6 (int i) +{ + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } } + return 42; +} + +constexpr int +fn7 (int i) +{ + static int s = i; // { dg-error "static" "" { target c++20_down } } + return s; +} diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C new file mode 100644 index 00000000000..228e90f14c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C @@ -0,0 +1,53 @@ +// PR c++/106649 +// P2448 - Relaxing some constexpr restrictions +// { dg-do compile { target c++14 } } +// { dg-options "" } + +// [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" "" { target c++20_down } } + S(int, int) { } + // Target constructor isn't constexpr. + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } } +}; + +namespace N1 { +struct X { + void x(); +}; +struct Y { + X x; + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } } +}; +} + +void g(); + +struct A { + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } } +}; + +struct B { + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index 85e66871fd1..3ce36e45cb7 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -134,8 +134,8 @@ #ifndef __cpp_constexpr # error "__cpp_constexpr" -#elif __cpp_constexpr != 202110 -# error "__cpp_constexpr != 202110" +#elif __cpp_constexpr != 202207 +# error "__cpp_constexpr != 202207" #endif #ifndef __cpp_decltype_auto diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C index 83463868668..627ab142d5a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -57,7 +57,6 @@ consteval int f13 (int x) { static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } } - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } return x; } diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C index bb60a8ee91b..b2c98853882 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept; constexpr bool foo () { - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } *p = 1; ::operator delete (p); return false; @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; }; constexpr bool bar () { - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } auto q = new (p) S (); q->s++; q->~S (); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C index 216634dc56c..eb66105d7c4 100644 --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C @@ -6,7 +6,6 @@ constexpr int foo () try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l:; return 0; @@ -22,7 +21,6 @@ constexpr int bar () { int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l:; try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C index dff59271a1e..fb62ecbfdb5 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C @@ -9,7 +9,7 @@ struct A struct B { A a; - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } } + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } } }; constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C index 7a517a8016c..69eaa7b9b20 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C @@ -7,8 +7,8 @@ struct A { struct D { A i; - bool operator==(const D& x) const = default; // { dg-error "A::operator==" } - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" } + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } }; constexpr D d{A()}; diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C index c962a60c847..19242d15ba8 100644 --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C @@ -1,7 +1,5 @@ // { dg-do compile { target c++11 } } constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } } -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 } constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } } -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 } diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C index 582eedb6d6d..f4c30c0b3f4 100644 --- a/gcc/testsuite/g++.dg/gomp/pr79664.C +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C @@ -1,6 +1,6 @@ // PR c++/79664 // { dg-do compile } -// { dg-options "-std=c++14 -fopenmp" } +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" } constexpr int f1 () diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C index a21d3d60a3a..1efd3f77a55 100644 --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C @@ -1,7 +1,7 @@ // Verify that -fsanitize=vptr downcast instrumentation works properly // inside of constexpr. // { dg-do compile } -// { dg-options "-std=c++11 -fsanitize=vptr" } +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" } struct S { constexpr S() : a(0) {} base-commit: 7026d0455dce1092975d4884f450a12a6ed205c7 -- 2.38.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-16 16:06 ` [PATCH v3] " Marek Polacek @ 2022-11-16 20:27 ` Jason Merrill 2022-11-19 2:26 ` Jason Merrill 0 siblings, 1 reply; 9+ messages in thread From: Jason Merrill @ 2022-11-16 20:27 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 11/16/22 11:06, Marek Polacek wrote: > On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote: >> On 11/15/22 19:30, Marek Polacek wrote: >>> @@ -996,19 +1040,26 @@ 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. */ >> >> The "if it comes from a constexpr template" wording has needed an update for >> a while now. > > Probably ever since r178519. I've added "Otherwise, explain why the function > cannot be used in a constexpr context." Is that acceptable? > >>> --- /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>. */ >> >> As I commented on the PR, I don't think this is actually a bug, so let's >> omit this FIXME. > > I'm glad I didn't really attempt to "fix" it (the inform message is flawed > and should be improved). Thanks for taking a look. > > Here's a version with the two comments updated. > > Ok? OK. > -- >8 -- > 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. > --- > gcc/c-family/c-cppbuiltin.cc | 2 +- > gcc/c-family/c-opts.cc | 4 + > gcc/c-family/c.opt | 4 + > gcc/cp/constexpr.cc | 299 ++++++++++++------ > gcc/cp/cp-tree.h | 1 + > gcc/cp/method.cc | 8 +- > gcc/doc/invoke.texi | 28 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ctor2.C | 2 +- > .../g++.dg/cpp0x/constexpr-default-ctor.C | 6 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 3 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-friend.C | 2 +- > .../g++.dg/cpp0x/constexpr-generated1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ice5.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-ice6.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-memfn1.C | 4 +- > gcc/testsuite/g++.dg/cpp0x/constexpr-neg2.C | 4 +- > .../g++.dg/cpp0x/constexpr-non-const-arg.C | 2 +- > .../g++.dg/cpp0x/constexpr-reinterpret1.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/pr65327.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-105050.C | 2 +- > .../g++.dg/cpp1y/constexpr-89285-2.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C | 2 +- > .../g++.dg/cpp1y/constexpr-89785-2.C | 4 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C | 2 +- > .../g++.dg/cpp1y/constexpr-nsdmi7b.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-throw.C | 6 +- > .../g++.dg/cpp23/constexpr-nonlit10.C | 96 ++++++ > .../g++.dg/cpp23/constexpr-nonlit11.C | 53 ++++ > .../g++.dg/cpp23/constexpr-nonlit12.C | 24 ++ > .../g++.dg/cpp23/constexpr-nonlit13.C | 14 + > .../g++.dg/cpp23/constexpr-nonlit14.C | 26 ++ > .../g++.dg/cpp23/constexpr-nonlit15.C | 35 ++ > .../g++.dg/cpp23/constexpr-nonlit16.C | 23 ++ > .../g++.dg/cpp23/constexpr-nonlit3.C | 2 +- > .../g++.dg/cpp23/constexpr-nonlit6.C | 13 +- > .../g++.dg/cpp23/constexpr-nonlit8.C | 96 ++++++ > .../g++.dg/cpp23/constexpr-nonlit9.C | 53 ++++ > gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- > gcc/testsuite/g++.dg/cpp2a/consteval3.C | 1 - > gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C | 4 +- > gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C | 2 - > .../g++.dg/cpp2a/spaceship-constexpr1.C | 2 +- > gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 4 +- > gcc/testsuite/g++.dg/diagnostic/constexpr1.C | 2 - > gcc/testsuite/g++.dg/gomp/pr79664.C | 2 +- > gcc/testsuite/g++.dg/ubsan/vptr-4.C | 2 +- > 47 files changed, 715 insertions(+), 146 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > > diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc > index f0be0598190..26447d02097 100644 > --- a/gcc/c-family/c-cppbuiltin.cc > +++ b/gcc/c-family/c-cppbuiltin.cc > @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) > /* Set feature test macros for C++23. */ > cpp_define (pfile, "__cpp_size_t_suffix=202011L"); > cpp_define (pfile, "__cpp_if_consteval=202106L"); > - cpp_define (pfile, "__cpp_constexpr=202110L"); > + cpp_define (pfile, "__cpp_constexpr=202207L"); > cpp_define (pfile, "__cpp_multidimensional_subscript=202211L"); > cpp_define (pfile, "__cpp_named_character_escapes=202207L"); > cpp_define (pfile, "__cpp_static_call_operator=202207L"); > diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc > index bc1f85e3b85..70745aa4e7c 100644 > --- a/gcc/c-family/c-opts.cc > +++ b/gcc/c-family/c-opts.cc > @@ -1059,6 +1059,10 @@ c_common_post_options (const char **pfilename) > if (flag_sized_deallocation == -1) > flag_sized_deallocation = (cxx_dialect >= cxx14); > > + /* Pedwarn about invalid constexpr functions before C++23. */ > + if (warn_invalid_constexpr == -1) > + warn_invalid_constexpr = (cxx_dialect < cxx23); > + > /* char8_t support is implicitly enabled in C++20 and C2X. */ > if (flag_char8_t == -1) > flag_char8_t = (cxx_dialect >= cxx20) || flag_isoc2x; > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > index 63a300ecd7c..3daeab85531 100644 > --- a/gcc/c-family/c.opt > +++ b/gcc/c-family/c.opt > @@ -817,6 +817,10 @@ Wint-to-pointer-cast > C ObjC C++ ObjC++ Var(warn_int_to_pointer_cast) Init(1) Warning > Warn when there is a cast to a pointer from an integer of a different size. > > +Winvalid-constexpr > +C++ ObjC++ Var(warn_invalid_constexpr) Init(-1) Warning > +Warn when a function never produces a constant expression. > + > Winvalid-offsetof > C++ ObjC++ Var(warn_invalid_offsetof) Init(1) Warning > Warn about invalid uses of the \"offsetof\" macro. > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index e665839f5b1..a390cf921f3 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 07f96ea861f..811a8345712 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 96f0b6d08bd..31d031cd25c 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..aa35fa88189 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C > @@ -0,0 +1,35 @@ > +// 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; > + S& operator=(S&&) = default; > +}; > + > +struct U { > + constexpr U& operator=(const U&) = default; > + constexpr U& operator=(U&&) = default; > +}; > + > +constexpr void > +g () > +{ > + S a; > + S b; > + b = a; > + b = S{}; > + > + U u, v; > + u = v; > + u = U{}; > +} > + > +static_assert ((g(), true), ""); > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > new file mode 100644 > index 00000000000..a6c4d19ffc6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C > @@ -0,0 +1,23 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++20 } } > +// { dg-options "" } > + > +template <typename T> > +struct Wrapper { > + constexpr Wrapper() = default; > + constexpr Wrapper(Wrapper const&) = default; > + constexpr Wrapper(T const& t) : t(t) { } > + > + constexpr T get() const { return t; } > + constexpr bool operator==(Wrapper const&) const = default; // { dg-warning "call to" "" { target c++20_down } } > +private: > + T t; > +}; > + > +struct X { > + X(); > + bool operator==(X const&) const; > +}; > + > +Wrapper<X> x; > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > index 3b5585dcd84..2238db91157 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C > @@ -5,6 +5,6 @@ constexpr int > foo () > { > goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > -lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } > +lab: > return 1; > } > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > index fbeb83075b0..a1436938318 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit6.C > @@ -18,8 +18,19 @@ bar () > } > > constexpr int > -baz (int x) > +baz () > { > thread_local int a; // { dg-error "'a' defined 'thread_local' in 'constexpr' context" } > return ++a; > } > + > +// In C++23, we get errors about the non-constant expressions only if we > +// actually call the functions in a constexpr context. > + > +void > +test () > +{ > + constexpr int a = foo (); // { dg-error "constant expression" } > + constexpr int b = bar (); // { dg-error "constant expression" } > + constexpr int c = baz (); // { dg-error "constant expression" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > new file mode 100644 > index 00000000000..3fb1b93bd07 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C > @@ -0,0 +1,96 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++14 } } > +// { dg-options "" } > + > +// 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" "" { target c++20_down } } > +{ > + return NonLiteral{}; > +} > + > +constexpr int > +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target c++20_down } } > +{ > + return 42; > +} > + > +// From P2448. > +void f(int& i) { > + i = 0; > +} > + > +constexpr void g(int& i) { > + f(i); // { dg-warning "call to" "" { target c++20_down } } > +} > + > +// [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" "" { target c++20_down } } > + // 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" "" { target c++20_down } } > +} > + > +constexpr unsigned int > +fn5 (int *p) > +{ > + unsigned int *q = reinterpret_cast<unsigned int *>(p); // { dg-warning "reinterpret_cast" "" { target c++20_down } } > + return *q; > +} > + > +constexpr int > +fn6 (int i) > +{ > + void *p = (void *) 1LL; // { dg-warning ".reinterpret_cast. from integer to pointer" "" { target c++20_down } } > + return 42; > +} > + > +constexpr int > +fn7 (int i) > +{ > + static int s = i; // { dg-error "static" "" { target c++20_down } } > + return s; > +} > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > new file mode 100644 > index 00000000000..228e90f14c1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C > @@ -0,0 +1,53 @@ > +// PR c++/106649 > +// P2448 - Relaxing some constexpr restrictions > +// { dg-do compile { target c++14 } } > +// { dg-options "" } > + > +// [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" "" { target c++20_down } } > + S(int, int) { } > + // Target constructor isn't constexpr. > + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target c++20_down } } > +}; > + > +namespace N1 { > +struct X { > + void x(); > +}; > +struct Y { > + X x; > + constexpr void y() { x.x(); } // { dg-warning "call to" "" { target c++20_down } } > +}; > +} > + > +void g(); > + > +struct A { > + constexpr A() { g(); } // { dg-warning "call to" "" { target c++20_down } } > +}; > + > +struct B { > + constexpr B& operator=(const B&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } > + constexpr B& operator=(B&&) { g(); return *this; } // { dg-warning "call to" "" { target c++20_down } } > +}; > diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > index 85e66871fd1..3ce36e45cb7 100644 > --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C > @@ -134,8 +134,8 @@ > > #ifndef __cpp_constexpr > # error "__cpp_constexpr" > -#elif __cpp_constexpr != 202110 > -# error "__cpp_constexpr != 202110" > +#elif __cpp_constexpr != 202207 > +# error "__cpp_constexpr != 202207" > #endif > > #ifndef __cpp_decltype_auto > diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C > index 83463868668..627ab142d5a 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/consteval3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C > @@ -57,7 +57,6 @@ consteval int > f13 (int x) > { > static int a = 5; // { dg-error "'a' defined 'static' in 'consteval' function only available with" "" { target c++20_only } } > - // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > thread_local int b = 6; // { dg-error "'b' defined 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } > return x; > } > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > index bb60a8ee91b..b2c98853882 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new7.C > @@ -13,7 +13,7 @@ void *operator new (std::size_t) noexcept; > constexpr bool > foo () > { > - auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" } > + auto p = static_cast<int *> (::operator new (sizeof (int))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } > *p = 1; > ::operator delete (p); > return false; > @@ -24,7 +24,7 @@ struct S { constexpr S () : s (0) {} int s; }; > constexpr bool > bar () > { > - auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" } > + auto p = static_cast<S *> (::operator new (sizeof (S))); // { dg-error "call to non-'constexpr' function" "" { target c++20_down } } > auto q = new (p) S (); > q->s++; > q->~S (); > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > index 216634dc56c..eb66105d7c4 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C > @@ -6,7 +6,6 @@ constexpr int foo () > try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > static double b = 1.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } > - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l:; > return 0; > @@ -22,7 +21,6 @@ constexpr int bar () > { > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > static long double b = 3.0;// { dg-error "'b' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } > - // { dg-error "'b' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } > goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l:; > try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > index dff59271a1e..fb62ecbfdb5 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-constexpr1.C > @@ -9,7 +9,7 @@ struct A > struct B > { > A a; > - bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { ! implicit_constexpr } } } > + bool operator==(const B&) const = default; // { dg-error "A::operator==" "" { target { { ! implicit_constexpr } && c++20_down } } } > }; > > constexpr bool x = B() == B(); // { dg-error "non-.constexpr" "" { target { ! implicit_constexpr } } } > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > index 7a517a8016c..69eaa7b9b20 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > @@ -7,8 +7,8 @@ struct A { > struct D > { > A i; > - bool operator==(const D& x) const = default; // { dg-error "A::operator==" } > - bool operator!=(const D& z) const = default; // { dg-error "D::operator==" } > + bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } > + bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } > }; > > constexpr D d{A()}; > diff --git a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > index c962a60c847..19242d15ba8 100644 > --- a/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > +++ b/gcc/testsuite/g++.dg/diagnostic/constexpr1.C > @@ -1,7 +1,5 @@ > // { dg-do compile { target c++11 } } > > constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. defined .thread_local." "" { target c++20_down } } > -// { dg-error "40:.i. defined .thread_local. in .constexpr. context" "" { target c++23 } .-1 } > > constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. defined .static." "" { target c++20_down } } > -// { dg-error "34:.i. defined .static. in .constexpr. context" "" { target c++23 } .-1 } > diff --git a/gcc/testsuite/g++.dg/gomp/pr79664.C b/gcc/testsuite/g++.dg/gomp/pr79664.C > index 582eedb6d6d..f4c30c0b3f4 100644 > --- a/gcc/testsuite/g++.dg/gomp/pr79664.C > +++ b/gcc/testsuite/g++.dg/gomp/pr79664.C > @@ -1,6 +1,6 @@ > // PR c++/79664 > // { dg-do compile } > -// { dg-options "-std=c++14 -fopenmp" } > +// { dg-options "-std=c++14 -fopenmp -Winvalid-constexpr -pedantic-errors" } > > constexpr int > f1 () > diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-4.C b/gcc/testsuite/g++.dg/ubsan/vptr-4.C > index a21d3d60a3a..1efd3f77a55 100644 > --- a/gcc/testsuite/g++.dg/ubsan/vptr-4.C > +++ b/gcc/testsuite/g++.dg/ubsan/vptr-4.C > @@ -1,7 +1,7 @@ > // Verify that -fsanitize=vptr downcast instrumentation works properly > // inside of constexpr. > // { dg-do compile } > -// { dg-options "-std=c++11 -fsanitize=vptr" } > +// { dg-options "-std=c++11 -fsanitize=vptr -Winvalid-constexpr -pedantic-errors" } > > struct S { > constexpr S() : a(0) {} > > base-commit: 7026d0455dce1092975d4884f450a12a6ed205c7 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-16 20:27 ` Jason Merrill @ 2022-11-19 2:26 ` Jason Merrill 2022-12-02 19:48 ` Marek Polacek 0 siblings, 1 reply; 9+ messages in thread From: Jason Merrill @ 2022-11-19 2:26 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 11/16/22 15:27, Jason Merrill wrote: > On 11/16/22 11:06, Marek Polacek wrote: >> On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote: >>> On 11/15/22 19:30, Marek Polacek wrote: >>>> @@ -996,19 +1040,26 @@ 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. */ >>> >>> The "if it comes from a constexpr template" wording has needed an >>> update for >>> a while now. >> >> Probably ever since r178519. I've added "Otherwise, explain why the >> function >> cannot be used in a constexpr context." Is that acceptable? >>>> --- /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>. */ >>> >>> As I commented on the PR, I don't think this is actually a bug, so let's >>> omit this FIXME. >> >> I'm glad I didn't really attempt to "fix" it (the inform message is >> flawed >> and should be improved). Thanks for taking a look. >> >> Here's a version with the two comments updated. >> >> Ok? > > OK. Since this patch I'm seeing these failures: FAIL: g++.dg/cpp0x/constexpr-ex1.C -std=c++23 -fimplicit-constexpr at line 91 (test for errors, line 89) FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr (test for warnings, line 14) FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr (test for warnings, line 20) FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr (test for warnings, line 28) FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr (test for warnings, line 31) FAIL: g++.dg/cpp2a/spaceship-eq3.C -std=c++23 -fimplicit-constexpr (test for excess errors) Jason ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-11-19 2:26 ` Jason Merrill @ 2022-12-02 19:48 ` Marek Polacek 2022-12-02 20:01 ` Jason Merrill 0 siblings, 1 reply; 9+ messages in thread From: Marek Polacek @ 2022-12-02 19:48 UTC (permalink / raw) To: Jason Merrill; +Cc: GCC Patches On Fri, Nov 18, 2022 at 09:26:26PM -0500, Jason Merrill wrote: > On 11/16/22 15:27, Jason Merrill wrote: > > On 11/16/22 11:06, Marek Polacek wrote: > > > On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote: > > > > On 11/15/22 19:30, Marek Polacek wrote: > > > > > @@ -996,19 +1040,26 @@ 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. */ > > > > > > > > The "if it comes from a constexpr template" wording has needed > > > > an update for > > > > a while now. > > > > > > Probably ever since r178519. I've added "Otherwise, explain why the > > > function > > > cannot be used in a constexpr context." Is that acceptable? > > > > > --- /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>. */ > > > > > > > > As I commented on the PR, I don't think this is actually a bug, so let's > > > > omit this FIXME. > > > > > > I'm glad I didn't really attempt to "fix" it (the inform message is > > > flawed > > > and should be improved). Thanks for taking a look. > > > > > > Here's a version with the two comments updated. > > > > > > Ok? > > > > OK. > > Since this patch I'm seeing these failures: > > FAIL: g++.dg/cpp0x/constexpr-ex1.C -std=c++23 -fimplicit-constexpr at line > 91 (test for errors, line 89) > FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr > (test for warnings, line 14) > FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr > (test for warnings, line 20) > FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr > (test for warnings, line 28) > FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr > (test for warnings, line 31) > FAIL: g++.dg/cpp2a/spaceship-eq3.C -std=c++23 -fimplicit-constexpr (test > for excess errors) Ah, sorry. The following patch fixes those fails. Ok? -- >8 -- Some of the new tests were failing with -fimplicit-constexpr. This patch adjusts the expected diagnostic. Tested with GXX_TESTSUITE_STDS=98,11,14,17,20,23 make check-c++ RUNTESTFLAGS="--target_board=unix\{,-fimplicit-constexpr\} dg.exp=spaceship-eq3.C" gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-ex1.C: Adjust dg-error. * g++.dg/cpp23/constexpr-nonlit10.C: Adjust dg-warning. * g++.dg/cpp23/constexpr-nonlit11.C: Likewise. * g++.dg/cpp2a/spaceship-eq3.C: Add dg-error. --- gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 6 +++--- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C | 4 ++-- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C | 4 ++-- gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C index 48281a47784..383d38a42d4 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C @@ -87,8 +87,8 @@ struct resource { } }; constexpr resource f(resource d) -{ 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 } } } +{ return d; } // { dg-error "non-.constexpr." "" { target { { { ! implicit_constexpr } && c++20_down } || c++11_only } } } +// { dg-error "non-.constexpr." "" { target { c++23 && { ! implicit_constexpr } } } .-2 } +constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { { ! implicit_constexpr } || c++11_only } } } // 4.4 floating-point constant expressions diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C index 48706f7b66e..31d4b873bbf 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C @@ -11,13 +11,13 @@ struct 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" } +fn0 (int) // { dg-warning "invalid return type" "" { target { ! implicit_constexpr } } } { return NonLiteral{}; } constexpr int -fn1 (NonLiteral) // { dg-warning "invalid type" } +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target { ! implicit_constexpr } } } { return 42; } diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C index a7114bc66cb..e08809f873c 100644 --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C @@ -25,10 +25,10 @@ struct X { struct S { X x; // Calls a non-constexpr constructor X::X(int). - constexpr S(int i) : x(i) { } // { dg-warning "call to" } + constexpr S(int i) : x(i) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } } S(int, int) { } // Target constructor isn't constexpr. - constexpr S() : S(42, 42) { } // { dg-warning "call to" } + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } } }; namespace N1 { diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C index 69eaa7b9b20..246839f1f95 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C @@ -9,6 +9,7 @@ struct D A i; bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } +// { dg-error "called" "" { target { c++23 && implicit_constexpr } } .-1 } }; constexpr D d{A()}; base-commit: d19aa6af6634b1e97f38431ad091f3b3f12baf2f -- 2.38.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] c++: P2448 - Relaxing some constexpr restrictions [PR106649] 2022-12-02 19:48 ` Marek Polacek @ 2022-12-02 20:01 ` Jason Merrill 0 siblings, 0 replies; 9+ messages in thread From: Jason Merrill @ 2022-12-02 20:01 UTC (permalink / raw) To: Marek Polacek; +Cc: GCC Patches On 12/2/22 14:48, Marek Polacek wrote: > On Fri, Nov 18, 2022 at 09:26:26PM -0500, Jason Merrill wrote: >> On 11/16/22 15:27, Jason Merrill wrote: >>> On 11/16/22 11:06, Marek Polacek wrote: >>>> On Wed, Nov 16, 2022 at 08:41:53AM -0500, Jason Merrill wrote: >>>>> On 11/15/22 19:30, Marek Polacek wrote: >>>>>> @@ -996,19 +1040,26 @@ 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. */ >>>>> >>>>> The "if it comes from a constexpr template" wording has needed >>>>> an update for >>>>> a while now. >>>> >>>> Probably ever since r178519. I've added "Otherwise, explain why the >>>> function >>>> cannot be used in a constexpr context." Is that acceptable? >>>>>> --- /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>. */ >>>>> >>>>> As I commented on the PR, I don't think this is actually a bug, so let's >>>>> omit this FIXME. >>>> >>>> I'm glad I didn't really attempt to "fix" it (the inform message is >>>> flawed >>>> and should be improved). Thanks for taking a look. >>>> >>>> Here's a version with the two comments updated. >>>> >>>> Ok? >>> >>> OK. >> >> Since this patch I'm seeing these failures: >> >> FAIL: g++.dg/cpp0x/constexpr-ex1.C -std=c++23 -fimplicit-constexpr at line >> 91 (test for errors, line 89) >> FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr >> (test for warnings, line 14) >> FAIL: g++.dg/cpp23/constexpr-nonlit10.C -std=gnu++23 -fimplicit-constexpr >> (test for warnings, line 20) >> FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr >> (test for warnings, line 28) >> FAIL: g++.dg/cpp23/constexpr-nonlit11.C -std=gnu++23 -fimplicit-constexpr >> (test for warnings, line 31) >> FAIL: g++.dg/cpp2a/spaceship-eq3.C -std=c++23 -fimplicit-constexpr (test >> for excess errors) > > Ah, sorry. The following patch fixes those fails. > > Ok? OK, thanks. > -- >8 -- > Some of the new tests were failing with -fimplicit-constexpr. This > patch adjusts the expected diagnostic. Tested with > > GXX_TESTSUITE_STDS=98,11,14,17,20,23 make check-c++ RUNTESTFLAGS="--target_board=unix\{,-fimplicit-constexpr\} dg.exp=spaceship-eq3.C" > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/constexpr-ex1.C: Adjust dg-error. > * g++.dg/cpp23/constexpr-nonlit10.C: Adjust dg-warning. > * g++.dg/cpp23/constexpr-nonlit11.C: Likewise. > * g++.dg/cpp2a/spaceship-eq3.C: Add dg-error. > --- > gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C | 6 +++--- > gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C | 4 ++-- > gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C | 4 ++-- > gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C | 1 + > 4 files changed, 8 insertions(+), 7 deletions(-) > > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C > index 48281a47784..383d38a42d4 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ex1.C > @@ -87,8 +87,8 @@ struct resource { > } > }; > constexpr resource f(resource d) > -{ 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 } } } > +{ return d; } // { dg-error "non-.constexpr." "" { target { { { ! implicit_constexpr } && c++20_down } || c++11_only } } } > +// { dg-error "non-.constexpr." "" { target { c++23 && { ! implicit_constexpr } } } .-2 } > +constexpr resource d = f(9); // { dg-message ".constexpr." "" { target { { ! implicit_constexpr } || c++11_only } } } > > // 4.4 floating-point constant expressions > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C > index 48706f7b66e..31d4b873bbf 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C > @@ -11,13 +11,13 @@ struct 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" } > +fn0 (int) // { dg-warning "invalid return type" "" { target { ! implicit_constexpr } } } > { > return NonLiteral{}; > } > > constexpr int > -fn1 (NonLiteral) // { dg-warning "invalid type" } > +fn1 (NonLiteral) // { dg-warning "invalid type" "" { target { ! implicit_constexpr } } } > { > return 42; > } > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C > index a7114bc66cb..e08809f873c 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C > @@ -25,10 +25,10 @@ struct X { > struct S { > X x; > // Calls a non-constexpr constructor X::X(int). > - constexpr S(int i) : x(i) { } // { dg-warning "call to" } > + constexpr S(int i) : x(i) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } } > S(int, int) { } > // Target constructor isn't constexpr. > - constexpr S() : S(42, 42) { } // { dg-warning "call to" } > + constexpr S() : S(42, 42) { } // { dg-warning "call to" "" { target { ! implicit_constexpr } } } > }; > > namespace N1 { > diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > index 69eaa7b9b20..246839f1f95 100644 > --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-eq3.C > @@ -9,6 +9,7 @@ struct D > A i; > bool operator==(const D& x) const = default; // { dg-error "A::operator==" "" { target c++20_down } } > bool operator!=(const D& z) const = default; // { dg-error "D::operator==" "" { target c++20_down } } > +// { dg-error "called" "" { target { c++23 && implicit_constexpr } } .-1 } > }; > > constexpr D d{A()}; > > base-commit: d19aa6af6634b1e97f38431ad091f3b3f12baf2f ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2022-12-02 20:01 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2022-11-09 20:53 [PATCH] c++: P2448 - Relaxing some constexpr restrictions [PR106649] Marek Polacek 2022-11-14 23:00 ` Jason Merrill 2022-11-16 0:30 ` [PATCH v2] " Marek Polacek 2022-11-16 13:41 ` Jason Merrill 2022-11-16 16:06 ` [PATCH v3] " Marek Polacek 2022-11-16 20:27 ` Jason Merrill 2022-11-19 2:26 ` Jason Merrill 2022-12-02 19:48 ` Marek Polacek 2022-12-02 20:01 ` Jason Merrill
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).