cor3ntin updated this revision to Diff 522180. cor3ntin added a comment. Rebase
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D149276/new/ https://reviews.llvm.org/D149276 Files: clang/docs/ReleaseNotes.rst clang/include/clang/Parse/Parser.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseTentative.cpp clang/lib/Parse/Parser.cpp clang/test/Parser/cxx1z-decomposition.cpp clang/test/Parser/cxx2b-auto-x.cpp
Index: clang/test/Parser/cxx2b-auto-x.cpp =================================================================== --- clang/test/Parser/cxx2b-auto-x.cpp +++ clang/test/Parser/cxx2b-auto-x.cpp @@ -18,7 +18,37 @@ using T = looks_like_declaration *; void f() { T(&a)->n = 1; } -// FIXME: They should be deemed expressions without breaking function pointer -// parameter declarations with trailing return types. -// void g() { auto(&a)->n = 0; } -// void h() { auto{&a}->n = 0; } +void g() { auto(&a)->n = 0; } // cxx23-warning {{before C++23}} \ + // cxx20-error {{declaration of variable 'a' with deduced type 'auto (&)' requires an initializer}} \ + // cxx20-error {{expected ';' at end of declaration}} +void h() { auto{&a}->n = 0; } // cxx23-warning {{before C++23}} \ + // cxx20-error {{expected unqualified-id}} \ + // cxx20-error {{expected expression}} + +void e(auto (*p)(int y) -> decltype(y)) {} + +struct M; +struct S{ + S operator()(); + S* operator->(); + int N; + int M; +} s; // expected-note {{here}} + +void test() { + auto(s)()->N; // cxx23-warning {{expression result unused}} \ + // cxx23-warning {{before C++23}} \ + // cxx20-error {{unknown type name 'N'}} + auto(s)()->M; // expected-error {{redefinition of 's' as different kind of symbol}} +} + +void test_paren() { + int a = (auto(0)); // cxx23-warning {{before C++23}} \ + // cxx20-error {{expected expression}} \ + // cxx20-error {{expected ')'}} \ + // cxx20-note {{to match this '('}} + int b = (auto{0}); // cxx23-warning {{before C++23}} \ + // cxx20-error {{expected expression}} \ + // cxx20-error {{expected ')'}} \ + // cxx20-note {{to match this '('}} +} Index: clang/test/Parser/cxx1z-decomposition.cpp =================================================================== --- clang/test/Parser/cxx1z-decomposition.cpp +++ clang/test/Parser/cxx1z-decomposition.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -std=c++17 %s -verify -fcxx-exceptions +// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx17 -fcxx-exceptions +// RUN: %clang_cc1 -std=c++2b %s -verify=expected,cxx2b -fcxx-exceptions // RUN: not %clang_cc1 -std=c++17 %s -emit-llvm-only -fcxx-exceptions struct S { int a, b, c; }; @@ -30,7 +31,7 @@ namespace OtherDecl { // A parameter-declaration is not a simple-declaration. // This parses as an array declaration. - void f(auto [a, b, c]); // expected-error {{'auto' not allowed in function prototype}} expected-error {{'a'}} + void f(auto [a, b, c]); // cxx17-error {{'auto' not allowed in function prototype}} expected-error {{'a'}} void g() { // A condition is allowed as a Clang extension. @@ -57,7 +58,7 @@ namespace GoodSpecifiers { void f() { int n[1]; - const volatile auto &[a] = n; + const volatile auto &[a] = n; // cxx2b-warning {{volatile qualifier in structured binding declaration is deprecated}} } } @@ -67,8 +68,8 @@ struct S { int n; } s; void f() { // storage-class-specifiers - static auto &[a] = n; // expected-warning {{declared 'static' is a C++20 extension}} - thread_local auto &[b] = n; // expected-warning {{declared 'thread_local' is a C++20 extension}} + static auto &[a] = n; // cxx17-warning {{declared 'static' is a C++20 extension}} + thread_local auto &[b] = n; // cxx17-warning {{declared 'thread_local' is a C++20 extension}} extern auto &[c] = n; // expected-error {{cannot be declared 'extern'}} expected-error {{declaration of block scope identifier with linkage cannot have an initializer}} struct S { mutable auto &[d] = n; // expected-error {{not permitted in this context}} @@ -85,16 +86,19 @@ } static constexpr inline thread_local auto &[j1] = n; // expected-error {{cannot be declared with 'constexpr inline' specifiers}} - static thread_local auto &[j2] = n; // expected-warning {{declared with 'static thread_local' specifiers is a C++20 extension}} + static thread_local auto &[j2] = n; // cxx17-warning {{declared with 'static thread_local' specifiers is a C++20 extension}} inline auto &[k] = n; // expected-error {{cannot be declared 'inline'}} const int K = 5; + auto ([c]) = s; // expected-error {{decomposition declaration cannot be declared with parentheses}} void g() { // defining-type-specifiers other than cv-qualifiers and 'auto' S [a] = s; // expected-error {{cannot be declared with type 'S'}} decltype(auto) [b] = s; // expected-error {{cannot be declared with type 'decltype(auto)'}} - auto ([c]) = s; // expected-error {{cannot be declared with parentheses}} + auto ([c2]) = s; // cxx17-error {{decomposition declaration cannot be declared with parenthese}} \ + // cxx2b-error {{use of undeclared identifier 'c2'}} \ + // cxx2b-error {{expected body of lambda expression}} \ // FIXME: This error is not very good. auto [d]() = s; // expected-error {{expected ';'}} expected-error {{expected expression}} Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -1950,7 +1950,7 @@ assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) || Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) || - Tok.is(tok::kw___super)) && + Tok.is(tok::kw___super) || Tok.is(tok::kw_auto)) && "Cannot be a type or scope token!"); if (Tok.is(tok::kw_typename)) { Index: clang/lib/Parse/ParseTentative.cpp =================================================================== --- clang/lib/Parse/ParseTentative.cpp +++ clang/lib/Parse/ParseTentative.cpp @@ -262,6 +262,7 @@ /// attribute-specifier-seqopt type-specifier-seq declarator /// Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { + bool DeclSpecifierIsAuto = Tok.is(tok::kw_auto); if (TryConsumeDeclarationSpecifier() == TPResult::Error) return TPResult::Error; @@ -277,7 +278,8 @@ assert(TPR == TPResult::False); } - TPResult TPR = TryParseInitDeclaratorList(); + TPResult TPR = TryParseInitDeclaratorList( + /*mayHaveTrailingReturnType=*/DeclSpecifierIsAuto); if (TPR != TPResult::Ambiguous) return TPR; @@ -314,10 +316,15 @@ /// '{' initializer-list ','[opt] '}' /// '{' '}' /// -Parser::TPResult Parser::TryParseInitDeclaratorList() { +Parser::TPResult +Parser::TryParseInitDeclaratorList(bool MayHaveTrailingReturnType) { while (true) { // declarator - TPResult TPR = TryParseDeclarator(false/*mayBeAbstract*/); + TPResult TPR = TryParseDeclarator( + /*mayBeAbstract=*/false, + /*mayHaveIdentifier=*/true, + /*mayHaveDirectInit=*/false, + /*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType); if (TPR != TPResult::Ambiguous) return TPR; @@ -532,13 +539,18 @@ RevertingTentativeParsingAction PA(*this); // FIXME: A tag definition unambiguously tells us this is an init-statement. + bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto); if (State.update(TryConsumeDeclarationSpecifier())) return State.result(); assert(Tok.is(tok::l_paren) && "Expected '('"); while (true) { // Consume a declarator. - if (State.update(TryParseDeclarator(false/*mayBeAbstract*/))) + if (State.update(TryParseDeclarator( + /*mayBeAbstract=*/false, + /*mayHaveIdentifier=*/true, + /*mayHaveDirectInit=*/false, + /*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType))) return State.result(); // Attributes, asm label, or an initializer imply this is not an expression. @@ -623,13 +635,16 @@ // We need tentative parsing... RevertingTentativeParsingAction PA(*this); + bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto); // type-specifier-seq TryConsumeDeclarationSpecifier(); assert(Tok.is(tok::l_paren) && "Expected '('"); // declarator - TPR = TryParseDeclarator(true/*mayBeAbstract*/, false/*mayHaveIdentifier*/); + TPR = TryParseDeclarator(true /*mayBeAbstract*/, false /*mayHaveIdentifier*/, + /*mayHaveDirectInit=*/false, + MayHaveTrailingReturnType); // In case of an error, let the declaration parsing code handle it. if (TPR == TPResult::Error) @@ -658,6 +673,9 @@ TPR = TPResult::True; isAmbiguous = true; + } else if (Context == TypeIdInTrailingReturnType) { + TPR = TPResult::True; + isAmbiguous = true; } else TPR = TPResult::False; } @@ -1042,7 +1060,8 @@ /// Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier, - bool mayHaveDirectInit) { + bool mayHaveDirectInit, + bool mayHaveTrailingReturnType) { // declarator: // direct-declarator // ptr-operator declarator @@ -1084,7 +1103,7 @@ ImplicitTypenameContext::No))) { // 'int(int)' is a function. // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] // exception-specification[opt] - TPResult TPR = TryParseFunctionDeclarator(); + TPResult TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType); if (TPR != TPResult::Ambiguous) return TPR; } else { @@ -1123,7 +1142,7 @@ // direct-declarator '(' parameter-declaration-clause ')' // cv-qualifier-seq[opt] exception-specification[opt] ConsumeParen(); - TPR = TryParseFunctionDeclarator(); + TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType); } else if (Tok.is(tok::l_square)) { // direct-declarator '[' constant-expression[opt] ']' // direct-abstract-declarator[opt] '[' constant-expression[opt] ']' @@ -1390,6 +1409,16 @@ return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes, BracedCastResult, InvalidAsDeclSpec); + case tok::kw_auto: { + if (!getLangOpts().CPlusPlus23) + return TPResult::True; + if (NextToken().is(tok::l_brace)) + return TPResult::False; + if (NextToken().is(tok::l_paren)) + return TPResult::Ambiguous; + return TPResult::True; + } + case tok::coloncolon: { // ::foo::bar const Token &Next = NextToken(); if (Next.isOneOf(tok::kw_new, // ::new @@ -1423,7 +1452,6 @@ case tok::kw_static: case tok::kw_extern: case tok::kw_mutable: - case tok::kw_auto: case tok::kw___thread: case tok::kw_thread_local: case tok::kw__Thread_local: @@ -2023,7 +2051,10 @@ // declarator // abstract-declarator[opt] - TPR = TryParseDeclarator(true/*mayBeAbstract*/); + TPR = TryParseDeclarator(/*mayBeAbstract=*/true, + /*mayHaveIdentifier=*/true, + /*mayHaveDirectInit=*/false, + /*mayHaveTrailingReturnType=*/true); if (TPR != TPResult::Ambiguous) return TPR; @@ -2077,7 +2108,8 @@ /// exception-specification: /// 'throw' '(' type-id-list[opt] ')' /// -Parser::TPResult Parser::TryParseFunctionDeclarator() { +Parser::TPResult +Parser::TryParseFunctionDeclarator(bool MayHaveTrailingReturnType) { // The '(' is already parsed. TPResult TPR = TryParseParameterDeclarationClause(); @@ -2122,6 +2154,19 @@ } } + // attribute-specifier-seq + if (!TrySkipAttributes()) + return TPResult::Ambiguous; + + // trailing-return-type + if (Tok.is(tok::arrow) && MayHaveTrailingReturnType) { + if (TPR == TPResult::True) + return TPR; + ConsumeToken(); + if (isCXXTypeId(TentativeCXXTypeIdContext::TypeIdInTrailingReturnType)) + return TPResult::True; + } + return TPResult::Ambiguous; } Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -6494,8 +6494,9 @@ // that it's an initializer instead. if (D.mayOmitIdentifier() && D.mayBeFollowedByCXXDirectInit()) { RevertingTentativeParsingAction PA(*this); - if (TryParseDeclarator(true, D.mayHaveIdentifier(), true) == - TPResult::False) { + if (TryParseDeclarator(true, D.mayHaveIdentifier(), true, + D.getDeclSpec().getTypeSpecType() == TST_auto) == + TPResult::False) { D.SetIdentifier(nullptr, Tok.getLocation()); goto PastIdentifier; } Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2521,10 +2521,10 @@ enum TentativeCXXTypeIdContext { TypeIdInParens, TypeIdUnambiguous, - TypeIdAsTemplateArgument + TypeIdAsTemplateArgument, + TypeIdInTrailingReturnType, }; - /// isTypeIdInParens - Assumes that a '(' was parsed and now we want to know /// whether the parens contain an expression or a type-id. /// Returns true for a type-id and false for an expression. @@ -2652,14 +2652,15 @@ TPResult TryParseProtocolQualifiers(); TPResult TryParsePtrOperatorSeq(); TPResult TryParseOperatorId(); - TPResult TryParseInitDeclaratorList(); + TPResult TryParseInitDeclaratorList(bool MayHaveTrailingReturnType = false); TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true, - bool mayHaveDirectInit = false); + bool mayHaveDirectInit = false, + bool mayHaveTrailingReturnType = false); TPResult TryParseParameterDeclarationClause( bool *InvalidAsDeclaration = nullptr, bool VersusTemplateArg = false, ImplicitTypenameContext AllowImplicitTypename = ImplicitTypenameContext::No); - TPResult TryParseFunctionDeclarator(); + TPResult TryParseFunctionDeclarator(bool MayHaveTrailingReturnType = false); TPResult TryParseBracketDeclarator(); TPResult TryConsumeDeclarationSpecifier(); Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -445,6 +445,8 @@ - Some predefined expressions are now treated as string literals in MSVC compatibility mode. (`#114 <https://github.com/llvm/llvm-project/issues/114>`_) +- Fix parsing of `auto(x)`, when it is surrounded by parentheses. + (`#62494 <https://github.com/llvm/llvm-project/issues/62494>`_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits