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