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:
parent
f25960b038
commit
6851e3423c
15 changed files with 288 additions and 133 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *);
|
||||
|
|
4
gcc/testsuite/g++.dg/cpp2a/consteval32.C
Normal file
4
gcc/testsuite/g++.dg/cpp2a/consteval32.C
Normal file
|
@ -0,0 +1,4 @@
|
|||
// { dg-do compile { target c++20 } }
|
||||
|
||||
consteval int foo () { return 42; }
|
||||
int bar () { return (*(&foo)) (); } // { dg-error "taking address" }
|
34
gcc/testsuite/g++.dg/cpp2a/consteval33.C
Normal file
34
gcc/testsuite/g++.dg/cpp2a/consteval33.C
Normal 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);
|
33
gcc/testsuite/g++.dg/cpp2a/consteval34.C
Normal file
33
gcc/testsuite/g++.dg/cpp2a/consteval34.C
Normal 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" }
|
||||
}
|
10
gcc/testsuite/g++.dg/cpp2a/consteval35.C
Normal file
10
gcc/testsuite/g++.dg/cpp2a/consteval35.C
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
Loading…
Add table
Reference in a new issue