Implement P0018R3, C++17 lambda capture of *this by value as [=,*this]
authorJakub Jelinek <jakub@redhat.com>
Tue, 27 Sep 2016 19:10:38 +0000 (21:10 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 27 Sep 2016 19:10:38 +0000 (21:10 +0200)
Implement P0018R3, C++17 lambda capture of *this by value as [=,*this]
* parser.c (cp_parser_lambda_introducer): Formatting fix.  Pass
true instead of false as by_reference_p to add_capture for 'this'.
Parse '*this' simple-capture.
* lambda.c (build_capture_proxy): Handle '*this' capture by value.
(add_capture): Adjust function comment.  For id == this_identifier,
treat by_reference_p as capturing '*this' by reference, i.e. 'this'
by value, and !by_reference_p as capturing '*this' by value.
(add_default_capture): For implicit 'this' capture, always pass
by_reference_p true rather than false.

* g++.dg/cpp1z/lambda-this1.C: New test.
* g++.dg/cpp1z/lambda-this2.C: New test.

From-SVN: r240556

gcc/cp/ChangeLog
gcc/cp/lambda.c
gcc/cp/parser.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp1z/lambda-this1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/lambda-this2.C [new file with mode: 0644]

index 75c9d36..1a1f186 100644 (file)
@@ -1,5 +1,16 @@
 2016-09-27  Jakub Jelinek  <jakub@redhat.com>
 
+       Implement P0018R3, C++17 lambda capture of *this by value as [=,*this]
+       * parser.c (cp_parser_lambda_introducer): Formatting fix.  Pass
+       true instead of false as by_reference_p to add_capture for 'this'.
+       Parse '*this' simple-capture.
+       * lambda.c (build_capture_proxy): Handle '*this' capture by value.
+       (add_capture): Adjust function comment.  For id == this_identifier,
+       treat by_reference_p as capturing '*this' by reference, i.e. 'this'
+       by value, and !by_reference_p as capturing '*this' by value.
+       (add_default_capture): For implicit 'this' capture, always pass
+       by_reference_p true rather than false.
+
        PR c++/77722
        * cp-gimplify.c (cp_ubsan_maybe_instrument_return): Instrument also
        functions that have just a STATEMENT_LIST instead of BIND_EXPR, or
index d511185..cd32226 100644 (file)
@@ -380,6 +380,13 @@ build_capture_proxy (tree member)
 
   type = lambda_proxy_type (object);
 
+  if (name == this_identifier && !POINTER_TYPE_P (type))
+    {
+      type = build_pointer_type (type);
+      type = cp_build_qualified_type (type, TYPE_QUAL_CONST);
+      object = build_fold_addr_expr_with_type (object, type);
+    }
+
   if (DECL_VLA_CAPTURE_P (member))
     {
       /* Rebuild the VLA type from the pointer and maxindex.  */
@@ -440,7 +447,8 @@ 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.  */
+   and return it.  If ID is `this', BY_REFERENCE_P says whether
+   `*this' is captured by reference.  */
 
 tree
 add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
@@ -499,7 +507,14 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
       type = lambda_capture_field_type (initializer, explicit_init_p);
       if (type == error_mark_node)
        return error_mark_node;
-      if (by_reference_p)
+      if (id == this_identifier && !by_reference_p)
+       {
+         gcc_assert (POINTER_TYPE_P (type));
+         type = TREE_TYPE (type);
+         initializer = cp_build_indirect_ref (initializer, RO_NULL,
+                                              tf_warning_or_error);
+       }
+      if (id != this_identifier && by_reference_p)
        {
          type = build_reference_type (type);
          if (!dependent_type_p (type) && !lvalue_p (initializer))
@@ -628,8 +643,8 @@ add_default_capture (tree lambda_stack, tree id, tree initializer)
                             id,
                             initializer,
                             /*by_reference_p=*/
-                           (!this_capture_p
-                            && (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda)
+                           (this_capture_p
+                            || (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda)
                                 == CPLD_REFERENCE)),
                            /*explicit_init_p=*/false);
       initializer = convert_from_reference (var);
index 5ec8b1b..f672b8d 100644 (file)
@@ -9899,7 +9899,25 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
          cp_lexer_consume_token (parser->lexer);
          add_capture (lambda_expr,
                       /*id=*/this_identifier,
-                      /*initializer=*/finish_this_expr(),
+                      /*initializer=*/finish_this_expr (),
+                      /*by_reference_p=*/true,
+                      explicit_init_p);
+         continue;
+       }
+
+      /* Possibly capture `*this'.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_MULT)
+         && cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_THIS))
+       {
+         location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+         if (cxx_dialect < cxx1z)
+           pedwarn (loc, 0, "%<*this%> capture only available with "
+                            "-std=c++1z or -std=gnu++1z");
+         cp_lexer_consume_token (parser->lexer);
+         cp_lexer_consume_token (parser->lexer);
+         add_capture (lambda_expr,
+                      /*id=*/this_identifier,
+                      /*initializer=*/finish_this_expr (),
                       /*by_reference_p=*/false,
                       explicit_init_p);
          continue;
index 443a917..59e20b7 100644 (file)
@@ -1,5 +1,8 @@
 2016-09-27  Jakub Jelinek  <jakub@redhat.com>
 
+       * g++.dg/cpp1z/lambda-this1.C: New test.
+       * g++.dg/cpp1z/lambda-this2.C: New test.
+
        PR c++/77722
        * g++.dg/ubsan/return-4.C: New test.
        * g++.dg/ubsan/return-5.C: New test.
