c++: Move consteval folding to cp_fold_r

In the review of P2564:
<https://gcc.gnu.org/pipermail/gcc-patches/2023-August/628747.html>
it turned out that in order to correctly handle an example in the paper,
we should stop doing immediate evaluation in build_over_call and
bot_replace, and instead do it in cp_fold_r.  This patch does that.

Another benefit is that this is a pretty significant simplification, at
least in my opinion.  Also, this fixes the c++/110997 ICE (but the test
doesn't compile yet).

The main drawback seems to be that cp_fold_r doesn't process
uninstantiated templates.  We still have to handle things like
"false ? foo () : 1".  To that end, I've added cp_fold_immediate, called
on dead branches in cxx_eval_conditional_expression.

You'll see that I've reintroduced ADDR_EXPR_DENOTES_CALL_P here.  This
is to detect

  *(&foo)) ()
  (s.*&S::foo) ()

which were deemed ill-formed.

gcc/cp/ChangeLog:

	* call.cc (build_over_call): Set ADDR_EXPR_DENOTES_CALL_P.  Don't handle
	immediate_invocation_p here.
	* constexpr.cc (cxx_eval_call_expression): Use mce_true for
	DECL_IMMEDIATE_FUNCTION_P.
	(cxx_eval_conditional_expression): Call cp_fold_immediate.
	* cp-gimplify.cc (enum fold_flags): Add ff_fold_immediate.
	(maybe_replace_decl): Make static.
	(cp_fold_r): Expand immediate invocations.
	(cp_fold_immediate_r): New.
	(cp_fold_immediate): New.
	* cp-tree.h (ADDR_EXPR_DENOTES_CALL_P): Define.
	(cp_fold_immediate): Declare.
	* tree.cc (bot_replace): Don't handle immediate invocations here.

libstdc++-v3/ChangeLog:

	* testsuite/20_util/allocator/105975.cc: Add dg-error.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp23/consteval-if2.C: Add xfail.
	* g++.dg/cpp2a/consteval-memfn1.C: Adjust.
	* g++.dg/cpp2a/consteval11.C: Remove dg-message.
	* g++.dg/cpp2a/consteval3.C: Remove dg-message and dg-error.
	* g++.dg/cpp2a/consteval9.C: Remove dg-message.
	* g++.dg/cpp2a/consteval32.C: New test.
	* g++.dg/cpp2a/consteval33.C: New test.
	* g++.dg/cpp2a/consteval34.C: New test.
	* g++.dg/cpp2a/consteval35.C: New test.
This commit is contained in:
Marek Polacek 2023-08-31 20:11:50 -04:00
parent f25960b038
commit 6851e3423c
15 changed files with 288 additions and 133 deletions

View file

@ -10434,6 +10434,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
fn = build_addr_func (fn, complain);
if (fn == error_mark_node)
return error_mark_node;
/* We're actually invoking the function. (Immediate functions get an
& when invoking it even though the user didn't use &.) */
ADDR_EXPR_DENOTES_CALL_P (fn) = true;
}
tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
@ -10451,41 +10455,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
if (TREE_CODE (c) == CALL_EXPR)
suppress_warning (c /* Suppress all warnings. */);
}
if (TREE_CODE (fn) == ADDR_EXPR)
{
tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0));
if (immediate_invocation_p (fndecl))
{
tree obj_arg = NULL_TREE;
/* Undo convert_from_reference called by build_cxx_call. */
if (REFERENCE_REF_P (call))
call = TREE_OPERAND (call, 0);
if (DECL_CONSTRUCTOR_P (fndecl))
obj_arg = cand->first_arg ? cand->first_arg : (*args)[0];
if (obj_arg && is_dummy_object (obj_arg))
{
call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain);
obj_arg = NULL_TREE;
}
/* Look through *(const T *)&obj. */
else if (obj_arg && INDIRECT_REF_P (obj_arg))
{
tree addr = TREE_OPERAND (obj_arg, 0);
STRIP_NOPS (addr);
if (TREE_CODE (addr) == ADDR_EXPR)
{
tree typeo = TREE_TYPE (obj_arg);
tree typei = TREE_TYPE (TREE_OPERAND (addr, 0));
if (same_type_ignoring_top_level_qualifiers_p (typeo, typei))
obj_arg = TREE_OPERAND (addr, 0);
}
}
call = cxx_constant_value (call, obj_arg, complain);
if (obj_arg && !error_operand_p (call))
call = cp_build_init_expr (obj_arg, call);
call = convert_from_reference (call);
}
}
return call;
}

View file

