diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp | 354 |
1 files changed, 278 insertions, 76 deletions
diff --git a/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp b/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp index 3bf2bc455bfe..5bfabf55f50c 100644 --- a/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp +++ b/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp @@ -46,7 +46,10 @@ using namespace clang; /// 'using' 'namespace' '::'[opt] nested-name-specifier[opt] /// namespace-name ';' /// -bool Parser::isCXXDeclarationStatement() { +bool Parser::isCXXDeclarationStatement( + bool DisambiguatingWithExpression /*=false*/) { + assert(getLangOpts().CPlusPlus && "Must be called for C++ only."); + switch (Tok.getKind()) { // asm-definition case tok::kw_asm: @@ -59,6 +62,51 @@ bool Parser::isCXXDeclarationStatement() { case tok::kw_static_assert: case tok::kw__Static_assert: return true; + case tok::coloncolon: + case tok::identifier: { + if (DisambiguatingWithExpression) { + RevertingTentativeParsingAction TPA(*this); + // Parse the C++ scope specifier. + CXXScopeSpec SS; + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHasErrors=*/false, + /*EnteringContext=*/true); + + switch (Tok.getKind()) { + case tok::identifier: { + IdentifierInfo *II = Tok.getIdentifierInfo(); + bool isDeductionGuide = Actions.isDeductionGuideName( + getCurScope(), *II, Tok.getLocation(), SS, /*Template=*/nullptr); + if (Actions.isCurrentClassName(*II, getCurScope(), &SS) || + isDeductionGuide) { + if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), + isDeductionGuide, + DeclSpec::FriendSpecified::No)) + return true; + } else if (SS.isNotEmpty()) { + // If the scope is not empty, it could alternatively be something like + // a typedef or using declaration. That declaration might be private + // in the global context, which would be diagnosed by calling into + // isCXXSimpleDeclaration, but may actually be fine in the context of + // member functions and static variable definitions. Check if the next + // token is also an identifier and assume a declaration. + // We cannot check if the scopes match because the declarations could + // involve namespaces and friend declarations. + if (NextToken().is(tok::identifier)) + return true; + } + break; + } + case tok::kw_operator: + return true; + case tok::tilde: + return true; + default: + break; + } + } + } + [[fallthrough]]; // simple-declaration default: return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/false); @@ -111,8 +159,8 @@ bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) { // a case. bool InvalidAsDeclaration = false; - TPResult TPR = isCXXDeclarationSpecifier(TPResult::False, - &InvalidAsDeclaration); + TPResult TPR = isCXXDeclarationSpecifier( + ImplicitTypenameContext::No, TPResult::False, &InvalidAsDeclaration); if (TPR != TPResult::Ambiguous) return TPR != TPResult::False; // Returns true for TPResult::True or // TPResult::Error. @@ -158,10 +206,12 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { ConsumeToken(); break; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case tok::kw_typeof: case tok::kw___attribute: - case tok::kw___underlying_type: { +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait: +#include "clang/Basic/TransformTypeTraits.def" + { ConsumeToken(); if (Tok.isNot(tok::l_paren)) return TPResult::Error; @@ -203,7 +253,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { case tok::annot_cxxscope: ConsumeAnnotationToken(); - LLVM_FALLTHROUGH; + [[fallthrough]]; default: ConsumeAnyToken(); @@ -224,6 +274,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; @@ -231,7 +282,7 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { // simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the // overwhelmingly common case that the next token is a '('. if (Tok.isNot(tok::l_paren)) { - TPResult TPR = isCXXDeclarationSpecifier(); + TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No); if (TPR == TPResult::Ambiguous) return TPResult::True; if (TPR == TPResult::True || TPR == TPResult::Error) @@ -239,7 +290,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; @@ -276,10 +328,15 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { /// '{' initializer-list ','[opt] '}' /// '{' '}' /// -Parser::TPResult Parser::TryParseInitDeclaratorList() { - while (1) { +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; @@ -353,8 +410,8 @@ struct Parser::ConditionDeclarationOrInitStatementState { if (CanBeForRangeDecl) { // Skip until we hit a ')', ';', or a ':' with no matching '?'. // The final case is a for range declaration, the rest are not. + unsigned QuestionColonDepth = 0; while (true) { - unsigned QuestionColonDepth = 0; P.SkipUntil({tok::r_paren, tok::semi, tok::question, tok::colon}, StopBeforeMatch); if (P.Tok.is(tok::question)) @@ -440,7 +497,8 @@ bool Parser::isEnumBase(bool AllowSemi) { // FIXME: We could disallow non-type decl-specifiers here, but it makes no // difference: those specifiers are ill-formed regardless of the // interpretation. - TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True, + TPResult R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, + /*BracedCastResult=*/TPResult::True, &InvalidAsDeclSpec); if (R == TPResult::Ambiguous) { // We either have a decl-specifier followed by '(' or an undeclared @@ -454,7 +512,8 @@ bool Parser::isEnumBase(bool AllowSemi) { return true; // A second decl-specifier unambiguously indicatges an enum-base. - R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec); + R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::True, + &InvalidAsDeclSpec); } return R != TPResult::False; @@ -483,20 +542,27 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement, ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement, CanBeForRangeDecl); - if (State.update(isCXXDeclarationSpecifier())) + if (CanBeInitStatement && Tok.is(tok::kw_using)) + return ConditionOrInitStatement::InitStmtDecl; + if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No))) return State.result(); // It might be a declaration; we need tentative parsing. 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. @@ -569,7 +635,7 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { // type. The resolution is that any construct that could possibly be a type-id // in its syntactic context shall be considered a type-id. - TPResult TPR = isCXXDeclarationSpecifier(); + TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No); if (TPR != TPResult::Ambiguous) return TPR != TPResult::False; // Returns true for TPResult::True or // TPResult::Error. @@ -581,13 +647,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) @@ -599,7 +668,12 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { if (Context == TypeIdInParens && Tok.is(tok::r_paren)) { TPR = TPResult::True; isAmbiguous = true; - + // We are supposed to be inside the first operand to a _Generic selection + // expression, so if we find a comma after the declarator, we've found a + // type and not an expression. + } else if (Context == TypeIdAsGenericSelectionArgument && Tok.is(tok::comma)) { + TPR = TPResult::True; + isAmbiguous = true; // We are supposed to be inside a template argument, so if after // the abstract declarator we encounter a '>', '>>' (in C++0x), or // ','; or, in C++0x, an ellipsis immediately preceding such, this @@ -616,6 +690,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; } @@ -663,6 +740,9 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, if (Tok.is(tok::kw_alignas)) return CAK_AttributeSpecifier; + if (Tok.isRegularKeywordAttribute()) + return CAK_AttributeSpecifier; + if (Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) return CAK_NotAttributeSpecifier; @@ -802,7 +882,8 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, bool Parser::TrySkipAttributes() { while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec, - tok::kw_alignas)) { + tok::kw_alignas) || + Tok.isRegularKeywordAttribute()) { if (Tok.is(tok::l_square)) { ConsumeBracket(); if (Tok.isNot(tok::l_square)) @@ -813,6 +894,9 @@ bool Parser::TrySkipAttributes() { // Note that explicitly checking for `[[` and `]]` allows to fail as // expected in the case of the Objective-C message send syntax. ConsumeBracket(); + } else if (Tok.isRegularKeywordAttribute() && + !doesKeywordAttributeTakeArgs(Tok.getKind())) { + ConsumeToken(); } else { ConsumeToken(); if (Tok.isNot(tok::l_paren)) @@ -930,7 +1014,7 @@ Parser::TPResult Parser::TryParseOperatorId() { // Maybe this is a conversion-function-id. bool AnyDeclSpecifiers = false; while (true) { - TPResult TPR = isCXXDeclarationSpecifier(); + TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No); if (TPR == TPResult::Error) return TPR; if (TPR == TPResult::False) { @@ -1000,7 +1084,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 @@ -1035,13 +1120,14 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, } else if (Tok.is(tok::l_paren)) { ConsumeParen(); if (mayBeAbstract && - (Tok.is(tok::r_paren) || // 'int()' is a function. - // 'int(...)' is a function. + (Tok.is(tok::r_paren) || // 'int()' is a function. + // 'int(...)' is a function. (Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren)) || - isDeclarationSpecifier())) { // 'int(int)' is a function. + isDeclarationSpecifier( + 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 { @@ -1066,7 +1152,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, if (mayHaveDirectInit) return TPResult::Ambiguous; - while (1) { + while (true) { TPResult TPR(TPResult::Ambiguous); if (Tok.is(tok::l_paren)) { @@ -1080,7 +1166,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] ']' @@ -1101,9 +1187,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, } bool Parser::isTentativelyDeclared(IdentifierInfo *II) { - return std::find(TentativelyDeclaredIdentifiers.begin(), - TentativelyDeclaredIdentifiers.end(), II) - != TentativelyDeclaredIdentifiers.end(); + return llvm::is_contained(TentativelyDeclaredIdentifiers, II); } namespace { @@ -1245,19 +1329,37 @@ public: /// [GNU] restrict /// Parser::TPResult -Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, +Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, + Parser::TPResult BracedCastResult, bool *InvalidAsDeclSpec) { - auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId, - int Lookahead) { + auto IsPlaceholderSpecifier = [&](TemplateIdAnnotation *TemplateId, + int Lookahead) { // We have a placeholder-constraint (we check for 'auto' or 'decltype' to // distinguish 'C<int>;' from 'C<int> auto c = 1;') return TemplateId->Kind == TNK_Concept_template && - GetLookAheadToken(Lookahead + 1).isOneOf(tok::kw_auto, tok::kw_decltype, - // If we have an identifier here, the user probably forgot the - // 'auto' in the placeholder constraint, e.g. 'C<int> x = 2;' - // This will be diagnosed nicely later, so disambiguate as a - // declaration. - tok::identifier); + (GetLookAheadToken(Lookahead + 1) + .isOneOf(tok::kw_auto, tok::kw_decltype, + // If we have an identifier here, the user probably + // forgot the 'auto' in the placeholder constraint, + // e.g. 'C<int> x = 2;' This will be diagnosed nicely + // later, so disambiguate as a declaration. + tok::identifier, + // CVR qualifierslikely the same situation for the + // user, so let this be diagnosed nicely later. We + // cannot handle references here, as `C<int> & Other` + // and `C<int> && Other` are both legal. + tok::kw_const, tok::kw_volatile, tok::kw_restrict) || + // While `C<int> && Other` is legal, doing so while not specifying a + // template argument is NOT, so see if we can fix up in that case at + // minimum. Concepts require at least 1 template parameter, so we + // can count on the argument count. + // FIXME: In the future, we migth be able to have SEMA look up the + // declaration for this concept, and see how many template + // parameters it has. If the concept isn't fully specified, it is + // possibly a situation where we want deduction, such as: + // `BinaryConcept<int> auto f = bar();` + (TemplateId->NumArgs == 0 && + GetLookAheadToken(Lookahead + 1).isOneOf(tok::amp, tok::ampamp))); }; switch (Tok.getKind()) { case tok::identifier: { @@ -1288,7 +1390,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // template template argument, we'll undo this when checking the // validity of the argument. if (getLangOpts().CPlusPlus17) { - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; if (Tok.isNot(tok::identifier)) break; @@ -1309,7 +1411,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // a missing 'typename' keyword. Don't use TryAnnotateName in this case, // since it will annotate as a primary expression, and we want to use the // "missing 'typename'" logic. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; // If annotation failed, assume it's a non-type. // FIXME: If this happens due to an undeclared identifier, treat it as @@ -1319,30 +1421,43 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, } // We annotated this token as something. Recurse to handle whatever we got. - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult, + InvalidAsDeclSpec); } case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(ImplicitTypenameContext::Yes)) return TPResult::Error; - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + 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 tok::kw_delete)) // ::delete return TPResult::False; - LLVM_FALLTHROUGH; + [[fallthrough]]; } case tok::kw___super: case tok::kw_decltype: // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult, + InvalidAsDeclSpec); // decl-specifier: // storage-class-specifier @@ -1361,7 +1476,6 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, 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: @@ -1401,7 +1515,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_private: if (!getLangOpts().OpenCL) return TPResult::False; - LLVM_FALLTHROUGH; + [[fallthrough]]; case tok::kw___private: case tok::kw___local: case tok::kw___global: @@ -1414,6 +1528,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // OpenCL pipe case tok::kw_pipe: + // HLSL address space qualifiers + case tok::kw_groupshared: + case tok::kw_in: + case tok::kw_inout: + case tok::kw_out: + // GNU case tok::kw_restrict: case tok::kw__Complex: @@ -1443,6 +1563,10 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw___kindof: return TPResult::True; + // WebAssemblyFuncref + case tok::kw___funcref: + return TPResult::True; + // Borland case tok::kw___pascal: return TPResult::True; @@ -1451,6 +1575,17 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw___vector: return TPResult::True; + case tok::kw_this: { + // Try to parse a C++23 Explicit Object Parameter + // We do that in all language modes to produce a better diagnostic. + if (getLangOpts().CPlusPlus) { + RevertingTentativeParsingAction PA(*this); + ConsumeToken(); + return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult, + InvalidAsDeclSpec); + } + return TPResult::False; + } case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); // If lookup for the template-name found nothing, don't assume we have a @@ -1472,14 +1607,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(SS); + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); assert(Tok.is(tok::annot_typename)); goto case_typename; } case tok::annot_cxxscope: // foo::bar or ::foo::bar, but already parsed // We've already annotated a scope; try to annotate a type. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; if (!Tok.is(tok::annot_typename)) { if (Tok.is(tok::annot_cxxscope) && @@ -1510,8 +1645,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, bool isIdentifier = Tok.is(tok::identifier); TPResult TPR = TPResult::False; if (!isIdentifier) - TPR = isCXXDeclarationSpecifier(BracedCastResult, - InvalidAsDeclSpec); + TPR = isCXXDeclarationSpecifier( + AllowImplicitTypename, BracedCastResult, InvalidAsDeclSpec); if (isIdentifier || TPR == TPResult::True || TPR == TPResult::Error) @@ -1537,7 +1672,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, } else { // Try to resolve the name. If it doesn't exist, assume it was // intended to name a type and keep disambiguating. - switch (TryAnnotateName()) { + switch (TryAnnotateName(/*CCC=*/nullptr, AllowImplicitTypename)) { case ANK_Error: return TPResult::Error; case ANK_TentativeDecl: @@ -1548,7 +1683,10 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, if (getLangOpts().CPlusPlus17) { if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; - if (Tok.isNot(tok::identifier)) + // If we annotated then the current token should not still be :: + // FIXME we may want to also check for tok::annot_typename but + // currently don't have a test case. + if (Tok.isNot(tok::annot_cxxscope)) break; } @@ -1567,13 +1705,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // Annotated it, check again. assert(Tok.isNot(tok::annot_cxxscope) || NextToken().isNot(tok::identifier)); - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier(AllowImplicitTypename, + BracedCastResult, InvalidAsDeclSpec); } } return TPResult::False; } // If that succeeded, fallthrough into the generic simple-type-id case. - LLVM_FALLTHROUGH; + [[fallthrough]]; // The ambiguity resides in a simple-type-specifier/typename-specifier // followed by a '('. The '(' could either be the start of: @@ -1616,7 +1755,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, return TPResult::True; } - LLVM_FALLTHROUGH; + [[fallthrough]]; case tok::kw_char: case tok::kw_wchar_t: @@ -1637,8 +1776,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw___bf16: case tok::kw__Float16: case tok::kw___float128: + case tok::kw___ibm128: case tok::kw_void: case tok::annot_decltype: + case tok::kw__Accum: + case tok::kw__Fract: + case tok::kw__Sat: #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: #include "clang/Basic/OpenCLImageTypes.def" if (NextToken().is(tok::l_paren)) @@ -1681,14 +1824,15 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, return TPResult::True; } - // C++0x type traits support - case tok::kw___underlying_type: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait: +#include "clang/Basic/TransformTypeTraits.def" return TPResult::True; // C11 _Atomic case tok::kw__Atomic: return TPResult::True; + case tok::kw__BitInt: case tok::kw__ExtInt: { if (NextToken().isNot(tok::l_paren)) return TPResult::Error; @@ -1719,7 +1863,8 @@ bool Parser::isCXXDeclarationSpecifierAType() { case tok::annot_template_id: case tok::annot_typename: case tok::kw_typeof: - case tok::kw___underlying_type: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait: +#include "clang/Basic/TransformTypeTraits.def" return true; // elaborated-type-specifier @@ -1740,6 +1885,7 @@ bool Parser::isCXXDeclarationSpecifierAType() { case tok::kw_short: case tok::kw_int: case tok::kw__ExtInt: + case tok::kw__BitInt: case tok::kw_long: case tok::kw___int64: case tok::kw___int128: @@ -1751,9 +1897,13 @@ bool Parser::isCXXDeclarationSpecifierAType() { case tok::kw___bf16: case tok::kw__Float16: case tok::kw___float128: + case tok::kw___ibm128: case tok::kw_void: case tok::kw___unknown_anytype: case tok::kw___auto_type: + case tok::kw__Accum: + case tok::kw__Fract: + case tok::kw__Sat: #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: #include "clang/Basic/OpenCLImageTypes.def" return true; @@ -1821,7 +1971,8 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() { /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// -bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { +bool Parser::isCXXFunctionDeclarator( + bool *IsAmbiguous, ImplicitTypenameContext AllowImplicitTypename) { // C++ 8.2p1: // The ambiguity arising from the similarity between a function-style cast and @@ -1836,7 +1987,9 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { ConsumeParen(); bool InvalidAsDeclaration = false; - TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration); + TPResult TPR = TryParseParameterDeclarationClause( + &InvalidAsDeclaration, /*VersusTemplateArgument=*/false, + AllowImplicitTypename); if (TPR == TPResult::Ambiguous) { if (Tok.isNot(tok::r_paren)) TPR = TPResult::False; @@ -1880,9 +2033,9 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { /// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] /// attributes[opt] '=' assignment-expression /// -Parser::TPResult -Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, - bool VersusTemplateArgument) { +Parser::TPResult Parser::TryParseParameterDeclarationClause( + bool *InvalidAsDeclaration, bool VersusTemplateArgument, + ImplicitTypenameContext AllowImplicitTypename) { if (Tok.is(tok::r_paren)) return TPResult::Ambiguous; @@ -1894,7 +2047,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, // parameter-declaration // parameter-declaration-list ',' parameter-declaration // - while (1) { + while (true) { // '...'[opt] if (Tok.is(tok::ellipsis)) { ConsumeToken(); @@ -1915,8 +2068,8 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, // decl-specifier-seq // A parameter-declaration's initializer must be preceded by an '=', so // decl-specifier-seq '{' is not a parameter in C++11. - TPResult TPR = isCXXDeclarationSpecifier(TPResult::False, - InvalidAsDeclaration); + TPResult TPR = isCXXDeclarationSpecifier( + AllowImplicitTypename, TPResult::False, InvalidAsDeclaration); // A declaration-specifier (not followed by '(' or '{') means this can't be // an expression, but it could still be a template argument. if (TPR != TPResult::Ambiguous && @@ -1924,6 +2077,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, return TPR; bool SeenType = false; + bool DeclarationSpecifierIsAuto = Tok.is(tok::kw_auto); do { SeenType |= isCXXDeclarationSpecifierAType(); if (TryConsumeDeclarationSpecifier() == TPResult::Error) @@ -1933,7 +2087,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, if (SeenType && Tok.is(tok::identifier)) return TPResult::True; - TPR = isCXXDeclarationSpecifier(TPResult::False, + TPR = isCXXDeclarationSpecifier(AllowImplicitTypename, TPResult::False, InvalidAsDeclaration); if (TPR == TPResult::Error) return TPR; @@ -1945,7 +2099,11 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, // declarator // abstract-declarator[opt] - TPR = TryParseDeclarator(true/*mayBeAbstract*/); + TPR = TryParseDeclarator( + /*mayBeAbstract=*/true, + /*mayHaveIdentifier=*/true, + /*mayHaveDirectInit=*/false, + /*mayHaveTrailingReturnType=*/DeclarationSpecifierIsAuto); if (TPR != TPResult::Ambiguous) return TPR; @@ -1999,7 +2157,8 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, /// exception-specification: /// 'throw' '(' type-id-list[opt] ')' /// -Parser::TPResult Parser::TryParseFunctionDeclarator() { +Parser::TPResult +Parser::TryParseFunctionDeclarator(bool MayHaveTrailingReturnType) { // The '(' is already parsed. TPResult TPR = TryParseParameterDeclarationClause(); @@ -2044,9 +2203,52 @@ 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 (Tok.is(tok::identifier) && NameAfterArrowIsNonType()) { + return TPResult::False; + } + if (isCXXTypeId(TentativeCXXTypeIdContext::TypeIdInTrailingReturnType)) + return TPResult::True; + } + return TPResult::Ambiguous; } +// When parsing an identifier after an arrow it may be a member expression, +// in which case we should not annotate it as an independant expression +// so we just lookup that name, if it's not a type the construct is not +// a function declaration. +bool Parser::NameAfterArrowIsNonType() { + assert(Tok.is(tok::identifier)); + Token Next = NextToken(); + if (Next.is(tok::coloncolon)) + return false; + IdentifierInfo *Name = Tok.getIdentifierInfo(); + SourceLocation NameLoc = Tok.getLocation(); + CXXScopeSpec SS; + TentativeParseCCC CCC(Next); + Sema::NameClassification Classification = + Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next, &CCC); + switch (Classification.getKind()) { + case Sema::NC_OverloadSet: + case Sema::NC_NonType: + case Sema::NC_VarTemplate: + case Sema::NC_FunctionTemplate: + return true; + default: + break; + } + return false; +} + /// '[' constant-expression[opt] ']' /// Parser::TPResult Parser::TryParseBracketDeclarator() { @@ -2095,9 +2297,9 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { // but one good distinguishing factor is that a "decl-specifier" not // followed by '(' or '{' can't appear in an expression. bool InvalidAsTemplateArgumentList = false; - if (isCXXDeclarationSpecifier(TPResult::False, - &InvalidAsTemplateArgumentList) == - TPResult::True) + if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False, + &InvalidAsTemplateArgumentList) == + TPResult::True) return TPResult::True; if (InvalidAsTemplateArgumentList) return TPResult::False; |