In dealing with variadic capture, we've historically not bothered
trying to track the capture proxies and just dealt with the
COMPONENT_REFs directly.  This was breaking the new DECL_CAPTURED_VAR
code.  The simplest solution for now is to make an exception for this
case; this doesn't actually cause any problems because the primary use
of DECL_CAPTURED_VAR is looking up the value of a constant variable,
and a function parameter pack is never a constant variable.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 35c8dba74b3dac15cc440e060e2e7f829202e85c
Author: Jason Merrill <ja...@redhat.com>
Date:   Thu Feb 1 17:34:42 2018 -0500

            PR c++/84160 - ICE with nested variadic capture.
    
            * lambda.c (is_capture_proxy_with_ref): New.
            (insert_capture_proxy): Don't set DECL_CAPTURED_VARIABLE from a
            COMPONENT_REF.
            * expr.c (mark_use): Use is_capture_proxy_with_ref.
            * constexpr.c (potential_constant_expression_1): Likewise.
            * semantics.c (process_outer_var_ref): Likewise.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 1390405c416..171c389515a 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -5369,7 +5369,7 @@ potential_constant_expression_1 (tree t, bool want_rval, 
bool strict, bool now,
     case VAR_DECL:
       if (DECL_HAS_VALUE_EXPR_P (t))
        {
-         if (now && is_normal_capture_proxy (t))
+         if (now && is_capture_proxy_with_ref (t))
            {
              /* -- in a lambda-expression, a reference to this or to a
                 variable with automatic storage duration defined outside that
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5f14e514638..a53f4fd9c03 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6901,6 +6901,7 @@ extern void insert_capture_proxy          (tree);
 extern void insert_pending_capture_proxies     (void);
 extern bool is_capture_proxy                   (tree);
 extern bool is_normal_capture_proxy             (tree);
+extern bool is_capture_proxy_with_ref           (tree);
 extern void register_capture_members           (tree);
 extern tree lambda_expr_this_capture            (tree, bool);
 extern void maybe_generic_this_capture         (tree, tree);
diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c
index 2e679868970..b2c8cfaf88c 100644
--- a/gcc/cp/expr.c
+++ b/gcc/cp/expr.c
@@ -111,7 +111,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
     {
     case VAR_DECL:
     case PARM_DECL:
-      if (rvalue_p && is_normal_capture_proxy (expr))
+      if (rvalue_p && is_capture_proxy_with_ref (expr))
        {
          /* Look through capture by copy.  */
          tree cap = DECL_CAPTURED_VARIABLE (expr);
@@ -154,7 +154,7 @@ mark_use (tree expr, bool rvalue_p, bool read_p,
        {
          /* Try to look through the reference.  */
          tree ref = TREE_OPERAND (expr, 0);
-         if (rvalue_p && is_normal_capture_proxy (ref))
+         if (rvalue_p && is_capture_proxy_with_ref (ref))
            {
              /* Look through capture by reference.  */
              tree cap = DECL_CAPTURED_VARIABLE (ref);
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index e1caaef6fe9..ff8236ad316 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -290,13 +290,24 @@ is_normal_capture_proxy (tree decl)
   return DECL_NORMAL_CAPTURE_P (val);
 }
 
+/* Returns true iff DECL is a capture proxy for which we can use
+   DECL_CAPTURED_VARIABLE.  In effect, this is a normal proxy other than a
+   nested capture of a function parameter pack.  */
+
+bool
+is_capture_proxy_with_ref (tree var)
+{
+  return (is_normal_capture_proxy (var) && DECL_LANG_SPECIFIC (var)
+         && DECL_CAPTURED_VARIABLE (var));
+}
+
 /* VAR is a capture proxy created by build_capture_proxy; add it to the
    current function, which is the operator() for the appropriate lambda.  */
 
 void
 insert_capture_proxy (tree var)
 {
-  if (is_normal_capture_proxy (var))
+  if (is_capture_proxy_with_ref (var))
     {
       tree cap = DECL_CAPTURED_VARIABLE (var);
       if (CHECKING_P)
@@ -443,11 +454,20 @@ build_capture_proxy (tree member, tree init)
            init = TREE_OPERAND (init, 0);
          STRIP_NOPS (init);
        }
-      gcc_assert (VAR_P (init) || TREE_CODE (init) == PARM_DECL);
-      while (is_normal_capture_proxy (init))
-       init = DECL_CAPTURED_VARIABLE (init);
-      retrofit_lang_decl (var);
-      DECL_CAPTURED_VARIABLE (var) = init;
+
+      if (TREE_CODE (init) == COMPONENT_REF)
+       /* We're capturing a capture of a function parameter pack, and have
+          lost track of the original variable.  It's not important to have
+          DECL_CAPTURED_VARIABLE in this case, since a function parameter pack
+          isn't a constant variable, so don't bother trying to set it.  */;
+      else
+       {
+         gcc_assert (VAR_P (init) || TREE_CODE (init) == PARM_DECL);
+         while (is_normal_capture_proxy (init))
+           init = DECL_CAPTURED_VARIABLE (init);
+         retrofit_lang_decl (var);
+         DECL_CAPTURED_VARIABLE (var) = init;
+       }
     }
 
   if (name == this_identifier)
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 76160345882..ea92da37625 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3321,7 +3321,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t 
complain, bool odr_use)
     {
       /* Check whether we've already built a proxy.  */
       tree var = decl;
-      while (is_normal_capture_proxy (var))
+      while (is_capture_proxy_with_ref (var))
        var = DECL_CAPTURED_VARIABLE (var);
       tree d = retrieve_local_specialization (var);
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic6.C 
b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic6.C
new file mode 100644
index 00000000000..d9707d05c70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic6.C
@@ -0,0 +1,12 @@
+// PR c++/84160
+// { dg-do compile { target c++11 } }
+
+template < typename ... T > void f (T ... a) 
+{
+  [a ...] { [a ...] {}; };
+}
+
+void g ()
+{
+  f < int > (0);
+}

Reply via email to