Reimplement handling of lambdas in templates.

* cp-tree.h (LAMBDA_FUNCTION_P): Check DECL_DECLARES_FUNCTION_P.
	* decl.c (start_preparsed_function): Call start_lambda_scope.
	(finish_function): Call finish_lambda_scope.
	* init.c (get_nsdmi): Call start/finish_lambda_scope.
	* lambda.c (start_lambda_scope): Only ignore VAR_DECL in a function.
	* parser.c (cp_parser_function_definition_after_declarator): Don't
	call start/finish_lambda_scope.
	* pt.c (retrieve_specialization): Ignore lambda functions in
	templates.
	(find_parameter_packs_r): Ignore capture proxies.  Look into
	lambdas.
	(check_for_bare_parameter_packs): Allow bare packs in lambdas.
	(tsubst_default_argument): Call start/finish_lambda_scope.
	(tsubst_function_decl): Handle lambda functions differently.
	(tsubst_template_decl): Likewise.
	(tsubst_expr) [DECL_EXPR]: Skip closure declarations and capture
	proxies.
	(tsubst_lambda_expr): Create a new closure rather than instantiate
	the one from the template.
	(tsubst_copy_and_build): Don't register a specialization of a pack.
	(regenerate_decl_from_template): Call start/finish_lambda_scope.
	(instantiate_decl): Remove special lambda function handling.
	* semantics.c (process_outer_var_ref): Remove special generic lambda
	handling.  Don't implicitly capture in a lambda in a template.  Look
	for an existing proxy.
	* class.c (current_nonlambda_class_type): Use decl_type_context.

From-SVN: r251433
This commit is contained in:
Jason Merrill 2017-08-29 16:37:15 -04:00 committed by Jason Merrill
parent 72932ccf0a
commit f44a8dd56f
11 changed files with 869 additions and 641 deletions

View file

@ -1,3 +1,33 @@
2017-08-23 Jason Merrill <jason@redhat.com>
Reimplement handling of lambdas in templates.
* cp-tree.h (LAMBDA_FUNCTION_P): Check DECL_DECLARES_FUNCTION_P.
* decl.c (start_preparsed_function): Call start_lambda_scope.
(finish_function): Call finish_lambda_scope.
* init.c (get_nsdmi): Call start/finish_lambda_scope.
* lambda.c (start_lambda_scope): Only ignore VAR_DECL in a function.
* parser.c (cp_parser_function_definition_after_declarator): Don't
call start/finish_lambda_scope.
* pt.c (retrieve_specialization): Ignore lambda functions in
templates.
(find_parameter_packs_r): Ignore capture proxies. Look into
lambdas.
(check_for_bare_parameter_packs): Allow bare packs in lambdas.
(tsubst_default_argument): Call start/finish_lambda_scope.
(tsubst_function_decl): Handle lambda functions differently.
(tsubst_template_decl): Likewise.
(tsubst_expr) [DECL_EXPR]: Skip closure declarations and capture
proxies.
(tsubst_lambda_expr): Create a new closure rather than instantiate
the one from the template.
(tsubst_copy_and_build): Don't register a specialization of a pack.
(regenerate_decl_from_template): Call start/finish_lambda_scope.
(instantiate_decl): Remove special lambda function handling.
* semantics.c (process_outer_var_ref): Remove special generic lambda
handling. Don't implicitly capture in a lambda in a template. Look
for an existing proxy.
* class.c (current_nonlambda_class_type): Use decl_type_context.
2017-08-29 Jason Merrill <jason@redhat.com>
* cp-tree.h (LAMBDA_EXPR_CLOSURE): Use TREE_TYPE.

View file

@ -7709,27 +7709,10 @@ outermost_open_class (void)
tree
current_nonlambda_class_type (void)
{
int i;
/* We start looking from 1 because entry 0 is from global scope,
and has no type. */
for (i = current_class_depth; i > 0; --i)
{
tree c;
if (i == current_class_depth)
c = current_class_type;
else
{
if (current_class_stack[i].hidden)
break;
c = current_class_stack[i].type;
}
if (!c)
continue;
if (!LAMBDA_TYPE_P (c))
return c;
}
return NULL_TREE;
tree type = current_class_type;
while (type && LAMBDA_TYPE_P (type))
type = decl_type_context (TYPE_NAME (type));
return type;
}
/* When entering a class scope, all enclosing class scopes' names with

View file

@ -1216,8 +1216,9 @@ struct GTY (()) tree_trait_expr {
(CLASS_TYPE_P (NODE) && CLASSTYPE_LAMBDA_EXPR (NODE))
/* Test if FUNCTION_DECL is a lambda function. */
#define LAMBDA_FUNCTION_P(FNDECL) \
(DECL_OVERLOADED_OPERATOR_P (FNDECL) == CALL_EXPR \
#define LAMBDA_FUNCTION_P(FNDECL) \
(DECL_DECLARES_FUNCTION_P (FNDECL) \
&& DECL_OVERLOADED_OPERATOR_P (FNDECL) == CALL_EXPR \
&& LAMBDA_TYPE_P (CP_DECL_CONTEXT (FNDECL)))
enum cp_lambda_default_capture_mode_type {
@ -6828,6 +6829,11 @@ extern bool is_lambda_ignored_entity (tree);
extern bool lambda_static_thunk_p (tree);
extern tree finish_builtin_launder (location_t, tree,
tsubst_flags_t);
extern void start_lambda_scope (tree);
extern void record_lambda_scope (tree);
extern void finish_lambda_scope (void);
extern tree start_lambda_function (tree fn, tree lambda_expr);
extern void finish_lambda_function (tree body);
/* in tree.c */
extern int cp_tree_operand_length (const_tree);

View file

@ -15097,6 +15097,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
&& !implicit_default_ctor_p (decl1))
cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr);
start_lambda_scope (decl1);
return true;
}
@ -15462,6 +15464,8 @@ finish_function (int flags)
if (fndecl == NULL_TREE)
return error_mark_node;
finish_lambda_scope ();
if (c_dialect_objc ())
objc_finish_function ();
@ -15565,11 +15569,11 @@ finish_function (int flags)
/* Lambda closure members are implicitly constexpr if possible. */
if (cxx_dialect >= cxx1z
&& LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))
&& (processing_template_decl
&& LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl)))
DECL_DECLARED_CONSTEXPR_P (fndecl)
= ((processing_template_decl
|| is_valid_constexpr_fn (fndecl, /*complain*/false))
&& potential_constant_expression (DECL_SAVED_TREE (fndecl)))
DECL_DECLARED_CONSTEXPR_P (fndecl) = true;
&& potential_constant_expression (DECL_SAVED_TREE (fndecl)));
/* Save constexpr function body before it gets munged by
the NRV transformation. */

View file

@ -574,13 +574,17 @@ get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain)
inject_this_parameter (DECL_CONTEXT (member), TYPE_UNQUALIFIED);
start_lambda_scope (member);
/* Do deferred instantiation of the NSDMI. */
init = (tsubst_copy_and_build
(init, DECL_TI_ARGS (member),
complain, member, /*function_p=*/false,
/*integral_constant_expression_p=*/false));
init = digest_nsdmi_init (member, init, complain);
finish_lambda_scope ();
DECL_INSTANTIATING_NSDMI_P (member) = 0;
if (init != error_mark_node)

View file

@ -1253,4 +1253,87 @@ is_lambda_ignored_entity (tree val)
return false;
}
/* Lambdas that appear in variable initializer or default argument scope
get that in their mangling, so we need to record it. We might as well
use the count for function and namespace scopes as well. */
static GTY(()) tree lambda_scope;
static GTY(()) int lambda_count;
struct GTY(()) tree_int
{
tree t;
int i;
};
static GTY(()) vec<tree_int, va_gc> *lambda_scope_stack;
void
start_lambda_scope (tree decl)
{
tree_int ti;
gcc_assert (decl);
/* Once we're inside a function, we ignore variable scope and just push
the function again so that popping works properly. */
if (current_function_decl && TREE_CODE (decl) == VAR_DECL)
decl = current_function_decl;
ti.t = lambda_scope;
ti.i = lambda_count;
vec_safe_push (lambda_scope_stack, ti);
if (lambda_scope != decl)
{
/* Don't reset the count if we're still in the same function. */
lambda_scope = decl;
lambda_count = 0;
}
}
void
record_lambda_scope (tree lambda)
{
LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope;
LAMBDA_EXPR_DISCRIMINATOR (lambda) = lambda_count++;
}
void
finish_lambda_scope (void)
{
tree_int *p = &lambda_scope_stack->last ();
if (lambda_scope != p->t)
{
lambda_scope = p->t;
lambda_count = p->i;
}
lambda_scope_stack->pop ();
}
tree
start_lambda_function (tree fco, tree lambda_expr)
{
/* Let the front end know that we are going to be defining this
function. */
start_preparsed_function (fco,
NULL_TREE,
SF_PRE_PARSED | SF_INCLASS_INLINE);
tree body = begin_function_body ();
/* Push the proxies for any explicit captures. */
for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap;
cap = TREE_CHAIN (cap))
build_capture_proxy (TREE_PURPOSE (cap));
return body;
}
void
finish_lambda_function (tree body)
{
finish_function_body (body);
/* Finish the function and generate code for it if necessary. */
tree fn = finish_function (/*inline*/2);
/* Only expand if the call op is not a template. */
if (!DECL_TEMPLATE_INFO (fn))
expand_or_defer_fn (fn);
}
#include "gt-cp-lambda.h"

