https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/140053
>From 7b4f82cef62e3b793673e4edfed55c7252be9bcf Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Thu, 15 May 2025 14:59:03 +0200 Subject: [PATCH 1/3] [Clang] Fix parsing of expressions of the form (T())[/*...*/] when checking cast expression, we were assuming `[` was the start of a lambda expression. However, the expression might instead by a subscripted parenthesized postfix expression. Therefore we need to check if the expression is in fact a lambda. We do that by looking an open brace, bailing early when possible. Fixes #20723 --- clang/docs/ReleaseNotes.rst | 1 + clang/include/clang/Parse/Parser.h | 4 + clang/lib/Parse/ParseExpr.cpp | 6 +- clang/lib/Parse/ParseExprCXX.cpp | 64 +++++++++ .../test/Parser/cxx0x-lambda-expressions.cpp | 82 +++++++++++ clang/test/Parser/cxx1z-constexpr-lambdas.cpp | 38 +++-- clang/test/Parser/cxx2a-template-lambdas.cpp | 44 +++--- clang/test/Parser/cxx2b-lambdas.cpp | 131 ++++++++++-------- 8 files changed, 280 insertions(+), 90 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 31c517338c21f..a7c8b2514b226 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -710,6 +710,7 @@ Bug Fixes to C++ Support - Clang now correctly parses arbitrary order of ``[[]]``, ``__attribute__`` and ``alignas`` attributes for declarations (#GH133107) - Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852) - Clang no longer segfaults when there is a configuration mismatch between modules and their users (http://crbug.com/400353616). +- Fix parsing of expressions of the form ``((T))[expr]``. (#GH20723) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index e6492b81dfff8..016708de2bf4c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -4639,6 +4639,10 @@ class Parser : public CodeCompletionHandler { ParseLambdaIntroducer(LambdaIntroducer &Intro, LambdaIntroducerTentativeParse *Tentative = nullptr); + /// Tries to determine if an expression of the form (S())[...]... + /// is a type-cast followed by a lambda, or a subscript expression + bool IsLambdaAfterTypeCast(); + /// ParseLambdaExpressionAfterIntroducer - Parse the rest of a lambda /// expression. ExprResult ParseLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 11cfbbe790418..09dabaad4e231 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1585,8 +1585,10 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, } break; } - Res = ParseLambdaExpression(); - break; + if (isTypeCast != TypeCastState::IsTypeCast || IsLambdaAfterTypeCast()) { + Res = ParseLambdaExpression(); + break; + } } if (getLangOpts().ObjC) { Res = ParseObjCMessageExpression(); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index d95260829e4a0..77544432bbee8 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -692,6 +692,70 @@ ExprResult Parser::ParseLambdaExpression() { return ParseLambdaExpressionAfterIntroducer(Intro); } +bool Parser::IsLambdaAfterTypeCast() { + assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) && + "Not at the start of a possible lambda expression."); + RevertingTentativeParsingAction TPA(*this); + ConsumeBracket(); + // skip the introducer + if (Tok.is(tok::equal) || + (Tok.is(tok::amp) && NextToken().isOneOf(tok::comma, tok::r_square))) + return true; + + SkipUntil(tok::r_square); + + auto IsLambdaKWOrAttribute = [&]() { + // These keyworks that either can appear somewhere in a lambda declarator, + // or cannot appear in a cast expression and we recover in favor of lambdas + if (Tok.isOneOf(tok::kw___declspec, tok::kw___noinline__, tok::kw_noexcept, + tok::kw_throw, tok::kw_mutable, tok::kw___attribute, + tok::kw_constexpr, tok::kw_consteval, tok::kw_static, + tok::kw_inline, tok::kw_extern, tok::kw___private, + tok::kw___global, tok::kw___local, tok::kw___constant, + tok::kw___generic, tok::kw_groupshared, tok::kw_requires, + tok::kw_noexcept)) + return true; + return Tok.isRegularKeywordAttribute() || + isCXX11AttributeSpecifier() != + CXX11AttributeKind::NotAttributeSpecifier; + }; + + if (Tok.is(tok::l_brace) || IsLambdaKWOrAttribute()) + return true; + + // This is a generic lambda, + if (Tok.is(tok::less)) { + ConsumeToken(); + // Common cases. We consider <> as an invalid lambda. + if (Tok.isOneOf(tok::greater, tok::kw_typename, tok::kw_auto, + tok::kw_template)) + return true; + if (isStartOfTemplateTypeParameter() != TPResult::False) + return true; + return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes) != + TPResult::False; + } + // skip the parameter list + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + SkipUntil(tok::r_paren); + } + + if (IsLambdaKWOrAttribute()) + return true; + + if (Tok.is(tok::arrow)) { + ConsumeToken(); + // These cases are always id-expressions + if (Tok.isOneOf(tok::kw_template, tok::kw_operator, tok::tilde)) + return false; + if (!Tok.is(tok::identifier)) + return true; + return isCXXTypeId(TentativeCXXTypeIdContext::InTrailingReturnType); + } + return Tok.is(tok::l_brace); +} + ExprResult Parser::TryParseLambdaExpression() { assert(getLangOpts().CPlusPlus && Tok.is(tok::l_square) && "Not at the start of a possible lambda expression."); diff --git a/clang/test/Parser/cxx0x-lambda-expressions.cpp b/clang/test/Parser/cxx0x-lambda-expressions.cpp index a786a964163e4..d5466d44d8fff 100644 --- a/clang/test/Parser/cxx0x-lambda-expressions.cpp +++ b/clang/test/Parser/cxx0x-lambda-expressions.cpp @@ -40,6 +40,33 @@ class C { return 1; } + int type_cast() { + int foo, bar; + + (void)[]; // expected-error {{expected expression}} + (void)[+] {}; // expected-error {{expected variable name or 'this' in lambda capture list}} + (void)[foo+] {}; // expected-error {{expected ',' or ']' in lambda capture list}} + (void)[foo,&this] {}; // expected-error {{'this' cannot be captured by reference}} + (void)[&this] {}; // expected-error {{'this' cannot be captured by reference}} + (void)[&,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}} + (void)[=,] {}; // expected-error {{expected variable name or 'this' in lambda capture list}} + (void)[] {}; + (void)[=] (int i) {}; + (void)[&] (int) mutable -> void {}; + (void)[foo,bar] () { return 3; }; + (void)[=,&foo] () {}; + (void)[&,foo] () {}; + (void)[this] () {}; + (void)[] () -> class C { return C(); }; + (void)[] () -> enum E { return e; }; + + (void)[] -> int { return 0; }; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}} + (void)[] mutable -> int { return 0; }; // cxx23ext-warning {{is a C++23 extension}} + + (void)[](int) -> {}; // PR13652 expected-error {{expected a type}} + return 1; + } + void designator_or_lambda() { typedef int T; const int b = 0; @@ -125,10 +152,46 @@ class C { [][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}} } + void attributes_type_cast() { + (void)[] __attribute__((noreturn)){}; // cxx23ext-warning {{lambda without a parameter clause is a C++23 extension}} + + (void)[]() [[]] + mutable {}; // expected-error {{expected body of lambda expression}} + + (void)[]() [[]] {}; + (void)[]() [[]] -> void {}; + (void)[]() mutable [[]] -> void {}; +#if __cplusplus >= 201103L + (void)[]() mutable noexcept [[]] -> void {}; +#endif + + // Testing GNU-style attributes on lambdas -- the attribute is specified + // before the mutable specifier instead of after (unlike C++11). + (void)[]() __attribute__((noreturn)) mutable { while(1); }; + (void)[]() mutable + __attribute__((noreturn)) { while(1); }; // expected-error {{expected body of lambda expression}} + + // Testing support for P2173 on adding attributes to the declaration + // rather than the type. + (void)[][[]](){}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}} + + (void)[]<typename>[[]](){}; // cxx20ext-warning {{explicit template parameter list for lambdas is a C++20 extension}} + // cxx23ext-warning@-1 {{an attribute specifier sequence in this position is a C++23 extension}} + + (void)[][[]]{}; // cxx23ext-warning {{an attribute specifier sequence in this position is a C++23 extension}} + } + void missing_parens() { [] mutable {}; // cxx23ext-warning {{is a C++23 extension}} #if __cplusplus >= 201103L [] noexcept {}; // cxx23ext-warning {{is a C++23 extension}} +#endif + } + + void missing_parens_type_cast() { + (void)[] mutable {}; // cxx23ext-warning {{is a C++23 extension}} +#if __cplusplus >= 201103L + (void)[] noexcept {}; // cxx23ext-warning {{is a C++23 extension}} #endif } }; @@ -150,6 +213,25 @@ struct S { }; } +#if __cplusplus >= 201103L +namespace GH20723 { +struct S { + S operator[](int); + S operator()(); + S operator<(int); + S* operator->(); + long a; +}; +int n; +void f() { + static_assert(__is_same_as(decltype((S())[n]()), S), ""); + static_assert(__is_same_as(decltype((S())[n] < 0), S), ""); + static_assert(__is_same_as(decltype((S())[n]->a), long), ""); +} +} +#endif + + struct S { template <typename T> void m (T x =[0); // expected-error{{expected variable name or 'this' in lambda capture list}} diff --git a/clang/test/Parser/cxx1z-constexpr-lambdas.cpp b/clang/test/Parser/cxx1z-constexpr-lambdas.cpp index 87584ee5ca91b..bba63b4925298 100644 --- a/clang/test/Parser/cxx1z-constexpr-lambdas.cpp +++ b/clang/test/Parser/cxx1z-constexpr-lambdas.cpp @@ -1,26 +1,34 @@ -// RUN: %clang_cc1 -std=c++23 %s -verify -// RUN: %clang_cc1 -std=c++20 %s -verify -// RUN: %clang_cc1 -std=c++17 %s -verify -// RUN: %clang_cc1 -std=c++14 %s -verify -// RUN: %clang_cc1 -std=c++11 %s -verify +// RUN: %clang_cc1 -std=c++23 %s -verify -Wno-unused "-DTYPE_CAST=" +// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-unused "-DTYPE_CAST=" +// RUN: %clang_cc1 -std=c++17 %s -verify -Wno-unused "-DTYPE_CAST=" +// RUN: %clang_cc1 -std=c++14 %s -verify -Wno-unused "-DTYPE_CAST=" +// RUN: %clang_cc1 -std=c++11 %s -verify -Wno-unused "-DTYPE_CAST=" -auto XL0 = [] constexpr { return true; }; +// RUN: %clang_cc1 -std=c++23 %s -verify "-DTYPE_CAST=(void)" +// RUN: %clang_cc1 -std=c++20 %s -verify "-DTYPE_CAST=(void)" +// RUN: %clang_cc1 -std=c++17 %s -verify "-DTYPE_CAST=(void)" +// RUN: %clang_cc1 -std=c++14 %s -verify "-DTYPE_CAST=(void)" +// RUN: %clang_cc1 -std=c++11 %s -verify "-DTYPE_CAST=(void)" + +void test() { + +TYPE_CAST [] constexpr { return true; }; #if __cplusplus <= 201402L // expected-warning@-2 {{is a C++17 extension}} #endif #if __cplusplus <= 202002L // expected-warning@-5 {{lambda without a parameter clause is a C++23 extension}} #endif -auto XL1 = []() mutable // +TYPE_CAST []() mutable // mutable // expected-error{{cannot appear multiple times}} mutable {}; // expected-error{{cannot appear multiple times}} #if __cplusplus > 201402L -auto XL2 = [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}} -auto L = []() mutable constexpr { }; -auto L2 = []() constexpr { }; -auto L4 = []() constexpr mutable { }; -auto XL16 = [] () constexpr +TYPE_CAST [] () constexpr mutable constexpr { }; //expected-error{{cannot appear multiple times}} +TYPE_CAST []() mutable constexpr { }; +TYPE_CAST []() constexpr { }; +TYPE_CAST []() constexpr mutable { }; +TYPE_CAST [] () constexpr mutable constexpr //expected-error{{cannot appear multiple times}} mutable //expected-error{{cannot appear multiple times}} @@ -31,8 +39,10 @@ auto XL16 = [] () constexpr #else auto L = []() mutable constexpr {return 0; }; //expected-warning{{is a C++17 extension}} -auto L2 = []() constexpr { return 0;};//expected-warning{{is a C++17 extension}} -auto L4 = []() constexpr mutable { return 0; }; //expected-warning{{is a C++17 extension}} +TYPE_CAST []() constexpr { return 0;};//expected-warning{{is a C++17 extension}} +TYPE_CAST []() constexpr mutable { return 0; }; //expected-warning{{is a C++17 extension}} #endif +} + diff --git a/clang/test/Parser/cxx2a-template-lambdas.cpp b/clang/test/Parser/cxx2a-template-lambdas.cpp index 98c74a247b535..bb43101045e9e 100644 --- a/clang/test/Parser/cxx2a-template-lambdas.cpp +++ b/clang/test/Parser/cxx2a-template-lambdas.cpp @@ -1,38 +1,44 @@ -// RUN: %clang_cc1 -std=c++23 %s -verify -// RUN: %clang_cc1 -std=c++20 %s -verify +// RUN: %clang_cc1 -std=c++23 %s -verify -Wno-unused "-DTYPE_CAST=" +// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-unused "-DTYPE_CAST=" +// RUN: %clang_cc1 -std=c++23 %s -verify "-DTYPE_CAST=(void)" +// RUN: %clang_cc1 -std=c++20 %s -verify "-DTYPE_CAST=(void)" -auto L0 = []<> { }; //expected-error {{cannot be empty}} +void test() { -auto L1 = []<typename T1, typename T2> { }; -auto L2 = []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { }; -auto L3 = []<typename T>(auto arg) { T t; }; -auto L4 = []<int I>() { }; +TYPE_CAST []<> { }; //expected-error {{cannot be empty}} + +TYPE_CAST []<typename T1, typename T2> { }; +TYPE_CAST []<typename T1, typename T2>(T1 arg1, T2 arg2) -> T1 { }; +TYPE_CAST []<typename T>(auto arg) { T t; }; +TYPE_CAST []<int I>() { }; // http://llvm.org/PR49736 -auto L5 = []<auto>(){}; -auto L6 = []<auto>{}; -auto L7 = []<auto>() noexcept {}; -auto L8 = []<auto> noexcept {}; +TYPE_CAST []<auto>(){}; +TYPE_CAST []<auto>{}; +TYPE_CAST []<auto>() noexcept {}; +TYPE_CAST []<auto> noexcept {}; #if __cplusplus <= 202002L // expected-warning@-2 {{lambda without a parameter clause is a C++23 extension}} #endif -auto L9 = []<auto> requires true {}; -auto L10 = []<auto> requires true(){}; -auto L11 = []<auto> requires true() noexcept {}; -auto L12 = []<auto> requires true noexcept {}; +TYPE_CAST []<auto> requires true {}; +TYPE_CAST []<auto> requires true(){}; +TYPE_CAST []<auto> requires true() noexcept {}; +TYPE_CAST []<auto> requires true noexcept {}; #if __cplusplus <= 202002L // expected-warning@-2 {{is a C++23 extension}} #endif -auto L13 = []<auto>() noexcept requires true {}; -auto L14 = []<auto> requires true() noexcept requires true {}; +TYPE_CAST []<auto>() noexcept requires true {}; +TYPE_CAST []<auto> requires true() noexcept requires true {}; -auto XL0 = []<auto> noexcept requires true {}; // expected-error {{expected body of lambda expression}} -auto XL1 = []<auto> requires true noexcept requires true {}; // expected-error {{expected body}} +TYPE_CAST []<auto> noexcept requires true {}; // expected-error {{expected body of lambda expression}} +TYPE_CAST []<auto> requires true noexcept requires true {}; // expected-error {{expected body}} #if __cplusplus <= 202002L // expected-warning@-3 {{is a C++23 extension}} // expected-warning@-3 {{is a C++23 extension}} #endif +} + namespace GH64962 { void f() { [] <typename T>(T i) -> int[] // expected-error {{function cannot return array type 'int[]'}} diff --git a/clang/test/Parser/cxx2b-lambdas.cpp b/clang/test/Parser/cxx2b-lambdas.cpp index 758ec9a42f56d..a769fbda36276 100644 --- a/clang/test/Parser/cxx2b-lambdas.cpp +++ b/clang/test/Parser/cxx2b-lambdas.cpp @@ -1,88 +1,109 @@ -// RUN: %clang_cc1 -std=c++03 %s -verify -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -Wno-c++11-extensions -// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11 -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -// RUN: %clang_cc1 -std=c++14 %s -verify -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -// RUN: %clang_cc1 -std=c++17 %s -verify -Wno-c++23-extensions -Wno-c++20-extensions -// RUN: %clang_cc1 -std=c++20 %s -verify -Wno-c++23-extensions -// RUN: %clang_cc1 -std=c++23 %s -verify - -auto LL0 = [] {}; -auto LL1 = []() {}; -auto LL2 = []() mutable {}; +// RUN: %clang_cc1 -std=c++03 %s "-DTYPE_CAST=" -verify -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -Wno-c++11-extensions +// RUN: %clang_cc1 -std=c++11 %s "-DTYPE_CAST=" -verify=expected,cxx11 -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions +// RUN: %clang_cc1 -std=c++14 %s "-DTYPE_CAST=" -verify -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions +// RUN: %clang_cc1 -std=c++17 %s "-DTYPE_CAST=" -verify -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions +// RUN: %clang_cc1 -std=c++20 %s "-DTYPE_CAST=" -verify -Wno-unused -Wno-c++23-extensions +// RUN: %clang_cc1 -std=c++23 %s "-DTYPE_CAST=" -verify -Wno-unused + +// RUN: %clang_cc1 -std=c++03 %s "-DTYPE_CAST=(void)" -verify -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions -Wno-c++11-extensions +// RUN: %clang_cc1 -std=c++11 %s "-DTYPE_CAST=(void)" -verify=expected,cxx11 -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c++14-extensions +// RUN: %clang_cc1 -std=c++14 %s "-DTYPE_CAST=(void)" -verify -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions -Wno-c++17-extensions +// RUN: %clang_cc1 -std=c++17 %s "-DTYPE_CAST=(void)" -verify -Wno-unused -Wno-c++23-extensions -Wno-c++20-extensions +// RUN: %clang_cc1 -std=c++20 %s "-DTYPE_CAST=(void)" -verify -Wno-unused -Wno-c++23-extensions +// RUN: %clang_cc1 -std=c++23 %s "-DTYPE_CAST=(void)" -verify -Wno-unused + +void test() { + +TYPE_CAST [] {}; +TYPE_CAST []() {}; +TYPE_CAST []() mutable {}; #if __cplusplus >= 201103L -auto LL3 = []() constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST []() constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} #endif #if __cplusplus >= 201103L -auto L0 = [] constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST [] constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} #endif -auto L1 = [] mutable {}; +TYPE_CAST [] mutable {}; #if __cplusplus >= 201103L -auto L2 = [] noexcept {}; -auto L3 = [] constexpr mutable {}; // cxx11-error {{return type 'void' is not a literal type}} -auto L4 = [] mutable constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} -auto L5 = [] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST [] noexcept {}; +TYPE_CAST [] constexpr mutable {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST [] mutable constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST [] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}} #endif -auto L6 = [s = 1] mutable {}; +TYPE_CAST [s = 1] mutable {}; #if __cplusplus >= 201103L -auto L7 = [s = 1] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST [s = 1] constexpr mutable noexcept {}; // cxx11-error {{return type 'void' is not a literal type}} #endif -auto L8 = [] -> bool { return true; }; -auto L9 = []<typename T> { return true; }; +TYPE_CAST [] -> bool { return true; }; +TYPE_CAST []<typename T> { return true; }; #if __cplusplus >= 201103L -auto L10 = []<typename T> noexcept { return true; }; +TYPE_CAST []<typename T> noexcept { return true; }; #endif -auto L11 = []<typename T> -> bool { return true; }; +TYPE_CAST []<typename T> -> bool { return true; }; #if __cplusplus >= 202002L -auto L12 = [] consteval {}; -auto L13 = []() requires true {}; // expected-error{{non-templated function cannot have a requires clause}} -auto L14 = []<auto> requires true() requires true {}; -auto L15 = []<auto> requires true noexcept {}; +TYPE_CAST [] consteval {}; +TYPE_CAST []() requires true {}; // expected-error{{non-templated function cannot have a requires clause}} +TYPE_CAST []<auto> requires true() requires true {}; +TYPE_CAST []<auto> requires true noexcept {}; #endif -auto L16 = [] [[maybe_unused]]{}; +TYPE_CAST [] [[maybe_unused]]{}; #if __cplusplus >= 201103L -auto XL0 = [] mutable constexpr mutable {}; // expected-error{{cannot appear multiple times}} cxx11-error {{return type 'void' is not a literal type}} -auto XL1 = [] constexpr mutable constexpr {}; // expected-error{{cannot appear multiple times}} cxx11-error {{return type 'void' is not a literal type}} -auto XL2 = []) constexpr mutable constexpr {}; // expected-error{{expected body of lambda expression}} -auto XL3 = []( constexpr mutable constexpr {}; // expected-error{{invalid storage class specifier}} \ - // expected-error{{function parameter cannot be constexpr}} \ - // expected-error{{a type specifier is required}} \ - // expected-error{{expected ')'}} \ - // expected-note{{to match this '('}} \ - // expected-error{{expected body}} \ - // expected-warning{{duplicate 'constexpr'}} +TYPE_CAST [] mutable constexpr mutable {}; // expected-error{{cannot appear multiple times}} cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST [] constexpr mutable constexpr {}; // expected-error{{cannot appear multiple times}} cxx11-error {{return type 'void' is not a literal type}} + +[]) constexpr mutable constexpr {}; // expected-error{{expected body of lambda expression}} +[]( constexpr mutable constexpr {}; // expected-error{{invalid storage class specifier}} \ + // expected-error{{function parameter cannot be constexpr}} \ + // expected-error{{a type specifier is required}} \ + // expected-error{{expected ')'}} \ + // expected-note{{to match this '('}} \ + // expected-error{{expected body}} \ + // expected-warning{{duplicate 'constexpr'}} + #endif // http://llvm.org/PR49736 -auto XL4 = [] requires true {}; // expected-error{{expected body}} + +#if __cplusplus >= 202002L +[] requires true {}; // expected-error{{expected body}} +(void)[] requires true {}; // expected-error{{expected body}} +#else +[] requires true {}; // expected-error{{expected body}} +(void)[] requires true {}; // expected-error{{expected expression}} +#endif + #if __cplusplus >= 201703L -auto XL5 = []<auto> requires true requires true {}; // expected-error{{expected body}} -auto XL6 = []<auto> requires true noexcept requires true {}; // expected-error{{expected body}} +TYPE_CAST []<auto> requires true requires true {}; // expected-error{{expected body}} +TYPE_CAST []<auto> requires true noexcept requires true {}; // expected-error{{expected body}} #endif -auto XL7 = []() static static {}; // expected-error {{cannot appear multiple times}} -auto XL8 = []() static mutable {}; // expected-error {{cannot be both mutable and static}} +TYPE_CAST []() static static {}; // expected-error {{cannot appear multiple times}} +TYPE_CAST []() static mutable {}; // expected-error {{cannot be both mutable and static}} #if __cplusplus >= 202002L -auto XL9 = []() static consteval {}; +TYPE_CAST []() static consteval {}; #endif #if __cplusplus >= 201103L -auto XL10 = []() static constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} +TYPE_CAST []() static constexpr {}; // cxx11-error {{return type 'void' is not a literal type}} #endif -auto XL11 = [] static {}; -auto XL12 = []() static {}; -auto XL13 = []() static extern {}; // expected-error {{expected body of lambda expression}} -auto XL14 = []() extern {}; // expected-error {{expected body of lambda expression}} +TYPE_CAST [] static {}; +TYPE_CAST []() static {}; +TYPE_CAST []() static extern {}; // expected-error {{expected body of lambda expression}} +TYPE_CAST []() extern {}; // expected-error {{expected body of lambda expression}} + +} void static_captures() { int x; - auto SC1 = [&]() static {}; // expected-error {{a static lambda cannot have any captures}} - auto SC4 = [x]() static {}; // expected-error {{a static lambda cannot have any captures}} - auto SC2 = [&x]() static {}; // expected-error {{a static lambda cannot have any captures}} - auto SC3 = [y=x]() static {}; // expected-error {{a static lambda cannot have any captures}} - auto SC5 = [&y = x]() static {}; // expected-error {{a static lambda cannot have any captures}} - auto SC6 = [=]() static {}; // expected-error {{a static lambda cannot have any captures}} + TYPE_CAST [&]() static {}; // expected-error {{a static lambda cannot have any captures}} + TYPE_CAST [x]() static {}; // expected-error {{a static lambda cannot have any captures}} + TYPE_CAST [&x]() static {}; // expected-error {{a static lambda cannot have any captures}} + TYPE_CAST [y=x]() static {}; // expected-error {{a static lambda cannot have any captures}} + TYPE_CAST [&y = x]() static {}; // expected-error {{a static lambda cannot have any captures}} + TYPE_CAST [=]() static {}; // expected-error {{a static lambda cannot have any captures}} struct X { int z; void f() { >From d9c6d2180e220667ba1bb5ae942a3a04a0b86ed4 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Thu, 15 May 2025 15:27:31 +0200 Subject: [PATCH 2/3] fix typo --- clang/lib/Parse/ParseExprCXX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 77544432bbee8..a40e2ad0cadb5 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -705,8 +705,8 @@ bool Parser::IsLambdaAfterTypeCast() { SkipUntil(tok::r_square); auto IsLambdaKWOrAttribute = [&]() { - // These keyworks that either can appear somewhere in a lambda declarator, - // or cannot appear in a cast expression and we recover in favor of lambdas + // These are keyworks that can appear somewhere in a lambda declarator, + // or cannot appear in a cast-expression and we recover in favor of lambdas if (Tok.isOneOf(tok::kw___declspec, tok::kw___noinline__, tok::kw_noexcept, tok::kw_throw, tok::kw_mutable, tok::kw___attribute, tok::kw_constexpr, tok::kw_consteval, tok::kw_static, >From 2be96b5b81b0f9007cc3bb9db5da085e914c072a Mon Sep 17 00:00:00 2001 From: Corentin Jabot <corentinja...@gmail.com> Date: Thu, 15 May 2025 17:54:21 +0200 Subject: [PATCH 3/3] Improve trailing return tyoe handling --- clang/lib/Parse/ParseExprCXX.cpp | 18 +++++++++++++++++- clang/test/Parser/cxx0x-lambda-expressions.cpp | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index a40e2ad0cadb5..e60cae9510979 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -751,7 +751,23 @@ bool Parser::IsLambdaAfterTypeCast() { return false; if (!Tok.is(tok::identifier)) return true; - return isCXXTypeId(TentativeCXXTypeIdContext::InTrailingReturnType); + if(NextToken().is(tok::l_brace)) + return true; + + // Use a nested unannotated so that we can revert + // annotations when we fail to find a brace. + TentativeParsingAction TPA(*this, /*Unannotated=*/true); + if(TryAnnotateTypeOrScopeToken() + || !Tok.isSimpleTypeSpecifier(getLangOpts())) { + TPA.Revert(); + return false; + } + ConsumeAnyToken(); + if(Tok.is(tok::l_brace)) { + TPA.Commit(); + return true; + } + TPA.Revert(); } return Tok.is(tok::l_brace); } diff --git a/clang/test/Parser/cxx0x-lambda-expressions.cpp b/clang/test/Parser/cxx0x-lambda-expressions.cpp index d5466d44d8fff..f6d9f7effbfe8 100644 --- a/clang/test/Parser/cxx0x-lambda-expressions.cpp +++ b/clang/test/Parser/cxx0x-lambda-expressions.cpp @@ -213,8 +213,8 @@ struct S { }; } -#if __cplusplus >= 201103L namespace GH20723 { +#if __cplusplus >= 201103L struct S { S operator[](int); S operator()(); @@ -228,9 +228,25 @@ void f() { static_assert(__is_same_as(decltype((S())[n] < 0), S), ""); static_assert(__is_same_as(decltype((S())[n]->a), long), ""); } +#endif +#if __cplusplus >= 202302 +struct S2 { + S2 operator[](); + S2* operator->(); + template <typename U> + constexpr static int trailing = 0; +}; + +template <typename T> +struct trailing{}; + +void f2() { + static_assert(__is_same_as(decltype((S2())[]->trailing<int>), const int)); + (void)[]->trailing<int>{return {};}(); } #endif +} struct S { template <typename T> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits