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:
Jason Merrill 2022-10-07 20:34:53 -04:00
parent bfcd9f8453
commit 9bf74082bc
10 changed files with 94 additions and 110 deletions

View File

@ -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. */

View File

@ -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. */

View File

@ -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;
}

View File

@ -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);

View File

@ -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:

View File

@ -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. */

View File

@ -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);

View File

@ -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 ();

View File

@ -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)
{

View 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();
}