View file

@ -9982,57 +9982,6 @@ cp_parser_trait_expr (cp_parser* parser, enum rid keyword)
}
}
/* Lambdas that appear in variable initializer or default argument scope
get that in their mangling, so we need to record it. We might as well
use the count for function and namespace scopes as well. */
static GTY(()) tree lambda_scope;
static GTY(()) int lambda_count;
struct GTY(()) tree_int
{
tree t;
int i;
};
static GTY(()) vec<tree_int, va_gc> *lambda_scope_stack;
static void
start_lambda_scope (tree decl)
{
tree_int ti;
gcc_assert (decl);
/* Once we're inside a function, we ignore other scopes and just push
the function again so that popping works properly. */
if (current_function_decl && TREE_CODE (decl) != FUNCTION_DECL)
decl = current_function_decl;
ti.t = lambda_scope;
ti.i = lambda_count;
vec_safe_push (lambda_scope_stack, ti);
if (lambda_scope != decl)
{
/* Don't reset the count if we're still in the same function. */
lambda_scope = decl;
lambda_count = 0;
}
}
static void
record_lambda_scope (tree lambda)
{
LAMBDA_EXPR_EXTRA_SCOPE (lambda) = lambda_scope;
LAMBDA_EXPR_DISCRIMINATOR (lambda) = lambda_count++;
}
static void
finish_lambda_scope (void)
{
tree_int *p = &lambda_scope_stack->last ();
if (lambda_scope != p->t)
{
lambda_scope = p->t;
lambda_count = p->i;
}
lambda_scope_stack->pop ();
}
/* Parse a lambda expression.
lambda-expression:
@ -10605,29 +10554,14 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
+ ctor_initializer_opt_and_function_body */
{
tree fco = lambda_function (lambda_expr);
tree body;
tree body = start_lambda_function (fco, lambda_expr);
bool done = false;
tree compound_stmt;
tree cap;
/* Let the front end know that we are going to be defining this
function. */
start_preparsed_function (fco,
NULL_TREE,
SF_PRE_PARSED | SF_INCLASS_INLINE);
start_lambda_scope (fco);
body = begin_function_body ();
matching_braces braces;
if (!braces.require_open (parser))
goto out;
/* Push the proxies for any explicit captures. */
for (cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap;
cap = TREE_CHAIN (cap))
build_capture_proxy (TREE_PURPOSE (cap));
compound_stmt = begin_compound_stmt (0);
/* 5.1.1.4 of the standard says:
@ -10691,15 +10625,7 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
finish_compound_stmt (compound_stmt);
out:
finish_function_body (body);
finish_lambda_scope ();
/* Finish the function and generate code for it if necessary. */
tree fn = finish_function (/*inline*/2);
/* Only expand if the call op is not a template. */
if (!DECL_TEMPLATE_INFO (fco))
expand_or_defer_fn (fn);
finish_lambda_function (body);
}
restore_omp_privatization_clauses (omp_privatization_save);
@ -26577,8 +26503,6 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
= parser->num_template_parameter_lists;
parser->num_template_parameter_lists = 0;
start_lambda_scope (current_function_decl);
/* If the next token is `try', `__transaction_atomic', or
`__transaction_relaxed`, then we are looking at either function-try-block
or function-transaction-block. Note that all of these include the
@ -26596,8 +26520,6 @@ cp_parser_function_definition_after_declarator (cp_parser* parser,
ctor_initializer_p = cp_parser_ctor_initializer_opt_and_function_body
(parser, /*in_function_try_block=*/false);
finish_lambda_scope ();
/* Finish the function. */
fn = finish_function ((ctor_initializer_p ? 1 : 0) |
(inline_p ? 2 : 0));

File diff suppressed because it is too large Load diff

View file

@ -3301,40 +3301,56 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
if (!mark_used (decl, complain))
return error_mark_node;
bool saw_generic_lambda = false;
if (parsing_nsdmi ())
containing_function = NULL_TREE;
else
/* If we are in a lambda function, we can move out until we hit
1. the context,
2. a non-lambda function, or
3. a non-default capturing lambda function. */
while (context != containing_function
/* containing_function can be null with invalid generic lambdas. */
&& containing_function
&& LAMBDA_FUNCTION_P (containing_function))
{
tree closure = DECL_CONTEXT (containing_function);
lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure);
if (generic_lambda_fn_p (containing_function))
saw_generic_lambda = true;
if (containing_function && DECL_TEMPLATE_INFO (context)
&& LAMBDA_FUNCTION_P (containing_function))
{
/* Check whether we've already built a proxy;
insert_pending_capture_proxies doesn't update
local_specializations. */
tree d = lookup_name (DECL_NAME (decl));
if (d && is_capture_proxy (d)
&& DECL_CONTEXT (d) == containing_function)
return d;
}
if (TYPE_CLASS_SCOPE_P (closure))
/* A lambda in an NSDMI (c++/64496). */
break;
/* If we are in a lambda function, we can move out until we hit
1. the context,
2. a non-lambda function, or
3. a non-default capturing lambda function. */
while (context != containing_function
/* containing_function can be null with invalid generic lambdas. */
&& containing_function
&& LAMBDA_FUNCTION_P (containing_function))
{
tree closure = DECL_CONTEXT (containing_function);
lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure);
if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr)
== CPLD_NONE)
break;
if (TYPE_CLASS_SCOPE_P (closure))
/* A lambda in an NSDMI (c++/64496). */
break;
lambda_stack = tree_cons (NULL_TREE,
lambda_expr,
lambda_stack);
if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr)
== CPLD_NONE)
break;
containing_function
= decl_function_context (containing_function);
}
lambda_stack = tree_cons (NULL_TREE,
lambda_expr,
lambda_stack);
containing_function
= decl_function_context (containing_function);
}
/* In a lambda within a template, wait until instantiation
time to implicitly capture. */
if (context == containing_function
&& DECL_TEMPLATE_INFO (containing_function)
&& any_dependent_template_arguments_p (DECL_TI_ARGS
(containing_function)))
return decl;
/* Core issue 696: "[At the July 2009 meeting] the CWG expressed
support for an approach in which a reference to a local
@ -3343,26 +3359,11 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
the complexity of the problem"
FIXME update for final resolution of core issue 696. */
if (decl_maybe_constant_var_p (decl))
if (decl_constant_var_p (decl))
{
if (processing_template_decl && !saw_generic_lambda)
/* In a non-generic lambda within a template, wait until instantiation
time to decide whether to capture. For a generic lambda, we can't
wait until we instantiate the op() because the closure class is
already defined at that point. FIXME to get the semantics exactly
right we need to partially-instantiate the lambda body so the only
dependencies left are on the generic parameters themselves. This
probably means moving away from our current model of lambdas in
templates (instantiating the closure type) to one based on creating
the closure type when instantiating the lambda context. That is
probably also the way to handle lambdas within pack expansions. */
return decl;
else if (decl_constant_var_p (decl))
{
tree t = maybe_constant_value (convert_from_reference (decl));
if (TREE_CONSTANT (t))
return t;
}
tree t = maybe_constant_value (convert_from_reference (decl));
if (TREE_CONSTANT (t))
return t;
}
if (lambda_expr && VAR_P (decl)

View file

@ -0,0 +1,14 @@
// { dg-do run }
// { dg-options -std=c++17 }
template <class... T>
auto f() {
int i = 42;
return ([i]{ return T(i); }() + ...);
}
int main()
{
if (f<int,double>() != 84)
__builtin_abort();
}

View file

@ -43,7 +43,7 @@ template <class T>
void f4(int i) {
[=]{
int j = i; // { dg-message "shadowed declaration" }
int i; // { dg-warning "shadows a lambda capture" }
int i; // { dg-warning "shadows a " }
i = 1;
};
}