@ -3135,6 +3135,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
/* Make sure we fold std::is_constant_evaluated to true in an
immediate function. */
if (DECL_IMMEDIATE_FUNCTION_P (fun))
call_ctx.manifestly_const_eval = mce_true;
/* If this is a constexpr destructor, the object's const and volatile
semantics are no longer in effect; see [class.dtor]p5. */
if (new_obj && DECL_DESTRUCTOR_P (fun))
@ -3807,8 +3812,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
}
/* Subroutine of cxx_eval_constant_expression.
Attempt to evaluate condition expressions. Dead branches are not
looked into. */
Attempt to evaluate condition expressions. */
static tree
cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
@ -3837,12 +3841,25 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
boolean_type_node);
}
/* Don't VERIFY_CONSTANT the other operands. */
if (integer_zerop (val))
const bool zero_p = integer_zerop (val);
if (zero_p)
val = TREE_OPERAND (t, 2);
else
val = TREE_OPERAND (t, 1);
if (TREE_CODE (t) == IF_STMT && !val)
val = void_node;
/* P2564: a subexpression of a manifestly constant-evaluated expression
or conversion is an immediate function context. */
if (ctx->manifestly_const_eval != mce_true
&& !in_immediate_context ()
&& cp_fold_immediate (&TREE_OPERAND (t, zero_p ? 1 : 2),
ctx->manifestly_const_eval))
{
*non_constant_p = true;
return t;
}
/* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still
serve as the initializer for the same object as the outer TARGET_EXPR,
as in

View file

@ -53,6 +53,8 @@ enum fold_flags {
definitely not in a manifestly constant-evaluated
context. */
ff_mce_false = 1 << 1,
/* Whether we're being called from cp_fold_immediate. */
ff_fold_immediate = 1 << 2,
};
using fold_flags_t = int;
@ -1000,7 +1002,7 @@ cp_genericize_target_expr (tree *stmt_p)
replacement when cp_folding TARGET_EXPR to preserve the invariant that
AGGR_INIT_EXPR_SLOT agrees with the enclosing TARGET_EXPR_SLOT. */
bool
static bool
maybe_replace_decl (tree *tp, tree decl, tree replacement)
{
if (!*tp || !VOID_TYPE_P (TREE_TYPE (*tp)))
@ -1029,6 +1031,123 @@ struct cp_genericize_data
bool handle_invisiref_parm_p;
};
/* A subroutine of cp_fold_r to handle immediate functions. */
static tree
cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
{
auto data = static_cast<cp_fold_data *>(data_);
tree stmt = *stmt_p;
/* The purpose of this is not to emit errors for mce_unknown. */
const tsubst_flags_t complain = (data->flags & ff_mce_false
? tf_error : tf_none);
/* No need to look into types or unevaluated operands.
NB: This affects cp_fold_r as well. */
if (TYPE_P (stmt) || unevaluated_p (TREE_CODE (stmt)))
{
*walk_subtrees = 0;
return NULL_TREE;
}
switch (TREE_CODE (stmt))
{
/* Unfortunately we must handle code like
false ? bar () : 42
where we have to check bar too. The cp_fold call in cp_fold_r could
fold the ?: into a constant before we see it here. */
case COND_EXPR:
/* If we are called from cp_fold_immediate, we don't need to worry about
cp_fold folding away the COND_EXPR. */
if (data->flags & ff_fold_immediate)
break;
if (TREE_OPERAND (stmt, 1)
&& cp_walk_tree (&TREE_OPERAND (stmt, 1), cp_fold_immediate_r, data,
nullptr))
return error_mark_node;
if (TREE_OPERAND (stmt, 2)
&& cp_walk_tree (&TREE_OPERAND (stmt, 2), cp_fold_immediate_r, data,
nullptr))
return error_mark_node;
/* We're done here. Don't clear *walk_subtrees here though: we're called
from cp_fold_r and we must let it recurse on the expression with
cp_fold. */
break;
case PTRMEM_CST:
if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
&& DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
{
if (!data->pset.add (stmt) && (complain & tf_error))
{
error_at (PTRMEM_CST_LOCATION (stmt),
"taking address of an immediate function %qD",
PTRMEM_CST_MEMBER (stmt));
*stmt_p = build_zero_cst (TREE_TYPE (stmt));
}
return error_mark_node;
}
break;
/* Expand immediate invocations. */
case CALL_EXPR:
case AGGR_INIT_EXPR:
if (tree fn = cp_get_callee (stmt))
if (TREE_CODE (fn) != ADDR_EXPR || ADDR_EXPR_DENOTES_CALL_P (fn))
if (tree fndecl = cp_get_fndecl_from_callee (fn, /*fold*/false))
if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
{
stmt = cxx_constant_value (stmt, complain);
if (stmt == error_mark_node)
{
if (complain & tf_error)
*stmt_p = error_mark_node;
return error_mark_node;
}
*stmt_p = stmt;
}
break;
case ADDR_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
&& DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0))
&& !ADDR_EXPR_DENOTES_CALL_P (stmt))
{
if (complain & tf_error)
{
error_at (EXPR_LOCATION (stmt),
"taking address of an immediate function %qD",
TREE_OPERAND (stmt, 0));
*stmt_p = build_zero_cst (TREE_TYPE (stmt));
}
return error_mark_node;
}
break;
default:
break;
}
return NULL_TREE;
}
/* A wrapper around cp_fold_immediate_r. Return true if we found
a non-constant immediate function, or taking the address of an
immediate function. */
bool
cp_fold_immediate (tree *tp, mce_value manifestly_const_eval)
{
if (cxx_dialect <= cxx17)
return false;
fold_flags_t flags = ff_fold_immediate;
if (manifestly_const_eval == mce_false)
flags |= ff_mce_false;
cp_fold_data data (flags);
return !!cp_walk_tree_without_duplicates (tp, cp_fold_immediate_r, &data);
}
/* Perform any pre-gimplification folding of C++ front end trees to
GENERIC.
Note: The folding of non-omp cases is something to move into
@ -1043,36 +1162,8 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
tree stmt = *stmt_p;
enum tree_code code = TREE_CODE (stmt);
switch (code)
{
case PTRMEM_CST:
if (TREE_CODE (PTRMEM_CST_MEMBER (stmt)) == FUNCTION_DECL
&& DECL_IMMEDIATE_FUNCTION_P (PTRMEM_CST_MEMBER (stmt)))
{
if (!data->pset.add (stmt))
error_at (PTRMEM_CST_LOCATION (stmt),
"taking address of an immediate function %qD",
PTRMEM_CST_MEMBER (stmt));
stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
break;
}
break;
case ADDR_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 0)) == FUNCTION_DECL
&& DECL_IMMEDIATE_FUNCTION_P (TREE_OPERAND (stmt, 0)))
{
error_at (EXPR_LOCATION (stmt),
"taking address of an immediate function %qD",
TREE_OPERAND (stmt, 0));
stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
break;
}
break;
default:
break;
}
if (cxx_dialect > cxx17)
cp_fold_immediate_r (stmt_p, walk_subtrees, data);
*stmt_p = stmt = cp_fold (*stmt_p, data->flags);
@ -1084,7 +1175,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
always the same tree, which the first time cp_fold_r has been
called on it had the subtrees walked. */
*walk_subtrees = 0;
return NULL;
return NULL_TREE;
}
code = TREE_CODE (stmt);
@ -1136,7 +1227,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
}
cp_walk_tree (&OMP_FOR_PRE_BODY (stmt), cp_fold_r, data, NULL);
*walk_subtrees = 0;
return NULL;
return NULL_TREE;
case IF_STMT:
if (IF_STMT_CONSTEVAL_P (stmt))
@ -1146,7 +1237,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_r, data, NULL);
cp_walk_tree (&IF_SCOPE (stmt), cp_fold_r, data, NULL);
*walk_subtrees = 0;
return NULL;
return NULL_TREE;
}
break;
@ -1183,7 +1274,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
break;
}
return NULL;
return NULL_TREE;
}
/* Fold ALL the trees! FIXME we should be able to remove this, but

View file

@ -4784,6 +4784,11 @@ get_vec_init_expr (tree t)
#define PTRMEM_OK_P(NODE) \
TREE_LANG_FLAG_0 (TREE_CHECK3 ((NODE), ADDR_EXPR, OFFSET_REF, SCOPE_REF))
/* True if this ADDR_EXPR denotes a function call; that is, it's
fn() rather than &fn. */
#define ADDR_EXPR_DENOTES_CALL_P(NODE) \
(ADDR_EXPR_CHECK(NODE)->base.protected_flag)
/* Get the POINTER_TYPE to the METHOD_TYPE associated with this
pointer to member function. TYPE_PTRMEMFUNC_P _must_ be true,
before using this macro. */
@ -6580,6 +6585,24 @@ extern int class_dump_id;
extern int module_dump_id;
extern int raw_dump_id;
/* Whether the current context is manifestly constant-evaluated.
Used by the constexpr machinery to control folding of
__builtin_is_constant_evaluated. */
enum class mce_value
{
/* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */
mce_unknown = 0,
/* Fold it to true. */
mce_true = 1,
/* Fold it to false. Primarily used during cp_fold_function and
cp_fully_fold_init. */
mce_false = -1,
};
constexpr mce_value mce_unknown = mce_value::mce_unknown;
constexpr mce_value mce_true = mce_value::mce_true;
constexpr mce_value mce_false = mce_value::mce_false;
/* in call.cc */
extern bool check_dtor_name (tree, tree);
int magic_varargs_p (tree);
@ -8354,6 +8377,7 @@ extern tree process_stmt_assume_attribute (tree, tree, location_t);
extern bool simple_empty_class_p (tree, tree, tree_code);
extern tree fold_builtin_source_location (const_tree);
extern tree get_source_location_impl_type ();
extern bool cp_fold_immediate (tree *, mce_value);
/* in name-lookup.cc */
extern tree strip_using_decl (tree);
@ -8515,24 +8539,6 @@ struct GTY((for_user)) constexpr_fundef {
tree result;
};
/* Whether the current context is manifestly constant-evaluated.
Used by the constexpr machinery to control folding of
__builtin_is_constant_evaluated. */
enum class mce_value
{
/* Unknown, so treat __builtin_is_constant_evaluated as non-constant. */
mce_unknown = 0,
/* Fold it to true. */
mce_true = 1,
/* Fold it to false. Primarily used during cp_fold_function and
cp_fully_fold_init. */
mce_false = -1,
};
constexpr mce_value mce_unknown = mce_value::mce_unknown;
constexpr mce_value mce_true = mce_value::mce_true;
constexpr mce_value mce_false = mce_value::mce_false;
extern void fini_constexpr (void);
extern bool literal_type_p (tree);
extern void maybe_save_constexpr_fundef (tree);

