On Wed, Mar 20, 2019 at 11:10:19AM -0400, Nathan Sidwell wrote:
> I was unclear.  I was for the lazy creation of the hash.  I just think it
> can be lazily created at either the first or second explicit capture.

On top of the https://gcc.gnu.org/ml/gcc-patches/2019-03/msg00991.html
patch I've just posted here are two different patches to do this, the
first one optimizes just the zero captures case and creates a hash_set
on the first add_capture, the second one optimizes zero and one captures
case and creates a hash_set on the second add_capture.

        Jakub
2019-03-20  Jakub Jelinek  <ja...@redhat.com>

        PR c++/89767
        * cp-tree.h (add_capture): Add ids argument.
        * parser.c (cp_parser_lambda_introducer): Add ids variable and pass
        its address to add_capture calls.
        * lambda.c (add_capture): Add ids argument, don't use
        IDENTIFIER_MARKED, instead use ids hash_set for that.
        (register_capture_members): Don't clear IDENTIFIER_MARKED here.
        (add_default_capture): Adjust add_capture caller.

        * g++.dg/cpp1y/lambda-init18.C: New test.
        * g++.dg/cpp1y/lambda-init19.C: New test.
        * g++.dg/cpp1y/pr89767.C: New test.

--- gcc/cp/parser.c.jj  2019-03-20 17:05:02.072175252 +0100
+++ gcc/cp/parser.c     2019-03-20 17:36:04.360920665 +0100
@@ -10547,6 +10547,7 @@ cp_parser_lambda_introducer (cp_parser*
        error ("non-local lambda expression cannot have a capture-default");
     }
 
+  hash_set<tree> ids (EMPTY_HASH);
   while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
     {
       cp_token* capture_token;
@@ -10586,7 +10587,7 @@ cp_parser_lambda_introducer (cp_parser*
                       /*id=*/this_identifier,
                       /*initializer=*/finish_this_expr (),
                       /*by_reference_p=*/true,
-                      explicit_init_p);
+                      explicit_init_p, &ids);
          continue;
        }
 
@@ -10604,7 +10605,7 @@ cp_parser_lambda_introducer (cp_parser*
                       /*id=*/this_identifier,
                       /*initializer=*/finish_this_expr (),
                       /*by_reference_p=*/false,
-                      explicit_init_p);
+                      explicit_init_p, &ids);
          continue;
        }
 
@@ -10757,7 +10758,7 @@ cp_parser_lambda_introducer (cp_parser*
                   capture_id,
                   capture_init_expr,
                   /*by_reference_p=*/capture_kind == BY_REFERENCE,
-                  explicit_init_p);
+                  explicit_init_p, &ids);
 
       /* If there is any qualification still in effect, clear it
         now; we will be starting fresh with the next capture.  */
