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:
parent
72932ccf0a
commit
f44a8dd56f
11 changed files with 869 additions and 641 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
|
|
1153
gcc/cp/pt.c
1153
gcc/cp/pt.c
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
|
14
gcc/testsuite/g++.dg/cpp1z/fold-lambda.C
Normal file
14
gcc/testsuite/g++.dg/cpp1z/fold-lambda.C
Normal 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();
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue