mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-05 17:30:58 +08:00
c++: P2448 - Relaxing some constexpr restrictions [PR106649]
This patch implements C++23 P2448, which lifts more restrictions on the constexpr keyword. It's effectively going the way of being just a hint (hello, inline!). This gist is relatively simple: in C++23, a constexpr function's return type/parameter type doesn't have to be a literal type; and you can have a constexpr function for which no invocation satisfies the requirements of a core constant expression. For example, void f(int& i); // not constexpr constexpr void g(int& i) { f(i); // unconditionally calls a non-constexpr function } is now OK, even though there isn't an invocation of 'g' that would be a constant expression. Maybe 'f' will be made constexpr soon, or maybe this depends on the version of C++ used, and similar. The patch is unfortunately not that trivial. The important bit is to use the new require_potential_rvalue_constant_expression_fncheck in maybe_save_constexpr_fundef (and where appropriate). It has a new flag that says that we're checking the body of a constexpr function, and in that case it's OK to find constructs that aren't a constant expression. Since it's useful to be able to check for problematic constructs even in C++23, this patch implements a new warning, -Winvalid-constexpr, which is a pedwarn turned on by default in C++20 and earlier, and which can be turned on in C++23 as well, in which case it's an ordinary warning. This I implemented by using the new function constexpr_error, used in p_c_e_1 and friends. (In some cases I believe fundef_p will be always false (= hard error), but it made sense to me to be consistent and use constexpr_error throughout p_c_e_1.) While working on this I think I found a bug, see constexpr-nonlit15.C and <https://gcc.gnu.org/PR107598>. This patch doesn't address that. This patch includes changes to diagnose the problem if the user doesn't use -Winvalid-constexpr and calls a constexpr function that in fact isn't constexpr-ready yet: maybe_save_constexpr_fundef registers the function if warn_invalid_constexpr is 0 and explain_invalid_constexpr_fn then gives the diagnostic. PR c++/106649 gcc/c-family/ChangeLog: * c-cppbuiltin.cc (c_cpp_builtins): Update value of __cpp_constexpr for C++23. * c-opts.cc (c_common_post_options): Set warn_invalid_constexpr depending on cxx_dialect. * c.opt (Winvalid-constexpr): New option. gcc/cp/ChangeLog: * constexpr.cc (constexpr_error): New function. (is_valid_constexpr_fn): Use constexpr_error. (maybe_save_constexpr_fundef): Call require_potential_rvalue_constant_expression_fncheck rather than require_potential_rvalue_constant_expression. Register the function if -Wno-invalid-constexpr was specified. (explain_invalid_constexpr_fn): Don't return early if a function marked 'constexpr' that isn't actually a constant expression was called. (non_const_var_error): Add a bool parameter. Use constexpr_error. (inline_asm_in_constexpr_error): Likewise. (cxx_eval_constant_expression): Adjust calls to non_const_var_error and inline_asm_in_constexpr_error. (potential_constant_expression_1): Add a bool parameter. Use constexpr_error. (require_potential_rvalue_constant_expression_fncheck): New function. * cp-tree.h (require_potential_rvalue_constant_expression_fncheck): Declare. * method.cc (struct comp_info): Call require_potential_rvalue_constant_expression_fncheck rather than require_potential_rvalue_constant_expression. gcc/ChangeLog: * doc/invoke.texi: Document -Winvalid-constexpr. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/constexpr-ctor2.C: Expect an error in c++20_down only. * g++.dg/cpp0x/constexpr-default-ctor.C: Likewise. * g++.dg/cpp0x/constexpr-diag3.C: Likewise. * g++.dg/cpp0x/constexpr-ex1.C: Likewise. * g++.dg/cpp0x/constexpr-friend.C: Likewise. * g++.dg/cpp0x/constexpr-generated1.C: Likewise. * g++.dg/cpp0x/constexpr-ice5.C: Likewise. * g++.dg/cpp0x/constexpr-ice6.C: Likewise. * g++.dg/cpp0x/constexpr-memfn1.C: Likewise. * g++.dg/cpp0x/constexpr-neg2.C: Likewise. * g++.dg/cpp0x/constexpr-non-const-arg.C: Likewise. * g++.dg/cpp0x/constexpr-reinterpret1.C: Likewise. * g++.dg/cpp0x/pr65327.C: Likewise. * g++.dg/cpp1y/constexpr-105050.C: Likewise. * g++.dg/cpp1y/constexpr-89285-2.C: Likewise. * g++.dg/cpp1y/constexpr-89285.C: Likewise. * g++.dg/cpp1y/constexpr-89785-2.C: Likewise. * g++.dg/cpp1y/constexpr-neg1.C: Likewise. * g++.dg/cpp1y/constexpr-nsdmi7b.C: Likewise. * g++.dg/cpp1y/constexpr-throw.C: Likewise. * g++.dg/cpp23/constexpr-nonlit3.C: Remove dg-error. * g++.dg/cpp23/constexpr-nonlit6.C: Call the test functions. * g++.dg/cpp23/feat-cxx2b.C: Adjust the expected value of __cpp_constexpr. * g++.dg/cpp2a/consteval3.C: Remove dg-error. * g++.dg/cpp2a/constexpr-new7.C: Expect an error in c++20_down only. * g++.dg/cpp2a/constexpr-try5.C: Remove dg-error. * g++.dg/cpp2a/spaceship-constexpr1.C: Expect an error in c++20_down only. * g++.dg/cpp2a/spaceship-eq3.C: Likewise. * g++.dg/diagnostic/constexpr1.C: Remove dg-error. * g++.dg/gomp/pr79664.C: Use -Winvalid-constexpr -pedantic-errors. * g++.dg/ubsan/vptr-4.C: Likewise. * g++.dg/cpp23/constexpr-nonlit10.C: New test. * g++.dg/cpp23/constexpr-nonlit11.C: New test. * g++.dg/cpp23/constexpr-nonlit12.C: New test. * g++.dg/cpp23/constexpr-nonlit13.C: New test. * g++.dg/cpp23/constexpr-nonlit14.C: New test. * g++.dg/cpp23/constexpr-nonlit15.C: New test. * g++.dg/cpp23/constexpr-nonlit16.C: New test. * g++.dg/cpp23/constexpr-nonlit8.C: New test. * g++.dg/cpp23/constexpr-nonlit9.C: New test.
This commit is contained in:
parent
dbdce6adb7
commit
c85f8dbb17
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 } }
|
||||
};
|
||||
|
@ -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 } }
|
||||
|
@ -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 } } }
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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 } } }
|
||||
};
|
||||
|
@ -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 } }
|
||||
|
@ -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 } } }
|
||||
};
|
||||
|
@ -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 } }
|
||||
|
@ -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 {
|
||||
|
@ -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 } }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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 } }
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
|
96
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
Normal file
96
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit10.C
Normal file
@ -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;
|
||||
}
|
53
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
Normal file
53
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit11.C
Normal file
@ -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" }
|
||||
};
|
24
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
Normal file
24
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit12.C
Normal file
@ -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" }
|
||||
}
|
14
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
Normal file
14
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit13.C
Normal file
@ -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" }
|
26
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
Normal file
26
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit14.C
Normal file
@ -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
|
35
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
Normal file
35
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit15.C
Normal file
@ -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), "");
|
23
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C
Normal file
23
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit16.C
Normal file
@ -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;
|
@ -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;
|
||||
}
|
||||
|
@ -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" }
|
||||
}
|
||||
|
96
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C
Normal file
96
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit8.C
Normal file
@ -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;
|
||||
}
|
53
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C
Normal file
53
gcc/testsuite/g++.dg/cpp23/constexpr-nonlit9.C
Normal file
@ -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 } }
|
||||
};
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ();
|
||||
|
@ -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 } }
|
||||
|
@ -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 } } }
|
||||
|
@ -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()};
|
||||
|
@ -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 }
|
||||
|
@ -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 ()
|
||||
|
@ -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) {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user