--- gcc/cp/lambda.c.jj  2019-03-20 17:05:02.179173500 +0100
+++ gcc/cp/lambda.c     2019-03-20 17:36:39.034359206 +0100
@@ -500,11 +500,12 @@ vla_capture_type (tree array_type)
 /* From an ID and INITIALIZER, create a capture (by reference if
    BY_REFERENCE_P is true), add it to the capture-list for LAMBDA,
    and return it.  If ID is `this', BY_REFERENCE_P says whether
-   `*this' is captured by reference.  */
+   `*this' is captured by reference.  IDS is used during introducer
+   parsing to detect duplicate captures if there are many captures.  */
 
 tree
 add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
-            bool explicit_init_p)
+            bool explicit_init_p, hash_set<tree> *ids)
 {
   char *buf;
   tree type, member, name;
@@ -605,13 +606,16 @@ add_capture (tree lambda, tree id, tree
      for duplicates.  */
   if (!LAMBDA_EXPR_CLOSURE (lambda))
     {
-      if (IDENTIFIER_MARKED (name))
+      gcc_assert (ids);
+      if (ids->size () == 0)
+       ids->create ();
+      if (ids->contains (name))
        {
          pedwarn (input_location, 0,
                   "already captured %qD in lambda expression", id);
          return NULL_TREE;
        }
-      IDENTIFIER_MARKED (name) = true;
+      ids->add (name);
     }
 
   if (variadic)
@@ -674,8 +678,6 @@ register_capture_members (tree captures)
   if (PACK_EXPANSION_P (field))
     field = PACK_EXPANSION_PATTERN (field);
 
-  /* We set this in add_capture to avoid duplicates.  */
-  IDENTIFIER_MARKED (DECL_NAME (field)) = false;
   finish_member_declaration (field);
 }
 
@@ -706,7 +708,7 @@ add_default_capture (tree lambda_stack,
                            (this_capture_p
                             || (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda)
                                 == CPLD_REFERENCE)),
-                           /*explicit_init_p=*/false);
+                           /*explicit_init_p=*/false, NULL);
       initializer = convert_from_reference (var);
 
       /* Warn about deprecated implicit capture of this via [=].  */
--- gcc/cp/cp-tree.h.jj 2019-03-20 17:05:02.133174253 +0100
+++ gcc/cp/cp-tree.h    2019-03-20 17:35:25.113556194 +0100
@@ -7150,7 +7150,8 @@ extern tree lambda_return_type                    (tree);
 extern tree lambda_proxy_type                  (tree);
 extern tree lambda_function                    (tree);
 extern void apply_deduced_return_type           (tree, tree);
-extern tree add_capture                         (tree, tree, tree, bool, bool);
+extern tree add_capture                         (tree, tree, tree, bool, bool,
+                                                hash_set<tree> *);
 extern tree add_default_capture                 (tree, tree, tree);
 extern void insert_capture_proxy               (tree);
 extern void insert_pending_capture_proxies     (void);
--- gcc/testsuite/g++.dg/cpp1y/lambda-init18.C.jj       2019-03-20 
17:35:25.121556064 +0100
+++ gcc/testsuite/g++.dg/cpp1y/lambda-init18.C  2019-03-20 17:35:25.121556064 
+0100
@@ -0,0 +1,12 @@
+// PR c++/89767
+// { dg-do compile { target c++14 } }
+
+void bar (int);
+
+void
+foo ()
+{
+  int x = 0;
+  auto z = [x, y = [x] { bar (x); }] { y (); bar (x); };
+  z ();
+}
--- gcc/testsuite/g++.dg/cpp1y/lambda-init19.C.jj       2019-03-20 
17:35:25.121556064 +0100
+++ gcc/testsuite/g++.dg/cpp1y/lambda-init19.C  2019-03-20 17:35:25.121556064 
+0100
@@ -0,0 +1,15 @@
+// PR c++/89767
+// { dg-do compile { target c++14 } }
+
+void bar (int);
+
+void
+foo ()
+{
+  int x = 0;
+  int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0;
+  auto z = [x, y = [x] { bar (x); }, x] { y (); bar (x); };    // { dg-error 
"already captured 'x' in lambda expression" }
+  auto w = [x, a, b, c, d, y = [x] { bar (x); }, e, f, g, h, x] { y (); bar (x 
+ a + b + c + d + e + f + g + h); };    // { dg-error "already captured 'x' in 
lambda expression" }
+  z ();
+  w ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr89767.C.jj     2019-03-20 17:35:25.121556064 
+0100
+++ gcc/testsuite/g++.dg/cpp1y/pr89767.C        2019-03-20 17:35:25.121556064 
+0100
@@ -0,0 +1,32 @@
+// PR c++/89767
+// { dg-do compile { target c++14 } }
+// { dg-options "-O2 -Wall" }
+
+template <typename d> struct e { using g = d; };
+template <typename d, template <typename> class> using h = e<d>;
+template <typename d, template <typename> class i>
+using j = typename h<d, i>::g;
+template <typename c> int k(c);
+template <typename...> class au;
+struct l { template <typename c> using m = typename c::f; };
+struct s : l { using af = j<au<int, int> *, m>; };
+template <unsigned long, typename> struct o;
+template <long p, typename c> using q = typename o<p, c>::g;
+template <typename> struct r;
+template <typename c> struct r<c *> { typedef c aj; };
+template <typename ak, typename> struct al { typename r<ak>::aj operator*(); 
void operator++(); };
+template <typename am, typename an, typename ao>
+bool operator!=(al<am, ao>, al<an, ao>);
+template <unsigned long, typename...> struct ap;
+template <unsigned long aq, typename ar, typename... as>
+struct ap<aq, ar, as...> : ap<1, as...> {};
+template <unsigned long aq, typename ar> struct ap<aq, ar> {};
+template <typename... at> class au : public ap<0, at...> {};
+template <unsigned long p, typename ar, typename... as>
+struct o<p, au<ar, as...>> : o<p - 1, au<as...>> {};
+template <typename ar, typename... as> struct o<0, au<ar, as...>> { typedef ar 
g; };
+template <long p, typename ar> constexpr ar av(ap<p, ar> __t) { return ar (); }
+template <int p, typename... at> constexpr q<p, au<at...>> aw(au<at...> __t) { 
av<p>(__t); return q<p, au<at...>> (); }
+struct bg { typedef s::af af; };
+struct F { typedef al<bg::af, int> bk; bk begin(); bk end(); };
+void bo() { int t = 0; F cv; for (auto bp : cv) [t, n = k(aw<1>(bp))] {}; }
2019-03-20  Jakub Jelinek  <ja...@redhat.com>

        PR c++/89767
        * cp-tree.h (add_capture): Add ids argument.
        * parser.c (cp_parser_lambda_introducer): Add ids variable and pass
        its address to add_capture calls.
        * lambda.c (add_capture): Add ids argument, don't use
        IDENTIFIER_MARKED, instead use ids hash_set for that.
        (register_capture_members): Don't clear IDENTIFIER_MARKED here.
        (add_default_capture): Adjust add_capture caller.

        * g++.dg/cpp1y/lambda-init18.C: New test.
        * g++.dg/cpp1y/lambda-init19.C: New test.
        * g++.dg/cpp1y/pr89767.C: New test.

--- gcc/cp/parser.c.jj  2019-03-20 17:05:02.072175252 +0100
+++ gcc/cp/parser.c     2019-03-20 17:36:04.360920665 +0100
@@ -10547,6 +10547,7 @@ cp_parser_lambda_introducer (cp_parser*
        error ("non-local lambda expression cannot have a capture-default");
     }
 
+  hash_set<tree> ids (EMPTY_HASH);
   while (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
     {
       cp_token* capture_token;
@@ -10586,7 +10587,7 @@ cp_parser_lambda_introducer (cp_parser*
                       /*id=*/this_identifier,
                       /*initializer=*/finish_this_expr (),
                       /*by_reference_p=*/true,
-                      explicit_init_p);
+                      explicit_init_p, &ids);
          continue;
        }
 
@@ -10604,7 +10605,7 @@ cp_parser_lambda_introducer (cp_parser*
                       /*id=*/this_identifier,
                       /*initializer=*/finish_this_expr (),
                       /*by_reference_p=*/false,
-                      explicit_init_p);
+                      explicit_init_p, &ids);
          continue;
        }
 
@@ -10757,7 +10758,7 @@ cp_parser_lambda_introducer (cp_parser*
                   capture_id,
                   capture_init_expr,
                   /*by_reference_p=*/capture_kind == BY_REFERENCE,
-                  explicit_init_p);
+                  explicit_init_p, &ids);
 
       /* If there is any qualification still in effect, clear it
         now; we will be starting fresh with the next capture.  */
--- gcc/cp/lambda.c.jj  2019-03-20 17:05:02.179173500 +0100
+++ gcc/cp/lambda.c     2019-03-20 18:01:50.213854086 +0100
@@ -500,11 +500,12 @@ vla_capture_type (tree array_type)
 /* From an ID and INITIALIZER, create a capture (by reference if
    BY_REFERENCE_P is true), add it to the capture-list for LAMBDA,
    and return it.  If ID is `this', BY_REFERENCE_P says whether
