diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -307,8 +307,8 @@ not a type concept. - Fix crash when a doc comment contains a line splicing. (`#62054 `_) -- Work around with a clang coverage crash which happens when visiting - expressions/statements with invalid source locations in non-assert builds. +- Work around with a clang coverage crash which happens when visiting + expressions/statements with invalid source locations in non-assert builds. Assert builds may still see assertions triggered from this. - Fix a failed assertion due to an invalid source location when trying to form a coverage report for an unresolved constructor expression. @@ -357,6 +357,7 @@ - Fix bug in the computation of the ``__has_unique_object_representations`` builtin for types with unnamed bitfields. (`#61336 `_) +- Fix parsing of `auto(x)`, when it is surrounded by parenthesese. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -496,8 +496,6 @@ "declared type must be 'auto' or reference to 'auto'">; def err_decomp_decl_constraint : Error< "decomposition declaration cannot be declared with constrained 'auto'">; -def err_decomp_decl_parens : Error< - "decomposition declaration cannot be declared with parentheses">; def err_decomp_decl_template : Error< "decomposition declaration template not supported">; def err_decomp_decl_not_alone : Error< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/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(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6491,8 +6491,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; } diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -234,7 +234,7 @@ return TPResult::Error; if (Tok.is(tok::annot_cxxscope)) ConsumeAnnotationToken(); - if (Tok.is(tok::identifier)) + if (Tok.isOneOf(tok::identifier, tok::kw_auto)) ConsumeToken(); else if (Tok.is(tok::annot_template_id)) ConsumeAnnotationToken(); @@ -265,6 +265,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; @@ -280,7 +281,8 @@ assert(TPR == TPResult::False); } - TPResult TPR = TryParseInitDeclaratorList(); + TPResult TPR = TryParseInitDeclaratorList( + /*mayHaveTrailingReturnType=*/DeclSpecifierIsAuto); if (TPR != TPResult::Ambiguous) return TPR; @@ -317,10 +319,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; @@ -535,13 +542,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. @@ -626,13 +638,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) @@ -661,6 +676,9 @@ TPR = TPResult::True; isAmbiguous = true; + } else if (Context == TypeIdInTrailingReturnType) { + TPR = TPResult::True; + isAmbiguous = true; } else TPR = TPResult::False; } @@ -1045,7 +1063,8 @@ /// Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier, - bool mayHaveDirectInit) { + bool mayHaveDirectInit, + bool mayHaveTrailingReturnType) { // declarator: // direct-declarator // ptr-operator declarator @@ -1087,7 +1106,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 { @@ -1126,7 +1145,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] ']' @@ -1393,6 +1412,15 @@ return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes, BracedCastResult, InvalidAsDeclSpec); + case tok::kw_auto: { + 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 @@ -1400,6 +1428,7 @@ return TPResult::False; [[fallthrough]]; } + case tok::kw___super: case tok::kw_decltype: // Annotate typenames and C++ scope specifiers. If we get one, just @@ -1426,7 +1455,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: @@ -2026,7 +2054,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; @@ -2080,7 +2111,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(); @@ -2125,6 +2157,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; } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/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)) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -840,13 +840,7 @@ D.hasGroupingParens() || D.getNumTypeObjects() > 1 || (D.getNumTypeObjects() == 1 && D.getTypeObject(0).Kind != DeclaratorChunk::Reference)) { - Diag(Decomp.getLSquareLoc(), - (D.hasGroupingParens() || - (D.getNumTypeObjects() && - D.getTypeObject(0).Kind == DeclaratorChunk::Paren)) - ? diag::err_decomp_decl_parens - : diag::err_decomp_decl_type) - << R; + Diag(Decomp.getLSquareLoc(), diag::err_decomp_decl_type) << R; // In most cases, there's no actual problem with an explicitly-specified // type, but a function type won't work here, and ActOnVariableDeclarator diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp --- a/clang/test/Parser/cxx1z-decomposition.cpp +++ b/clang/test/Parser/cxx1z-decomposition.cpp @@ -94,7 +94,9 @@ // 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 ([c]) = s; // expected-error {{'auto' not allowed here}} \ + // expected-error {{use of undeclared identifier 'c'}} \ + // expected-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 --- a/clang/test/Parser/cxx2b-auto-x.cpp +++ b/clang/test/Parser/cxx2b-auto-x.cpp @@ -18,7 +18,25 @@ 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; } +void h() { auto{&a}->n = 0; } + +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; // expected-warning {{expression result unused}} + auto(s)()->M; // expected-error {{redefinition of 's' as different kind of symbol}} +} + +void test_paren() { + int a = (auto(0)); + int b = (auto{0}); +}