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. Regstrapped on x86_64- and i686-linux-gnu. Ok to install? for gcc/cp/ChangeLog PR c++/87322 * pt.c (tsubst_lambda_expr): Avoid duplicate tsubsting. Move cp_evaluated resetting before signature tsubsting. for gcc/testsuite/ChangeLog PR c++/87322 * g++.dg/cpp1y/pr87322.C: New. --- gcc/cp/pt.c | 18 ++++++++++++++---- gcc/testsuite/g++.dg/cpp1y/pr87322.C | 23 +++++++++++++++++++++++ 2 files changed, 37 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..3dffa8d2de88 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -17912,8 +17912,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++/86322. */ + 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 +18014,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 +18040,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/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