-   `*this' is captured by reference.  */
+   `*this' is captured by reference.  IDS is used during introducer
+   parsing to detect duplicate captures if there are many captures.  */
 
 tree
 add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
-            bool explicit_init_p)
+            bool explicit_init_p, hash_set<tree> *ids)
 {
   char *buf;
   tree type, member, name;
@@ -605,13 +606,37 @@ add_capture (tree lambda, tree id, tree
      for duplicates.  */
   if (!LAMBDA_EXPR_CLOSURE (lambda))
     {
-      if (IDENTIFIER_MARKED (name))
+      gcc_assert (ids);
+      bool found;
+      if (ids->size () == 0)
+       {
+         found = false;
+         /* Optimize for the zero or one explicit captures cases
+            and only create the hash_set after adding second capture.  */
+         if (LAMBDA_EXPR_CAPTURE_LIST (lambda))
+           {
+             tree field = TREE_PURPOSE (LAMBDA_EXPR_CAPTURE_LIST (lambda));
+             if (PACK_EXPANSION_P (field))
+               field = PACK_EXPANSION_PATTERN (field);
+             if (name == DECL_NAME (field))
+               found = true;
+             else
+               {
+                 ids->create ();
+                 ids->add (DECL_NAME (field));
+               }
+           }
+       }
+      else
+       found = ids->contains (name);
+      if (found)
        {
          pedwarn (input_location, 0,
                   "already captured %qD in lambda expression", id);
          return NULL_TREE;
        }
-      IDENTIFIER_MARKED (name) = true;
+      if (ids->size ())
+       ids->add (name);
     }
 
   if (variadic)
@@ -674,8 +699,6 @@ register_capture_members (tree captures)
   if (PACK_EXPANSION_P (field))
     field = PACK_EXPANSION_PATTERN (field);
 
-  /* We set this in add_capture to avoid duplicates.  */
-  IDENTIFIER_MARKED (DECL_NAME (field)) = false;
   finish_member_declaration (field);
 }
 
@@ -706,7 +729,7 @@ add_default_capture (tree lambda_stack,
                            (this_capture_p
                             || (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda)
                                 == CPLD_REFERENCE)),
-                           /*explicit_init_p=*/false);
+                           /*explicit_init_p=*/false, NULL);
       initializer = convert_from_reference (var);
 
       /* Warn about deprecated implicit capture of this via [=].  */
