diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9601849bd67d3..c62823ddec980 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,8 @@ Bug Fixes to C++ Support - Some predefined expressions are now treated as string literals in MSVC compatibility mode. (`#114 `_) +- Fix parsing of `auto(x)`, when it is surrounded by parentheses. + (`#62494 `_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index fc892d71b51bf..8927af55e3712 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2521,10 +2521,10 @@ class Parser : public CodeCompletionHandler { 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 @@ class Parser : public CodeCompletionHandler { 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(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 92fa7d8a8a759..15a38e647b2de 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6494,8 +6494,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // 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; } diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 02aa59ec6fa1f..bdb462d086f2c 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -262,6 +262,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { /// 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 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { assert(TPR == TPResult::False); } - TPResult TPR = TryParseInitDeclaratorList(); + TPResult TPR = TryParseInitDeclaratorList( + /*mayHaveTrailingReturnType=*/DeclSpecifierIsAuto); if (TPR != TPResult::Ambiguous) return TPR; @@ -314,10 +316,15 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { /// '{' 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 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement, 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 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { // 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 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { 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::TryParseOperatorId() { /// Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier, - bool mayHaveDirectInit) { + bool mayHaveDirectInit, + bool mayHaveTrailingReturnType) { // declarator: // direct-declarator // ptr-operator declarator @@ -1084,7 +1103,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, 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 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, // 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 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, 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 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, 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 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause( // 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 @@ Parser::TPResult Parser::TryParseParameterDeclarationClause( /// 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 @@ Parser::TPResult Parser::TryParseFunctionDeclarator() { } } + // 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; } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index c0f4556d743b2..f0fa075a91125 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1950,7 +1950,7 @@ bool Parser::TryAnnotateTypeOrScopeToken( 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)) { diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp index 10ef464bda50c..90d60df2e47fe 100644 --- a/clang/test/Parser/cxx1z-decomposition.cpp +++ b/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 ForRangeDecl { 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 OtherDecl { 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 @@ namespace BadSpecifiers { 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 @@ namespace BadSpecifiers { } 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}} diff --git a/clang/test/Parser/cxx2b-auto-x.cpp b/clang/test/Parser/cxx2b-auto-x.cpp index 5ca11d5f39950..9e0277eee76a9 100644 --- a/clang/test/Parser/cxx2b-auto-x.cpp +++ b/clang/test/Parser/cxx2b-auto-x.cpp @@ -18,7 +18,37 @@ struct looks_like_declaration { 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 '('}} +}