When tsubst_decl creates a new local static variable, it checks to see if it belongs to current_function_decl. It has done this by tsubsting the old DECL_CONTEXT, but that doesn't work with the new lambda model, where we can't get to the new lambda op() by tsubsting the old one. So this patch introduces a new function enclosing_instantiation_of, which looks out from current_function_decl to find which enclosing function corresponds to the context of the variable in the template.
I've attached two versions of this patch: one which matches up lambdas based on nesting level within an enclosing function, and one which uses a hash table. I've been ambivalent about which to go with; the first has more complicated logic, but uses less space, so I think that's the one I'm going to check in now. I might revisit this choice if I find other uses for the hash table. Tested x86_64-pc-linux-gnu, applying to trunk.
commit 4c88ba8c2a00d662c33f0e2569526122759dc2cb Author: Jason Merrill <ja...@redhat.com> Date: Wed Aug 30 16:26:24 2017 -0400 PR c++/82029 - __PRETTY_FUNCTION__ in lambda in template * pt.c (enclosing_instantiation_of, lambda_fn_in_template_p) (regenerated_lambda_fn_p): New. (tsubst_decl) [VAR_DECL]: Use enclosing_instantiation_of. (tsubst_copy) [VAR_DECL]: Likewise. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f4868ab..d5ab939 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12587,6 +12587,63 @@ tsubst_template_decl (tree t, tree args, tsubst_flags_t complain, return r; } +/* True if FN is the op() for a lambda in an uninstantiated template. */ + +bool +lambda_fn_in_template_p (tree fn) +{ + if (!LAMBDA_FUNCTION_P (fn)) + return false; + tree closure = DECL_CONTEXT (fn); + return CLASSTYPE_TEMPLATE_INFO (closure) != NULL_TREE; +} + +/* True if FN is the op() for a lambda regenerated from a lambda in an + uninstantiated template. */ + +bool +regenerated_lambda_fn_p (tree fn) +{ + return (LAMBDA_FUNCTION_P (fn) + && !DECL_TEMPLATE_INSTANTIATION (fn)); +} + +/* We're instantiating a variable from template function TCTX. Return the + corresponding current enclosing scope. This gets complicated because lambda + functions in templates are regenerated rather than instantiated, but generic + lambda functions are subsequently instantiated. */ + +static tree +enclosing_instantiation_of (tree tctx) +{ + tree fn = current_function_decl; + int lambda_count = 0; + + for (; tctx && lambda_fn_in_template_p (tctx); + tctx = decl_function_context (tctx)) + ++lambda_count; + for (; fn; fn = decl_function_context (fn)) + { + tree lambda = fn; + int flambda_count = 0; + for (; fn && regenerated_lambda_fn_p (fn); + fn = decl_function_context (fn)) + ++flambda_count; + if (DECL_TEMPLATE_INFO (fn) + ? most_general_template (fn) != most_general_template (tctx) + : fn != tctx) + continue; + if (lambda_count) + { + fn = lambda; + while (flambda_count-- > lambda_count) + fn = decl_function_context (fn); + } + return fn; + } + gcc_unreachable (); +} + /* Substitute the ARGS into the T, which is a _DECL. Return the result of the substitution. Issue error and warning messages under control of COMPLAIN. */ @@ -12955,7 +13012,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) enclosing function, in which case we need to fill it in now. */ if (TREE_STATIC (t)) { - tree fn = tsubst (DECL_CONTEXT (t), args, complain, in_decl); + tree fn = enclosing_instantiation_of (DECL_CONTEXT (t)); if (fn != current_function_decl) ctx = fn; } @@ -14734,9 +14791,7 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (r && !is_capture_proxy (r)) { /* Make sure that the one we found is the one we want. */ - tree ctx = DECL_CONTEXT (t); - if (DECL_LANG_SPECIFIC (ctx) && DECL_TEMPLATE_INFO (ctx)) - ctx = tsubst (ctx, args, complain, in_decl); + tree ctx = enclosing_instantiation_of (DECL_CONTEXT (t)); if (ctx != DECL_CONTEXT (r)) r = NULL_TREE; } diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-__func__2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-__func__2.C new file mode 100644 index 0000000..bc0e3b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-__func__2.C @@ -0,0 +1,13 @@ +// PR c++/82029 +// { dg-do compile { target c++11 } } + +template <typename> struct A { + void m_fn1() { + [] { return __func__; }(); + } +}; +struct B { + A<int> a; + void m_fn2(); +}; +void B::m_fn2() { a.m_fn1(); }
commit 3edefaa7d1dd5d6b947d4311be1e508552b303c9 Author: Jason Merrill <ja...@redhat.com> Date: Wed Aug 30 16:26:24 2017 -0400 PR c++/82029 - __PRETTY_FUNCTION__ in lambda in template * pt.c (enclosing_instantiation_of): New. (tsubst_decl) [VAR_DECL]: Use it. (tsubst_copy) [VAR_DECL]: Likewise. (lambda_fn_origin): New hash table. (tsubst_lambda_expr): Populate it. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f4868ab..41fecaf 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -12587,6 +12587,37 @@ tsubst_template_decl (tree t, tree args, tsubst_flags_t complain, return r; } +/* A hash table from a lambda op() in a template instantiation to the op() of + the corresponding lambda in the uninstantiated template. */ +static GTY(()) hash_map<tree, tree> *lambda_fn_origin; + +/* We're instantiating a variable from template function TCTX. Return the + corresponding current enclosing scope. This gets complicated because lambda + functions in templates are regenerated rather than instantiated, but generic + lambda functions are subsequently instantiated. */ + +static tree +enclosing_instantiation_of (tree tctx) +{ + for (tree fn = current_function_decl; fn; fn = decl_function_context (fn)) + { + tree origin; + tree *slot; + + if (LAMBDA_FUNCTION_P (fn) && lambda_fn_origin + && (slot = lambda_fn_origin->get (fn))) + origin = *slot; + else if (DECL_TEMPLATE_INFO (fn)) + origin = DECL_TEMPLATE_RESULT (most_general_template (fn)); + else + origin = fn; + + if (origin == tctx) + return fn; + } + gcc_unreachable (); +} + /* Substitute the ARGS into the T, which is a _DECL. Return the result of the substitution. Issue error and warning messages under control of COMPLAIN. */ @@ -12955,7 +12986,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain) enclosing function, in which case we need to fill it in now. */ if (TREE_STATIC (t)) { - tree fn = tsubst (DECL_CONTEXT (t), args, complain, in_decl); + tree fn = enclosing_instantiation_of (DECL_CONTEXT (t)); if (fn != current_function_decl) ctx = fn; } @@ -14734,9 +14765,7 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (r && !is_capture_proxy (r)) { /* Make sure that the one we found is the one we want. */ - tree ctx = DECL_CONTEXT (t); - if (DECL_LANG_SPECIFIC (ctx) && DECL_TEMPLATE_INFO (ctx)) - ctx = tsubst (ctx, args, complain, in_decl); + tree ctx = enclosing_instantiation_of (DECL_CONTEXT (t)); if (ctx != DECL_CONTEXT (r)) r = NULL_TREE; } @@ -16855,6 +16884,10 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) finish_member_declaration (fn); } + if (!lambda_fn_origin) + lambda_fn_origin = hash_map<tree,tree>::create_ggc (37); + lambda_fn_origin->put (fn, oldfn); + /* Let finish_function set this. */ DECL_DECLARED_CONSTEXPR_P (fn) = false; diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-__func__2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-__func__2.C new file mode 100644 index 0000000..bc0e3b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-__func__2.C @@ -0,0 +1,13 @@ +// PR c++/82029 +// { dg-do compile { target c++11 } } + +template <typename> struct A { + void m_fn1() { + [] { return __func__; }(); + } +}; +struct B { + A<int> a; + void m_fn2(); +}; +void B::m_fn2() { a.m_fn1(); }