View file

@ -3254,7 +3254,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_)
variables. */
static tree
bot_replace (tree* t, int* walk_subtrees, void* data_)
bot_replace (tree* t, int */*walk_subtrees*/, void* data_)
{
bot_data &data = *(bot_data*)data_;
splay_tree target_remap = data.target_remap;
@ -3284,27 +3284,6 @@ bot_replace (tree* t, int* walk_subtrees, void* data_)
/*check_access=*/false, /*nonnull=*/true,
tf_warning_or_error);
}
else if (cxx_dialect >= cxx20
&& (TREE_CODE (*t) == CALL_EXPR
|| TREE_CODE (*t) == AGGR_INIT_EXPR)
&& !in_immediate_context ())
{
/* Expand immediate invocations. */
if (tree fndecl = cp_get_callee_fndecl_nofold (*t))
if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
{
/* Make in_immediate_context true within the args. */
in_consteval_if_p_temp_override ito;
in_consteval_if_p = true;
int nargs = call_expr_nargs (*t);
for (int i = 0; i < nargs; ++i)
cp_walk_tree (&get_nth_callarg (*t, i), bot_replace, data_, NULL);
*t = cxx_constant_value (*t);
if (*t == error_mark_node)
return error_mark_node;
*walk_subtrees = 0;
}
}
return NULL_TREE;
}