--- gcc/cp/cp-tree.h.jj 2019-03-20 17:05:02.133174253 +0100
+++ gcc/cp/cp-tree.h    2019-03-20 17:35:25.113556194 +0100
@@ -7150,7 +7150,8 @@ extern tree lambda_return_type                    (tree);
 extern tree lambda_proxy_type                  (tree);
 extern tree lambda_function                    (tree);
 extern void apply_deduced_return_type           (tree, tree);
-extern tree add_capture                         (tree, tree, tree, bool, bool);
+extern tree add_capture                         (tree, tree, tree, bool, bool,
+                                                hash_set<tree> *);
 extern tree add_default_capture                 (tree, tree, tree);
 extern void insert_capture_proxy               (tree);
 extern void insert_pending_capture_proxies     (void);
--- gcc/testsuite/g++.dg/cpp1y/lambda-init18.C.jj       2019-03-20 
17:35:25.121556064 +0100
+++ gcc/testsuite/g++.dg/cpp1y/lambda-init18.C  2019-03-20 17:35:25.121556064 
+0100
@@ -0,0 +1,12 @@
+// PR c++/89767
+// { dg-do compile { target c++14 } }
+
+void bar (int);
+
+void
+foo ()
+{
+  int x = 0;
+  auto z = [x, y = [x] { bar (x); }] { y (); bar (x); };
+  z ();
+}
--- gcc/testsuite/g++.dg/cpp1y/lambda-init19.C.jj       2019-03-20 
17:35:25.121556064 +0100
+++ gcc/testsuite/g++.dg/cpp1y/lambda-init19.C  2019-03-20 17:35:25.121556064 
+0100
@@ -0,0 +1,15 @@
+// PR c++/89767
+// { dg-do compile { target c++14 } }
+
+void bar (int);
+
+void
+foo ()
+{
+  int x = 0;
+  int a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0;
+  auto z = [x, y = [x] { bar (x); }, x] { y (); bar (x); };    // { dg-error 
"already captured 'x' in lambda expression" }
+  auto w = [x, a, b, c, d, y = [x] { bar (x); }, e, f, g, h, x] { y (); bar (x 
+ a + b + c + d + e + f + g + h); };    // { dg-error "already captured 'x' in 
lambda expression" }
+  z ();
+  w ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr89767.C.jj     2019-03-20 17:35:25.121556064 
+0100
+++ gcc/testsuite/g++.dg/cpp1y/pr89767.C        2019-03-20 17:35:25.121556064 
+0100
@@ -0,0 +1,32 @@
+// PR c++/89767
+// { dg-do compile { target c++14 } }
+// { dg-options "-O2 -Wall" }
+
+template <typename d> struct e { using g = d; };
+template <typename d, template <typename> class> using h = e<d>;
+template <typename d, template <typename> class i>
+using j = typename h<d, i>::g;
+template <typename c> int k(c);
+template <typename...> class au;
+struct l { template <typename c> using m = typename c::f; };
+struct s : l { using af = j<au<int, int> *, m>; };
+template <unsigned long, typename> struct o;
+template <long p, typename c> using q = typename o<p, c>::g;
+template <typename> struct r;
+template <typename c> struct r<c *> { typedef c aj; };
+template <typename ak, typename> struct al { typename r<ak>::aj operator*(); 
void operator++(); };
+template <typename am, typename an, typename ao>
+bool operator!=(al<am, ao>, al<an, ao>);
+template <unsigned long, typename...> struct ap;
+template <unsigned long aq, typename ar, typename... as>
+struct ap<aq, ar, as...> : ap<1, as...> {};
+template <unsigned long aq, typename ar> struct ap<aq, ar> {};
+template <typename... at> class au : public ap<0, at...> {};
+template <unsigned long p, typename ar, typename... as>
+struct o<p, au<ar, as...>> : o<p - 1, au<as...>> {};
+template <typename ar, typename... as> struct o<0, au<ar, as...>> { typedef ar 
g; };
+template <long p, typename ar> constexpr ar av(ap<p, ar> __t) { return ar (); }
+template <int p, typename... at> constexpr q<p, au<at...>> aw(au<at...> __t) { 
av<p>(__t); return q<p, au<at...>> (); }
+struct bg { typedef s::af af; };
+struct F { typedef al<bg::af, int> bk; bk begin(); bk end(); };
+void bo() { int t = 0; F cv; for (auto bp : cv) [t, n = k(aw<1>(bp))] {}; }

Reply via email to