Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

This testcase was crashing from infinite recursion in the diagnostic
machinery, trying to print the lambda signature, which referred to the
__this capture field in the lambda, which wanted to print the lambda again.

But we don't want the signature to refer to the capture field; 'this' in an
unevaluated context refers to the 'this' from the enclosing function, not
the capture.

After fixing that, we still wrongly rejected the B case because
THIS_FORBIDDEN is set in a default (template) argument.  Since we don't
distinguish between THIS_FORBIDDEN being set for a default argument and it
being set for a static member function, let's just ignore it if
cp_unevaluated_operand; we'll give a better diagnostic for the static memfn
case in finish_this_expr.

        PR c++/120748

gcc/cp/ChangeLog:

        * lambda.cc (lambda_expr_this_capture): Don't return a FIELD_DECL.
        * parser.cc (cp_parser_primary_expression): Ignore THIS_FORBIDDEN
        if cp_unevaluated_operand.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/lambda-targ16.C: New test.
        * g++.dg/cpp0x/this1.C: Adjust diagnostics.
---
 gcc/cp/lambda.cc                           | 11 ++++++++
 gcc/cp/parser.cc                           |  5 +++-
 gcc/testsuite/g++.dg/cpp0x/this1.C         | 10 ++++----
 gcc/testsuite/g++.dg/cpp2a/lambda-targ16.C | 29 ++++++++++++++++++++++
 4 files changed, 49 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-targ16.C

diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index 2a9061acf55..ae732c3f72a 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -823,6 +823,14 @@ lambda_expr_this_capture (tree lambda, int add_capture_p)
   if (cp_unevaluated_operand)
     add_capture_p = false;
 
+  /* If we captured 'this' but don't have a capture proxy yet, look up the
+     captured 'this' again.  */
+  if (this_capture && TREE_CODE (this_capture) == FIELD_DECL)
+    {
+      gcc_assert (!add_capture_p);
+      this_capture = NULL_TREE;
+    }
+
   /* Try to default capture 'this' if we can.  */
   if (!this_capture)
     {
@@ -940,6 +948,9 @@ lambda_expr_this_capture (tree lambda, int add_capture_p)
       result = rvalue (result);
     }
 
+  gcc_checking_assert (!result || result == error_mark_node
+                      || TYPE_PTR_P (TREE_TYPE (result)));
+
   return result;
 }
 
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index b1626acb50b..617b7cd47d8 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -6307,7 +6307,10 @@ cp_parser_primary_expression (cp_parser *parser,
          /* Recognize the `this' keyword.  */
        case RID_THIS:
          cp_lexer_consume_token (parser->lexer);
-         if (parser->local_variables_forbidden_p & THIS_FORBIDDEN)
+         if ((parser->local_variables_forbidden_p & THIS_FORBIDDEN)
+             /* It's OK to refer to 'this' in an unevaluated operand in a
+                lambda default argument (lambda-targ16.C).  */
+             && !cp_unevaluated_operand)
            {
              error_at (token->location,
                        "%<this%> may not be used in this context");
diff --git a/gcc/testsuite/g++.dg/cpp0x/this1.C 
b/gcc/testsuite/g++.dg/cpp0x/this1.C
index 486e0450f4a..e70cd1a9082 100644
--- a/gcc/testsuite/g++.dg/cpp0x/this1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/this1.C
@@ -8,10 +8,10 @@ struct S1 {
   void m3 () noexcept(noexcept(this->a)) { }
   void m4 () noexcept(noexcept(this)) { }
 
-  static auto m5 () -> decltype(this->a) { return 0; } // { dg-error ".this. 
may not be used in this context" }
-  static auto m6 () -> decltype(this) { return 0; } // { dg-error ".this. may 
not be used in this context" }
-  static void m7 () noexcept(noexcept(this->a)) { } // { dg-error ".this. may 
not be used in this context" }
-  static void m8 () noexcept(noexcept(this)) { } // { dg-error ".this. may not 
be used in this context" }
+  static auto m5 () -> decltype(this->a) { return 0; } // { dg-error ".this." }
+  static auto m6 () -> decltype(this) { return 0; } // { dg-error ".this." }
+  static void m7 () noexcept(noexcept(this->a)) { } // { dg-error ".this." }
+  static void m8 () noexcept(noexcept(this)) { } // { dg-error ".this." }
 };
 
 template <typename T>
@@ -41,6 +41,6 @@ test ()
 }
 
 struct S5 {
-  friend auto bar() -> decltype(this); // { dg-error ".this. may not be used 
in this context" }
+  friend auto bar() -> decltype(this); // { dg-error ".this." }
   auto bar2() -> decltype(this);
 };
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ16.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ16.C
new file mode 100644
index 00000000000..11f23375e58
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ16.C
@@ -0,0 +1,29 @@
+// PR c++/120748
+// From Clang cxx20-lambda-decltype-this.cpp.
+// { dg-do compile { target c++20 } }
+
+namespace PR45881 {
+struct A {
+    void f();
+};
+int id(A*);
+void A::f() {
+    auto z = [*this](auto z2, decltype(z2(this)) z3){};
+    z(id,3);
+}
+
+struct B {
+    void f();
+};
+void B::f() {
+    auto z = []<typename TT, typename TTT=decltype(TT()(this))>(){return 0;};
+    z.template operator()<int(*)(B*)>();
+}
+struct C {
+    void f();
+};
+void C::f() {
+    auto z = []<typename TT, decltype(TT()(this)) n>(){return 0;};
+    z.template operator()<int(*)(C*), 8>();
+}
+} // namespace PR45881

base-commit: dc90649466a54ab61926d88500a05f59a55cb055
-- 
2.49.0

Reply via email to