diff --git a/gcc/testsuite/g++.dg/cpp1z/lambda-this1.C b/gcc/testsuite/g++.dg/cpp1z/lambda-this1.C
new file mode 100644 (file)
index 0000000..5a4c5f9
--- /dev/null
@@ -0,0 +1,68 @@
+// P0018R3 - C++17 lambda capture of *this
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int a;
+  void foo () {
+    int v = 4;
+    auto b = [*this, this] {};         // { dg-error "already captured 'this'" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+    auto c = [this, *this] {};         // { dg-error "already captured 'this'" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+    auto d = [this] { return a; };
+    auto e = [*this] { return a; };    // { dg-error "'*this' capture only available with" "" { target c++14_down } }
+    auto f = [this] { a++; };
+    auto g = [*this] { a++; };         // { dg-error "in read-only object" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+    auto h = [*this] () mutable { a++; };// { dg-error "'*this' capture only available with" "" { target c++14_down } }
+    auto i = [=] { return a; };
+    auto j = [&] { return a; };
+    auto k = [=, this] { return a; };// { dg-error "explicit by-copy capture of 'this' redundant with by-copy capture default" }
+    auto l = [&, this] { return a; };
+    auto m = [=, *this] { return a; };// { dg-error "'*this' capture only available with" "" { target c++14_down } }
+    auto n = [&, *this] { return a; };// { dg-error "'*this' capture only available with" "" { target c++14_down } }
+    auto o = [*this, &v] { return a + v; };// { dg-error "'*this' capture only available with" "" { target c++14_down } }
+    auto p = [*this] { this = 0; };    // { dg-error "lvalue required as left operand of assignment" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+  }
+};
+struct B {
+  double b;
+  B () : b (.007) {}
+  double foo () {
+    return [this]{ return [*this] { return b; }; }()();        // { dg-error "'*this' capture only available with" "" { target c++14_down } }
+  }
+  double bar () {
+    auto c = []{ return [*this] { return b; }; };      // { dg-error "'this' was not captured for this lambda function" }
+  }                                                    // { dg-error "invalid use of non-static data member 'B::b'" "" { target *-*-* } .-1 }
+};                                                     // { dg-error "'*this' capture only available with" "" { target c++14_down } .-2 }
+struct C {
+  int c;
+  C (const C &) = delete;
+  void bar () const;
+  void foo () {
+    auto d = [this] { return c; };
+    auto e = [*this] { return c; };    // { dg-error "use of deleted function" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+    auto f = [=] { return c; };
+    auto g = [&] { return c; };
+    auto h = [this] { bar (); };
+    auto i = [*this] { bar (); };      // { dg-error "use of deleted function" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+  }
+};
+struct D {
+  int d;
+  ~D () = delete;
+  void bar () const;
+  void foo () {
+    auto e = [this] { return d; };
+    auto f = [*this] { return d; };    // { dg-error "use of deleted function" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+    auto g = [=] { return d; };
+    auto h = [&] { return d; };
+    auto i = [this] { bar (); };
+    auto j = [*this] { bar (); };      // { dg-error "use of deleted function" }
+                                       // { dg-error "'*this' capture only available with" "" { target c++14_down } .-1 }
+  }
+};
diff --git a/gcc/testsuite/g++.dg/cpp1z/lambda-this2.C b/gcc/testsuite/g++.dg/cpp1z/lambda-this2.C
new file mode 100644 (file)
index 0000000..5a0066d
--- /dev/null
@@ -0,0 +1,68 @@
+// P0018R3 - C++17 lambda capture of *this
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+extern "C" void abort ();
+
+struct A {
+  int a, z;
+  A () : a (4), z (0) {}
+  A (const A &x) : a (x.a), z (1) {}
+  void foo () {
+    if (z != 0) abort ();
+    auto b = [this] { return &a; };
+    auto c = [*this] { return &a; };   // { dg-warning "'*this' capture only available with" "" { target c++14_down } }
+    auto d = [=] { return &a; };
+    auto e = [&] { return &a; };
+    if (b () != &a) abort ();
+    if (*b () != 4) abort ();
+    auto f = c ();
+    if (c () == &a) abort ();
+    if (c () != f) abort ();
+    if (*c () != 4) abort ();
+    if (d () != &a) abort ();
+    if (e () != &a) abort ();
+    auto g = [this] { return a + z; };
+    auto h = [*this] { return a + z; };        // { dg-warning "'*this' capture only available with" "" { target c++14_down } }
+    auto i = [=] { return a + z; };
+    auto j = [&] { return a + z; };
+    if (g () != 4 || h () != 5 || i () != 4 || j () != 4) abort ();
+  }
+};
+
+template <int N>
+struct B {
+  int a, z;
+  B () : a (N), z (0) {}
+  B (const B &x) : a (x.a), z (1) {}
+  void foo () {
+    if (z != 0) abort ();
+    auto b = [this] { return &a; };
+    auto c = [*this] { return &a; };   // { dg-warning "'*this' capture only available with" "" { target c++14_down } }
+    auto d = [=] { return &a; };
+    auto e = [&] { return &a; };
+    if (b () != &a) abort ();
+    if (*b () != 9) abort ();
+    auto f = c ();
+    if (c () == &a) abort ();
+    if (c () != f) abort ();
+    if (*c () != 9) abort ();
+    if (d () != &a) abort ();
+    if (e () != &a) abort ();
+    auto g = [this] { return a + z; };
+    auto h = [*this] { return a + z; };        // { dg-warning "'*this' capture only available with" "" { target c++14_down } }
+    auto i = [=] { return a + z; };
+    auto j = [&] { return a + z; };
+    if (g () != 9 || h () != 10 || i () != 9 || j () != 9) abort ();
+  }
+};
+
+int
+main ()
+{
+  A a;
+  a.foo ();
+  B<9> b;
+  b.foo ();
+  return 0;
+}