View file

@ -58,6 +58,7 @@ baz (int x)
return r;
}
// This function is not instantiated so NDR.
template <typename T>
constexpr int
qux (int x)
@ -65,7 +66,7 @@ qux (int x)
int r = 0;
if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } }
{
r += foo (x); // { dg-error "'x' is not a constant expression" }
r += foo (x); // { dg-error "'x' is not a constant expression" "" { xfail *-*-* } }
}
else
{

View file

@ -25,3 +25,10 @@ void VerifyHash(fixed_string s) {
fixed_string::size_static(-1); // { dg-message "expansion of" }
s(); // { dg-bogus "" }
}
void
do_test ()
{
fixed_string f;
VerifyHash<int>(f);
}

View file

@ -5,21 +5,21 @@ consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is n
constexpr int a = bar (1);
constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" }
constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" }
constexpr int c = 0 ? bar (3) : 1;
const int d = bar (4); // { dg-message "in 'constexpr' expansion of" }
const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" }
const int e = 0 ? bar (5) : 1;
int f = bar (1);
int g = bar (6); // { dg-message "in 'constexpr' expansion of" }
int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" }
int h = 0 ? bar (7) : 1;
void
foo ()
{
constexpr int a = bar (1);
constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" }
constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" }
constexpr int c = 0 ? bar (3) : 1;
const int d = bar (4); // { dg-message "in 'constexpr' expansion of" }
const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" }
const int e = 0 ? bar (5) : 1;
int f = bar (1);
int g = bar (6); // { dg-message "in 'constexpr' expansion of" }
int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" }
@ -33,13 +33,13 @@ foo ()
else
bar (12); // { dg-message "in 'constexpr' expansion of" }
if constexpr (0)
bar (13); // { dg-message "in 'constexpr' expansion of" }
bar (13);
else
bar (14); // { dg-message "in 'constexpr' expansion of" }
if constexpr (1)
bar (15); // { dg-message "in 'constexpr' expansion of" }
else
bar (16); // { dg-message "in 'constexpr' expansion of" }
bar (16);
}
consteval int
@ -77,22 +77,25 @@ template <typename T>
void
qux ()
{
// Used to give errors errors here, but not since we moved consteval
// function folding to cp_fold_r which isn't called on uninstantiated
// templates.
if (0)
bar (2); // { dg-message "in 'constexpr' expansion of" }
bar (2);
else
bar (3); // { dg-message "in 'constexpr' expansion of" }
bar (3);
if (1)
bar (4); // { dg-message "in 'constexpr' expansion of" }
bar (4);
else
bar (5); // { dg-message "in 'constexpr' expansion of" }
bar (5);
if constexpr (0)
bar (6); // { dg-message "in 'constexpr' expansion of" }
bar (6);
else
bar (7); // { dg-message "in 'constexpr' expansion of" }
bar (7);
if constexpr (1)
bar (8); // { dg-message "in 'constexpr' expansion of" }
bar (8);
else
bar (9); // { dg-message "in 'constexpr' expansion of" }
bar (9);
if (0)
bar ((T) 2);
else

View file

@ -18,8 +18,7 @@ consteval int f6 (int x) { return x; }
int d = 6; // { dg-message "'int d' is not const" }
int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" }
constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" }
constexpr int f = f7 (5); // { dg-error "" }
// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
constexpr int f = f7 (5);
using fnptr = int (int);
fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" }
int f8 (fnptr *);

View file

@ -0,0 +1,4 @@
// { dg-do compile { target c++20 } }
consteval int foo () { return 42; }
int bar () { return (*(&foo)) (); } // { dg-error "taking address" }

View file

@ -0,0 +1,34 @@
// { dg-do compile { target c++20 } }
consteval int id (int i) { return i; }
consteval int add (int i, int j) { return i + j; }
constexpr int
foo (int i = id (42))
{
return i + id (id (id (0)));
}
constexpr int
bar (int i = id (id (id (42))))
{
return i;
}
constexpr int
baz (int i = add (add (id (1), id (2)), id (3)))
{
return i;
}
void
g ()
{
foo ();
bar ();
baz ();
}
static_assert (foo () == 42);
static_assert (bar () == 42);
static_assert (baz () == 6);

View file

@ -0,0 +1,33 @@
// { dg-do compile { target c++20 } }
consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" }
constexpr int
foo (bool b)
{
return b ? bar (3) : 2; // { dg-message "in .constexpr. expansion" }
}
static_assert (foo (false) == 2);
__extension__ constexpr int g1 = false ?: bar (2); // { dg-message "in .constexpr. expansion" }
__extension__ constexpr int g2 = false ?: (1 + bar (2)); // { dg-message "in .constexpr. expansion" }
__extension__ constexpr int g3 = true ?: bar (2);
__extension__ constexpr int g4 = true ?: (1 + bar (2));
constexpr int g5 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
constexpr int g6 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
void
g ()
{
__extension__ int a1[bar(3)]; // { dg-message "in .constexpr. expansion" }
int a2[sizeof (bar(3))];
int a3 = false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
a3 += false ? (1 + bar (8)) : 1; // { dg-message "in .constexpr. expansion" }
__extension__ int a4 = false ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
__extension__ int a5 = true ?: (1 + bar (8)); // { dg-message "in .constexpr. expansion" }
int a6 = bar (2) ? 1 : 2; // { dg-message "in .constexpr. expansion" }
int a7 = bar (2) - 1 ? 1 : 2; // { dg-message "in .constexpr. expansion" }
}

View file

@ -0,0 +1,10 @@
// { dg-do compile { target c++20 } }
template <typename T, typename F>
constexpr bool is_not(T t, F f) {
return not f(t);
}
consteval bool is_even(int i) { return i % 2 == 0; }
static_assert(is_not(5, is_even)); // ok

View file

@ -15,10 +15,11 @@ void qux ()
int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" }
}
// This function is not instantiated so NDR.
template <int N>
void quux ()
{
int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" }
int a = bar (5);
}
void

View file

@ -13,6 +13,6 @@ consteval bool test_pr105957()
a.deallocate(p, n);
return true;
}
static_assert( test_pr105957() );
static_assert( test_pr105957() ); // { dg-error "non-constant" }
// { dg-error "throw_bad_array_new_length" "" { target *-*-* } 0 }