Here we were failing to handle static locals referred to from a generic
lambda properly: we decided that in that situation rather than try to
look up the primary decl for the variable (since its function is
probably out of scope when the lambda op() is instantiated), we can just
build a new VAR_DECL and use that instead. The problem with this was
that we weren't setting DECL_CONTEXT on the new decl, so it mangled
differently from the real decl, so references within the lambda were
finding a different object.
Fixed for statics in non-template functions by just using the variable
directly, and for statics in template functions by setting DECL_CONTEXT
appropriately.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 120812acdc3feda6cd79f634a4ac26ae0db8c087
Author: Jason Merrill <ja...@redhat.com>
Date: Fri May 20 14:00:38 2016 -0400
PR c++/70735 - generic lambda and local static variable
* pt.c (tsubst_copy): Just return a local variable from
non-template context. Don't call rest_of_decl_compilation for
duplicated static locals.
(tsubst_decl): Set DECL_CONTEXT of local static from another
function.
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 2bba571..59d6a95 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12280,6 +12280,14 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
local_p = true;
/* Subsequent calls to pushdecl will fill this in. */
ctx = NULL_TREE;
+ /* Unless this is a reference to a static variable from an
+ 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);
+ if (fn != current_function_decl)
+ ctx = fn;
+ }
spec = retrieve_local_specialization (t);
}
/* If we already have the specialization we need, there is
@@ -13991,7 +13999,8 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case FUNCTION_DECL:
if (DECL_LANG_SPECIFIC (t) && DECL_TEMPLATE_INFO (t))
r = tsubst (t, args, complain, in_decl);
- else if (local_variable_p (t))
+ else if (local_variable_p (t)
+ && uses_template_parms (DECL_CONTEXT (t)))
{
r = retrieve_local_specialization (t);
if (r == NULL_TREE)
@@ -14035,14 +14044,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
gcc_assert (cp_unevaluated_operand || TREE_STATIC (r)
|| decl_constant_var_p (r)
|| errorcount || sorrycount);
- if (!processing_template_decl)
- {
- if (TREE_STATIC (r))
- rest_of_decl_compilation (r, toplevel_bindings_p (),
- at_eof);
- else
- r = process_outer_var_ref (r, complain);
- }
+ if (!processing_template_decl
+ && !TREE_STATIC (r))
+ r = process_outer_var_ref (r, complain);
}
/* Remember this for subsequent uses. */
if (local_specializations)
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-static1.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-static1.C
new file mode 100644
index 0000000..a1667a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-static1.C
@@ -0,0 +1,13 @@
+// PR c++/70735
+// { dg-do run { target c++1y } }
+
+int main()
+{
+ static int a;
+ auto f = [](auto) { return a; };
+ if (f(0) != 0)
+ __builtin_abort();
+ a = 1;
+ if (f(0) != 1)
+ __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-static2.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-static2.C
new file mode 100644
index 0000000..51bf75f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-static2.C
@@ -0,0 +1,19 @@
+// PR c++/70735
+// { dg-do run { target c++1y } }
+
+template <class T>
+static void g()
+{
+ static int a;
+ auto f = [](auto) { return a; };
+ if (f(0) != 0)
+ __builtin_abort();
+ a = 1;
+ if (f(0) != 1)
+ __builtin_abort();
+}
+
+int main()
+{
+ g<int>();
+}