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(); }

Reply via email to