On Feb 7, 2019, Jason Merrill <ja...@redhat.com> wrote: >> + PR c++/86322. */
> Wrong PR number. Thanks >> + if (local_specializations) >> + if (tree r = retrieve_local_specialization (t)) >> + return r; > Hmm, I would expect this to do the wrong thing for pack expansion of a > lambda, giving us only one rather than one per element of the > expansion. Oooh. Indeed, I hadn't realized this was possible. I think we should separate local_specializations arising from different pack expansion bindings, considering they won't always appear in tsubst args: diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 0177d7f77891..9f954698f454 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11698,6 +11698,10 @@ gen_elem_of_pack_expansion_instantiation (tree pattern, ARGUMENT_PACK_SELECT_INDEX (aps) = index; } + // Any local specialization bindings arising from this substitution + // cannot be reused for a different INDEX. + local_specialization_stack lss (lss_copy); + /* Substitute into the PATTERN with the (possibly altered) arguments. */ if (pattern == in_decl) > For instance, in lambda-variadic5.C we should get three > instantiations of the "print" function template; can you add that > check to the test? This causes lambda-variadic5.C to get 6 separate lambdas, instead of 2, so it passes after the following patchlet: diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C index 97f64cd761a7..1f931757b72f 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C @@ -1,5 +1,7 @@ // PR c++/47226 // { dg-do compile { target c++11 } } +// { dg-options "-fdump-tree-original" } +// { dg-final { scan-tree-dump-times "::<lambda\\(\\)> \\(null\\)" 6 "original" } } template<class T> void print(const T&) {} Now, should I adjust lambda-variadic5.C to check for the presence of the expencted count of lambdas in compiler dumps? Or should I go for a new test? Thinking of how to test for it drew my attention to issues about lambda naming, that may have implications on ABI and duplicate code removal. There are two issues I'm concerned with: - we increment lambda_cnt when introducing a lambda within a generic, and then increment it again as we specialize, partially or fully, each lambda expr/object/type set. This makes for gaps in numbering of full specializations and, being a global counter, does not ensure the assignment of the same symbol name to corresponding instantiations in separate translation units - we don't seem to attempt to reuse lambdas across different specializations of argument pack expansions. I guess that's ok, we don't attempt to reuse them across different specializations of the enclosing template function or class either, but, unlike enclosing contexts, different argument pack indices do not mandate different mangled names. This came up because I was thinking of having a sub-map in local_specializations, so that the lambda would map to another map in which we could look up template arguments to find a specific instance. That wouldn't work, in part because args does not provide the entire set of substitutions, and in another part because we don't seem to be able to identify, in patterns undergoing substitution but before actually performing the substitution, the set of (implied?) template arguments that might affect the substitution result. To make this concrete: in [&t]{return([]{return(0);}()+t);}... we might be able to reuse the nested lambda across expansions of the enclosing lambda. except that, being nested, their contexts wouldn't allow that. but what if they were siblings, say: ([&t]{return(t);}()+[]{return(0);}())... here the latter is in no way affected by the t pack, so we could create a single lambda, reusing it for all indices in t. The patchlet above will stand in the way of it, but IIUC it doesn't matter, we're not supposed/expected to do that, even when the decision on whether or not to reuse might affect symbol naming, e.g. if it appears in an initializer of a data member. Here's the patch I'm testing. OK if it passes? [PR87322] move cp_evaluated up to tsubst all lambda parms From: Alexandre Oliva <aol...@redhat.com> A lambda capture variable initialized with a lambda expr taking more than one parameter got us confused. The first problem was that the parameter list was cut short during tsubsting because we tsubsted it with cp_unevaluated_operand. We reset it right after, to tsubst the function body, so I've moved the reset up so that it's in effect while processing the parameters as well. The second problem was that the lambda expr appeared twice, once in a decltype that gave the capture variable its type, and once in its initializer. This caused us to instantiate two separate lambda exprs and closure types, and then to flag that the lambda expr in the initializer could not be converted to the unrelated closure type determined for the capture variable. Recording the tsubsted expr in the local specialization map, and retrieving it for reuse fixed it. However, that required some care to avoid reusing the lambda expr across different indices in pack expansions. for gcc/cp/ChangeLog PR c++/87322 * pt.c (tsubst_lambda_expr): Avoid duplicate tsubsting. Move cp_evaluated resetting before signature tsubsting. (gen_elem_of_pack_expansion_instantiation): Separate local specializations per index. for gcc/testsuite/ChangeLog PR c++/87322 * g++.dg/cpp1y/pr87322.C: New. * g++.dg/cpp0x/lambda/lambda-variadic5.C: Test that we instantiate the expected number of lambda functions. --- gcc/cp/pt.c | 22 ++++++++++++++++--- .../g++.dg/cpp0x/lambda/lambda-variadic5.C | 2 ++ gcc/testsuite/g++.dg/cpp1y/pr87322.C | 23 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr87322.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index b8fbf4046f07..9f954698f454 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11698,6 +11698,10 @@ gen_elem_of_pack_expansion_instantiation (tree pattern, ARGUMENT_PACK_SELECT_INDEX (aps) = index; } + // Any local specialization bindings arising from this substitution + // cannot be reused for a different INDEX. + local_specialization_stack lss (lss_copy); + /* Substitute into the PATTERN with the (possibly altered) arguments. */ if (pattern == in_decl) @@ -17912,8 +17916,17 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) tree oldfn = lambda_function (t); in_decl = oldfn; + /* If we have already specialized this lambda expr, reuse it. See + PR c++/87322. */ + if (local_specializations) + if (tree r = retrieve_local_specialization (t)) + return r; + tree r = build_lambda_expr (); + if (local_specializations) + register_local_specialization (r, t); + LAMBDA_EXPR_LOCATION (r) = LAMBDA_EXPR_LOCATION (t); LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) @@ -18005,6 +18018,11 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) r = error_mark_node; else { + /* The body of a lambda-expression is not a subexpression of the + enclosing expression. Parms are to have DECL_CHAIN tsubsted, + which would be skipped if cp_unevaluated_operand. */ + cp_evaluated ev; + /* Fix the type of 'this'. */ fntype = build_memfn_type (fntype, type, type_memfn_quals (fntype), @@ -18026,10 +18044,6 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) /* Let finish_function set this. */ DECL_DECLARED_CONSTEXPR_P (fn) = false; - /* The body of a lambda-expression is not a subexpression of the - enclosing expression. */ - cp_evaluated ev; - bool nested = cfun; if (nested) push_function_context (); diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C index 97f64cd761a7..1f931757b72f 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C @@ -1,5 +1,7 @@ // PR c++/47226 // { dg-do compile { target c++11 } } +// { dg-options "-fdump-tree-original" } +// { dg-final { scan-tree-dump-times "::<lambda\\(\\)> \\(null\\)" 6 "original" } } template<class T> void print(const T&) {} diff --git a/gcc/testsuite/g++.dg/cpp1y/pr87322.C b/gcc/testsuite/g++.dg/cpp1y/pr87322.C new file mode 100644 index 000000000000..8f52e0e3b99b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr87322.C @@ -0,0 +1,23 @@ +// { dg-do compile { target c++14 } } + +#include <array> +#include <algorithm> + +int main() +{ + constexpr std::array<std::array<double,2>,3> my_mat { + { { 1., 1. }, + { 1., 1. }, + { 1., 1. }, } + }; + + std::for_each(my_mat.begin(), my_mat.end(), [ + inner_func = [] (auto a, auto b) { return a + b; } ](auto& row) { + std::for_each(row.begin(), row.end(), [&, + inner_func2 = [] (auto a, auto b) { return a + b; } ] + (const double&) { + return; + }); + }); + +} -- Alexandre Oliva, freedom fighter https://FSFLA.org/blogs/lxo Be the change, be Free! FSF Latin America board member GNU Toolchain Engineer Free Software Evangelist Hay que enGNUrecerse, pero sin perder la terGNUra jamás-GNUChe