https://gcc.gnu.org/g:e9549b5ee8496af12bac3ebfa3ec0aa8487fb725

commit r16-1991-ge9549b5ee8496af12bac3ebfa3ec0aa8487fb725
Author: Jason Merrill <ja...@redhat.com>
Date:   Thu Jul 3 12:05:12 2025 -0400

    c++: ICE with 'this' in lambda signature [PR120748]
    
    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.

Diff:
---
 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(-)

diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index 2a9061acf550..ae732c3f72a6 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 b1626acb50b7..617b7cd47d8f 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 486e0450f4a3..e70cd1a9082f 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 000000000000..11f23375e589
--- /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

Reply via email to