mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-15 23:11:25 +08:00
c++: defer all consteval in default args [DR2631]
The proposed resolution of CWG2631 extends our current handling of source_location::current to all consteval functions: default arguments are not evaluated until they're used in a call, the same should apply to evaluation of immediate invocations. And similarly for default member initializers. Previously we folded source_location::current in cp_fold_r; now we fold all consteval calls in default arguments/member initializers in bot_replace. DR 2631 gcc/cp/ChangeLog: * cp-tree.h (source_location_current_p): Remove. * name-lookup.h (struct cp_binding_level): Remove immediate_fn_ctx_p. * call.cc (in_immediate_context): All default args and DMI are potentially immediate context. (immediate_invocation_p): Don't treat source_location specially. (struct in_consteval_if_p_temp_override): Move to cp-tree.h. * constexpr.cc (get_nth_callarg): Move to cp-tree.h. * cp-gimplify.cc (cp_fold_r): Don't fold consteval. * name-lookup.cc (begin_scope): Don't set immediate_fn_ctx_p. * parser.cc (cp_parser_lambda_declarator_opt): Likewise. (cp_parser_direct_declarator): Likewise. * pt.cc (tsubst_default_argument): Open sk_function_parms level. * tree.cc (source_location_current_p): Remove. (bot_replace): Fold consteval here. (break_out_target_exprs): Handle errors. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/consteval-defarg3.C: New test.
This commit is contained in:
parent
bfcd9f8453
commit
9bf74082bc
@ -9301,7 +9301,8 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref)
|
||||
}
|
||||
|
||||
/* Return true if in an immediate function context, or an unevaluated operand,
|
||||
or a subexpression of an immediate invocation. */
|
||||
or a default argument/member initializer, or a subexpression of an immediate
|
||||
invocation. */
|
||||
|
||||
bool
|
||||
in_immediate_context ()
|
||||
@ -9309,8 +9310,11 @@ in_immediate_context ()
|
||||
return (cp_unevaluated_operand != 0
|
||||
|| (current_function_decl != NULL_TREE
|
||||
&& DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
|
||||
|| (current_binding_level->kind == sk_function_parms
|
||||
&& current_binding_level->immediate_fn_ctx_p)
|
||||
/* DR 2631: default args and DMI aren't immediately evaluated.
|
||||
Return true here so immediate_invocation_p returns false. */
|
||||
|| current_binding_level->kind == sk_function_parms
|
||||
|| current_binding_level->kind == sk_template_parms
|
||||
|| parsing_nsdmi ()
|
||||
|| in_consteval_if_p);
|
||||
}
|
||||
|
||||
@ -9318,29 +9322,13 @@ in_immediate_context ()
|
||||
is an immediate invocation. */
|
||||
|
||||
static bool
|
||||
immediate_invocation_p (tree fn, int nargs)
|
||||
immediate_invocation_p (tree fn)
|
||||
{
|
||||
return (TREE_CODE (fn) == FUNCTION_DECL
|
||||
&& DECL_IMMEDIATE_FUNCTION_P (fn)
|
||||
&& !in_immediate_context ()
|
||||
/* As an exception, we defer std::source_location::current ()
|
||||
invocations until genericization because LWG3396 mandates
|
||||
special behavior for it. */
|
||||
&& (nargs > 1 || !source_location_current_p (fn)));
|
||||
&& !in_immediate_context ());
|
||||
}
|
||||
|
||||
/* temp_override for in_consteval_if_p, which can't use make_temp_override
|
||||
because it is a bitfield. */
|
||||
|
||||
struct in_consteval_if_p_temp_override {
|
||||
bool save_in_consteval_if_p;
|
||||
in_consteval_if_p_temp_override ()
|
||||
: save_in_consteval_if_p (in_consteval_if_p) {}
|
||||
void reset () { in_consteval_if_p = save_in_consteval_if_p; }
|
||||
~in_consteval_if_p_temp_override ()
|
||||
{ reset (); }
|
||||
};
|
||||
|
||||
/* Subroutine of the various build_*_call functions. Overload resolution
|
||||
has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
|
||||
ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a
|
||||
@ -9398,7 +9386,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
|
||||
SET_EXPR_LOCATION (expr, input_location);
|
||||
if (TREE_THIS_VOLATILE (fn) && cfun)
|
||||
current_function_returns_abnormally = 1;
|
||||
if (immediate_invocation_p (fn, vec_safe_length (args)))
|
||||
if (immediate_invocation_p (fn))
|
||||
{
|
||||
tree obj_arg = NULL_TREE, exprimm = expr;
|
||||
if (DECL_CONSTRUCTOR_P (fn))
|
||||
@ -9543,7 +9531,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
|
||||
in_consteval_if_p_temp_override icip;
|
||||
/* If the call is immediate function invocation, make sure
|
||||
taking address of immediate functions is allowed in its arguments. */
|
||||
if (immediate_invocation_p (STRIP_TEMPLATE (fn), nargs))
|
||||
if (immediate_invocation_p (STRIP_TEMPLATE (fn)))
|
||||
in_consteval_if_p = true;
|
||||
|
||||
/* The implicit parameters to a constructor are not considered by overload
|
||||
@ -10072,7 +10060,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
|
||||
if (TREE_CODE (fn) == ADDR_EXPR)
|
||||
{
|
||||
tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0));
|
||||
if (immediate_invocation_p (fndecl, nargs))
|
||||
if (immediate_invocation_p (fndecl))
|
||||
{
|
||||
tree obj_arg = NULL_TREE;
|
||||
/* Undo convert_from_reference called by build_cxx_call. */
|
||||
|
@ -1324,26 +1324,6 @@ save_fundef_copy (tree fun, tree copy)
|
||||
*slot = copy;
|
||||
}
|
||||
|
||||
/* We have an expression tree T that represents a call, either CALL_EXPR
|
||||
or AGGR_INIT_EXPR. Return the Nth argument. */
|
||||
|
||||
static inline tree
|
||||
get_nth_callarg (tree t, int n)
|
||||
{
|
||||
switch (TREE_CODE (t))
|
||||
{
|
||||
case CALL_EXPR:
|
||||
return CALL_EXPR_ARG (t, n);
|
||||
|
||||
case AGGR_INIT_EXPR:
|
||||
return AGGR_INIT_EXPR_ARG (t, n);
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Whether our evaluation wants a prvalue (e.g. CONSTRUCTOR or _CST),
|
||||
a glvalue (e.g. VAR_DECL or _REF), or nothing. */
|
||||
|
||||
|
@ -1010,13 +1010,6 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
|
||||
}
|
||||
break;
|
||||
|
||||
case CALL_EXPR:
|
||||
if (tree fndecl = cp_get_callee_fndecl_nofold (stmt))
|
||||
if (DECL_IMMEDIATE_FUNCTION_P (fndecl)
|
||||
&& source_location_current_p (fndecl))
|
||||
*stmt_p = stmt = cxx_constant_value (stmt);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2030,6 +2030,18 @@ make_temp_override (T& var, type_identity_t<T> overrider)
|
||||
return { var, overrider };
|
||||
}
|
||||
|
||||
/* temp_override for in_consteval_if_p, which can't use make_temp_override
|
||||
because it is a bitfield. */
|
||||
|
||||
struct in_consteval_if_p_temp_override {
|
||||
bool save_in_consteval_if_p;
|
||||
in_consteval_if_p_temp_override ()
|
||||
: save_in_consteval_if_p (in_consteval_if_p) {}
|
||||
void reset () { in_consteval_if_p = save_in_consteval_if_p; }
|
||||
~in_consteval_if_p_temp_override ()
|
||||
{ reset (); }
|
||||
};
|
||||
|
||||
/* The cached class binding level, from the most recently exited
|
||||
class, or NULL if none. */
|
||||
|
||||
@ -4201,6 +4213,25 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
|
||||
for ((arg) = first_aggr_init_expr_arg ((call), &(iter)); (arg); \
|
||||
(arg) = next_aggr_init_expr_arg (&(iter)))
|
||||
|
||||
/* We have an expression tree T that represents a call, either CALL_EXPR
|
||||
or AGGR_INIT_EXPR. Return a reference to the Nth argument. */
|
||||
|
||||
static inline tree&
|
||||
get_nth_callarg (tree t, int n)
|
||||
{
|
||||
switch (TREE_CODE (t))
|
||||
{
|
||||
case CALL_EXPR:
|
||||
return CALL_EXPR_ARG (t, n);
|
||||
|
||||
case AGGR_INIT_EXPR:
|
||||
return AGGR_INIT_EXPR_ARG (t, n);
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
/* VEC_INIT_EXPR accessors. */
|
||||
#define VEC_INIT_EXPR_SLOT(NODE) TREE_OPERAND (VEC_INIT_EXPR_CHECK (NODE), 0)
|
||||
#define VEC_INIT_EXPR_INIT(NODE) TREE_OPERAND (VEC_INIT_EXPR_CHECK (NODE), 1)
|
||||
@ -7880,7 +7911,6 @@ extern tree bind_template_template_parm (tree, tree);
|
||||
extern tree array_type_nelts_total (tree);
|
||||
extern tree array_type_nelts_top (tree);
|
||||
extern bool array_of_unknown_bound_p (const_tree);
|
||||
extern bool source_location_current_p (tree);
|
||||
extern tree break_out_target_exprs (tree, bool = false);
|
||||
extern tree build_ctor_subob_ref (tree, tree, tree);
|
||||
extern tree replace_placeholders (tree, tree, bool * = NULL);
|
||||
|
@ -4302,8 +4302,6 @@ begin_scope (scope_kind kind, tree entity)
|
||||
|
||||
case sk_function_parms:
|
||||
scope->keep = keep_next_level_flag;
|
||||
if (entity)
|
||||
scope->immediate_fn_ctx_p = DECL_IMMEDIATE_FUNCTION_P (entity);
|
||||
break;
|
||||
|
||||
case sk_namespace:
|
||||
|
@ -307,13 +307,10 @@ struct GTY(()) cp_binding_level {
|
||||
'this_entity'. */
|
||||
unsigned defining_class_p : 1;
|
||||
|
||||
/* true for SK_FUNCTION_PARMS of immediate functions. */
|
||||
unsigned immediate_fn_ctx_p : 1;
|
||||
|
||||
/* True for SK_FUNCTION_PARMS of a requires-expression. */
|
||||
unsigned requires_expression: 1;
|
||||
|
||||
/* 21 bits left to fill a 32-bit word. */
|
||||
/* 22 bits left to fill a 32-bit word. */
|
||||
};
|
||||
|
||||
/* The binding level currently in effect. */
|
||||
|
@ -11519,31 +11519,11 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
|
||||
opening parenthesis if present. */
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
|
||||
{
|
||||
bool is_consteval = false;
|
||||
/* For C++20, before parsing the parameter list check if there is
|
||||
a consteval specifier in the corresponding decl-specifier-seq. */
|
||||
if (cxx_dialect >= cxx20)
|
||||
{
|
||||
for (size_t n = cp_parser_skip_balanced_tokens (parser, 1);
|
||||
cp_lexer_nth_token_is (parser->lexer, n, CPP_KEYWORD); n++)
|
||||
{
|
||||
if (cp_lexer_peek_nth_token (parser->lexer, n)->keyword
|
||||
== RID_CONSTEVAL)
|
||||
{
|
||||
is_consteval = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matching_parens parens;
|
||||
parens.consume_open (parser);
|
||||
|
||||
begin_scope (sk_function_parms, /*entity=*/NULL_TREE);
|
||||
|
||||
if (is_consteval)
|
||||
current_binding_level->immediate_fn_ctx_p = true;
|
||||
|
||||
/* Parse parameters. */
|
||||
param_list
|
||||
= cp_parser_parameter_declaration_clause
|
||||
@ -23186,10 +23166,6 @@ cp_parser_direct_declarator (cp_parser* parser,
|
||||
|
||||
begin_scope (sk_function_parms, NULL_TREE);
|
||||
|
||||
/* Signal we are in the immediate function context. */
|
||||
if (flags & CP_PARSER_FLAGS_CONSTEVAL)
|
||||
current_binding_level->immediate_fn_ctx_p = true;
|
||||
|
||||
/* Parse the parameter-declaration-clause. */
|
||||
params
|
||||
= cp_parser_parameter_declaration_clause (parser, flags);
|
||||
|
@ -13933,6 +13933,8 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg,
|
||||
push_to_top_level ();
|
||||
push_access_scope (fn);
|
||||
push_deferring_access_checks (dk_no_deferred);
|
||||
/* So in_immediate_context knows this is a default argument. */
|
||||
begin_scope (sk_function_parms, fn);
|
||||
start_lambda_scope (parm);
|
||||
|
||||
/* The default argument expression may cause implicitly defined
|
||||
@ -13956,6 +13958,7 @@ tsubst_default_argument (tree fn, int parmnum, tree type, tree arg,
|
||||
inform (input_location,
|
||||
" when instantiating default argument for call to %qD", fn);
|
||||
|
||||
leave_scope ();
|
||||
pop_deferring_access_checks ();
|
||||
pop_access_scope (fn);
|
||||
pop_from_top_level ();
|
||||
|
@ -3125,32 +3125,6 @@ array_type_nelts_total (tree type)
|
||||
return sz;
|
||||
}
|
||||
|
||||
/* Return true if FNDECL is std::source_location::current () method. */
|
||||
|
||||
bool
|
||||
source_location_current_p (tree fndecl)
|
||||
{
|
||||
gcc_checking_assert (TREE_CODE (fndecl) == FUNCTION_DECL
|
||||
&& DECL_IMMEDIATE_FUNCTION_P (fndecl));
|
||||
if (DECL_NAME (fndecl) == NULL_TREE
|
||||
|| TREE_CODE (TREE_TYPE (fndecl)) != FUNCTION_TYPE
|
||||
|| TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != RECORD_TYPE
|
||||
|| DECL_CONTEXT (fndecl) != TREE_TYPE (TREE_TYPE (fndecl))
|
||||
|| !id_equal (DECL_NAME (fndecl), "current"))
|
||||
return false;
|
||||
|
||||
tree source_location = DECL_CONTEXT (fndecl);
|
||||
if (TYPE_NAME (source_location) == NULL_TREE
|
||||
|| TREE_CODE (TYPE_NAME (source_location)) != TYPE_DECL
|
||||
|| TYPE_IDENTIFIER (source_location) == NULL_TREE
|
||||
|| !id_equal (TYPE_IDENTIFIER (source_location),
|
||||
"source_location")
|
||||
|| !decl_in_std_namespace_p (TYPE_NAME (source_location)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct bot_data
|
||||
{
|
||||
splay_tree target_remap;
|
||||
@ -3298,7 +3272,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;
|
||||
@ -3328,6 +3302,27 @@ 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;
|
||||
}
|
||||
@ -3353,7 +3348,8 @@ break_out_target_exprs (tree t, bool clear_location /* = false */)
|
||||
bot_data data = { target_remap, clear_location };
|
||||
if (cp_walk_tree (&t, bot_manip, &data, NULL) == error_mark_node)
|
||||
t = error_mark_node;
|
||||
cp_walk_tree (&t, bot_replace, &data, NULL);
|
||||
if (cp_walk_tree (&t, bot_replace, &data, NULL) == error_mark_node)
|
||||
t = error_mark_node;
|
||||
|
||||
if (!--target_remap_count)
|
||||
{
|
||||
|
23
gcc/testsuite/g++.dg/cpp2a/consteval-defarg3.C
Normal file
23
gcc/testsuite/g++.dg/cpp2a/consteval-defarg3.C
Normal file
@ -0,0 +1,23 @@
|
||||
// DR 2631: default args and DMI aren't immediately evaluated
|
||||
// { dg-do compile { target c++20 } }
|
||||
// { dg-final { scan-assembler-not "foober" } }
|
||||
|
||||
consteval int foober();
|
||||
|
||||
int g(int = foober());
|
||||
struct A { int i = foober(); };
|
||||
template <int i = foober()> struct B { };
|
||||
struct C
|
||||
{
|
||||
consteval C(int = foober()) { }
|
||||
};
|
||||
int h(C = C());
|
||||
|
||||
consteval int foober() { return 42; }
|
||||
|
||||
int main() {
|
||||
A a;
|
||||
B<> b;
|
||||
g();
|
||||
h();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user