diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-05-09 03:31:27 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-05-09 03:31:27 +0000 |
commit | b23c5e8c3df850177449268c5ca7dbf986157525 (patch) | |
tree | 676dabf23d28ebd62d0f453558f8874ea7eafe29 /clang/lib/Parse | |
parent | a438a898b02a93a0179be1dd5ed67fb5b01dcf6b (diff) | |
download | bcm5719-llvm-b23c5e8c3df850177449268c5ca7dbf986157525.tar.gz bcm5719-llvm-b23c5e8c3df850177449268c5ca7dbf986157525.zip |
[c++20] Implement P0846R0: allow (ADL-only) calls to template-ids whose
template name is not visible to unqualified lookup.
In order to support this without a severe degradation in our ability to
diagnose typos in template names, this change significantly restructures
the way we handle template-id-shaped syntax for which lookup of the
template name finds nothing.
Instead of eagerly diagnosing an undeclared template name, we now form a
placeholder template-name representing a name that is known to not find
any templates. When the parser sees such a name, it attempts to
disambiguate whether we have a less-than comparison or a template-id.
Any diagnostics or typo-correction for the name are delayed until its
point of use.
The upshot should be a small improvement of our diagostic quality
overall: we now take more syntactic context into account when trying to
resolve an undeclared identifier on the left hand side of a '<'. In
fact, this works well enough that the backwards-compatible portion (for
an undeclared identifier rather than a lookup that finds functions but
no function templates) is enabled in all language modes.
llvm-svn: 360308
Diffstat (limited to 'clang/lib/Parse')
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 97 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 26 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTemplate.cpp | 42 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 104 | ||||
-rw-r--r-- | clang/lib/Parse/Parser.cpp | 51 |
6 files changed, 218 insertions, 106 deletions
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f5c404f7e62..9c9a9f46798 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2916,6 +2916,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, case Sema::NC_Expression: case Sema::NC_VarTemplate: case Sema::NC_FunctionTemplate: + case Sema::NC_UndeclaredTemplate: // Might be a redeclaration of a prior entity. break; } @@ -3386,7 +3387,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // type-name case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - if (TemplateId->Kind != TNK_Type_template) { + if (TemplateId->Kind != TNK_Type_template && + TemplateId->Kind != TNK_Undeclared_template) { // This template-id does not refer to a type name, so we're // done with the type-specifiers. goto DoneWithDeclSpec; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index c4fe23c60c3..e8848841635 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -597,10 +597,11 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context, // Parse nested-name-specifier. IdentifierInfo *LastII = nullptr; - ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false, - /*MayBePseudoDtor=*/nullptr, - /*IsTypename=*/false, - /*LastII=*/&LastII); + if (ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false, + /*MayBePseudoDtor=*/nullptr, + /*IsTypename=*/false, + /*LastII=*/&LastII)) + return true; if (D.SS.isInvalid()) return true; @@ -1111,7 +1112,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, // Parse optional nested-name-specifier CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); + if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false)) + return true; BaseLoc = Tok.getLocation(); @@ -1135,7 +1137,8 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template || - TemplateId->Kind == TNK_Dependent_template_name) { + TemplateId->Kind == TNK_Dependent_template_name || + TemplateId->Kind == TNK_Undeclared_template) { AnnotateTemplateIdTokenAsType(/*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); @@ -1554,6 +1557,36 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateParameterLists *TemplateParams = TemplateInfo.TemplateParams; + auto RecoverFromUndeclaredTemplateName = [&](IdentifierInfo *Name, + SourceLocation NameLoc, + SourceRange TemplateArgRange, + bool KnownUndeclared) { + Diag(NameLoc, diag::err_explicit_spec_non_template) + << (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) + << TagTokKind << Name << TemplateArgRange << KnownUndeclared; + + // Strip off the last template parameter list if it was empty, since + // we've removed its template argument list. + if (TemplateParams && TemplateInfo.LastParameterListWasEmpty) { + if (TemplateParams->size() > 1) { + TemplateParams->pop_back(); + } else { + TemplateParams = nullptr; + const_cast<ParsedTemplateInfo &>(TemplateInfo).Kind = + ParsedTemplateInfo::NonTemplate; + } + } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) { + // Pretend this is just a forward declaration. + TemplateParams = nullptr; + const_cast<ParsedTemplateInfo &>(TemplateInfo).Kind = + ParsedTemplateInfo::NonTemplate; + const_cast<ParsedTemplateInfo &>(TemplateInfo).TemplateLoc = + SourceLocation(); + const_cast<ParsedTemplateInfo &>(TemplateInfo).ExternLoc = + SourceLocation(); + } + }; + // Parse the (optional) class name or simple-template-id. IdentifierInfo *Name = nullptr; SourceLocation NameLoc; @@ -1574,38 +1607,26 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // try to give any location information for the list. LAngleLoc = RAngleLoc = SourceLocation(); } - - Diag(NameLoc, diag::err_explicit_spec_non_template) - << (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) - << TagTokKind << Name << SourceRange(LAngleLoc, RAngleLoc); - - // Strip off the last template parameter list if it was empty, since - // we've removed its template argument list. - if (TemplateParams && TemplateInfo.LastParameterListWasEmpty) { - if (TemplateParams->size() > 1) { - TemplateParams->pop_back(); - } else { - TemplateParams = nullptr; - const_cast<ParsedTemplateInfo&>(TemplateInfo).Kind - = ParsedTemplateInfo::NonTemplate; - } - } else if (TemplateInfo.Kind - == ParsedTemplateInfo::ExplicitInstantiation) { - // Pretend this is just a forward declaration. - TemplateParams = nullptr; - const_cast<ParsedTemplateInfo&>(TemplateInfo).Kind - = ParsedTemplateInfo::NonTemplate; - const_cast<ParsedTemplateInfo&>(TemplateInfo).TemplateLoc - = SourceLocation(); - const_cast<ParsedTemplateInfo&>(TemplateInfo).ExternLoc - = SourceLocation(); - } + RecoverFromUndeclaredTemplateName( + Name, NameLoc, SourceRange(LAngleLoc, RAngleLoc), false); } } else if (Tok.is(tok::annot_template_id)) { TemplateId = takeTemplateIdAnnotation(Tok); NameLoc = ConsumeAnnotationToken(); - if (TemplateId->Kind != TNK_Type_template && + if (TemplateId->Kind == TNK_Undeclared_template) { + // Try to resolve the template name to a type template. + Actions.ActOnUndeclaredTypeTemplateName(getCurScope(), TemplateId->Template, + TemplateId->Kind, NameLoc, Name); + if (TemplateId->Kind == TNK_Undeclared_template) { + RecoverFromUndeclaredTemplateName( + Name, NameLoc, + SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc), true); + TemplateId = nullptr; + } + } + + if (TemplateId && TemplateId->Kind != TNK_Type_template && TemplateId->Kind != TNK_Dependent_template_name) { // The template-name in the simple-template-id refers to // something other than a class template. Give an appropriate @@ -1616,7 +1637,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // FIXME: Name may be null here. Diag(TemplateId->LAngleLoc, diag::err_template_spec_syntax_non_template) - << TemplateId->Name << static_cast<int>(TemplateId->Kind) << Range; + << TemplateId->Name << static_cast<int>(TemplateId->Kind) << Range; DS.SetTypeSpecError(); SkipUntil(tok::semi, StopBeforeMatch); @@ -3451,7 +3472,8 @@ void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) { MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { // parse '::'[opt] nested-name-specifier[opt] CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); + if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false)) + return true; // : identifier IdentifierInfo *II = nullptr; @@ -3477,11 +3499,14 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { ? takeTemplateIdAnnotation(Tok) : nullptr; if (TemplateId && (TemplateId->Kind == TNK_Type_template || - TemplateId->Kind == TNK_Dependent_template_name)) { + TemplateId->Kind == TNK_Dependent_template_name || + TemplateId->Kind == TNK_Undeclared_template)) { AnnotateTemplateIdTokenAsType(/*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TemplateTypeTy = getTypeAnnotation(Tok); ConsumeAnnotationToken(); + if (!TemplateTypeTy) + return true; } else { Diag(Tok, diag::err_expected_member_or_base_name); return true; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index fbafb436880..c6f457fb149 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -487,6 +487,14 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, EnteringContext, Template, MemberOfUnknownSpecialization)) { + // If lookup didn't find anything, we treat the name as a template-name + // anyway. C++20 requires this, and in prior language modes it improves + // error recovery. But before we commit to this, check that we actually + // have something that looks like a template-argument-list next. + if (!IsTypename && TNK == TNK_Undeclared_template && + isTemplateArgumentList(1) == TPResult::False) + break; + // We have found a template name, so annotate this token // with a template-id annotation. We do not permit the // template-id to be translated into a type annotation, @@ -501,7 +509,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, } if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) && - (IsTypename || IsTemplateArgumentList(1))) { + (IsTypename || isTemplateArgumentList(1) == TPResult::True)) { // We have something like t::getAs<T>, where getAs is a // member of an unknown specialization. However, this will only // parse correctly as a template, so suggest the keyword 'template' @@ -2138,9 +2146,15 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, TemplateKWLoc.isValid(), Id, ObjectType, EnteringContext, Template, MemberOfUnknownSpecialization); + // If lookup found nothing but we're assuming that this is a template + // name, double-check that makes sense syntactically before committing + // to it. + if (TNK == TNK_Undeclared_template && + isTemplateArgumentList(0) == TPResult::False) + return false; if (TNK == TNK_Non_template && MemberOfUnknownSpecialization && - ObjectType && IsTemplateArgumentList()) { + ObjectType && isTemplateArgumentList(0) == TPResult::True) { // We have something like t->getAs<T>(), where getAs is a // member of an unknown specialization. However, this will only // parse correctly as a template, so suggest the keyword 'template' @@ -2244,11 +2258,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs); // Constructor and destructor names. - TypeResult Type - = Actions.ActOnTemplateIdType(SS, TemplateKWLoc, - Template, Name, NameLoc, - LAngleLoc, TemplateArgsPtr, RAngleLoc, - /*IsCtorOrDtorName=*/true); + TypeResult Type = Actions.ActOnTemplateIdType( + getCurScope(), SS, TemplateKWLoc, Template, Name, NameLoc, LAngleLoc, + TemplateArgsPtr, RAngleLoc, /*IsCtorOrDtorName=*/true); if (Type.isInvalid()) return true; diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index d028c8f4c39..b53e6ea09bb 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1031,6 +1031,8 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, // If we failed to parse the template ID but skipped ahead to a >, we're not // going to be able to form a token annotation. Eat the '>' if present. TryConsumeToken(tok::greater); + // FIXME: Annotate the token stream so we don't produce the same errors + // again if we're doing this annotation as part of a tentative parse. return true; } @@ -1039,13 +1041,15 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, // Build the annotation token. if (TNK == TNK_Type_template && AllowTypeAnnotation) { TypeResult Type = Actions.ActOnTemplateIdType( - SS, TemplateKWLoc, Template, TemplateName.Identifier, + getCurScope(), SS, TemplateKWLoc, Template, TemplateName.Identifier, TemplateNameLoc, LAngleLoc, TemplateArgsPtr, RAngleLoc); if (Type.isInvalid()) { // If we failed to parse the template ID but skipped ahead to a >, we're // not going to be able to form a token annotation. Eat the '>' if // present. TryConsumeToken(tok::greater); + // FIXME: Annotate the token stream so we don't produce the same errors + // again if we're doing this annotation as part of a tentative parse. return true; } @@ -1108,14 +1112,16 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); assert((TemplateId->Kind == TNK_Type_template || - TemplateId->Kind == TNK_Dependent_template_name) && + TemplateId->Kind == TNK_Dependent_template_name || + TemplateId->Kind == TNK_Undeclared_template) && "Only works for type and dependent templates"); ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); TypeResult Type - = Actions.ActOnTemplateIdType(TemplateId->SS, + = Actions.ActOnTemplateIdType(getCurScope(), + TemplateId->SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -1272,36 +1278,6 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { ExprArg.get(), Loc); } -/// Determine whether the current tokens can only be parsed as a -/// template argument list (starting with the '<') and never as a '<' -/// expression. -bool Parser::IsTemplateArgumentList(unsigned Skip) { - struct AlwaysRevertAction : TentativeParsingAction { - AlwaysRevertAction(Parser &P) : TentativeParsingAction(P) { } - ~AlwaysRevertAction() { Revert(); } - } Tentative(*this); - - while (Skip) { - ConsumeAnyToken(); - --Skip; - } - - // '<' - if (!TryConsumeToken(tok::less)) - return false; - - // An empty template argument list. - if (Tok.is(tok::greater)) - return true; - - // See whether we have declaration specifiers, which indicate a type. - while (isCXXDeclarationSpecifier() == TPResult::True) - ConsumeAnyToken(); - - // If we have a '>' or a ',' then this is a template argument list. - return Tok.isOneOf(tok::greater, tok::comma); -} - /// ParseTemplateArgumentList - Parse a C++ template-argument-list /// (C++ [temp.names]). Returns true if there was an error. /// diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 0504f0ddcbe..2d2705d8471 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1178,12 +1178,17 @@ public: /// be either a decl-specifier or a function-style cast, and TPResult::Error /// if a parsing error was found and reported. /// -/// If HasMissingTypename is provided, a name with a dependent scope specifier -/// will be treated as ambiguous if the 'typename' keyword is missing. If this -/// happens, *HasMissingTypename will be set to 'true'. This will also be used -/// as an indicator that undeclared identifiers (which will trigger a later -/// parse error) should be treated as types. Returns TPResult::Ambiguous in -/// such cases. +/// If InvalidAsDeclSpec is not null, some cases that would be ill-formed as +/// declaration specifiers but possibly valid as some other kind of construct +/// return TPResult::Ambiguous instead of TPResult::False. When this happens, +/// the intent is to keep trying to disambiguate, on the basis that we might +/// find a better reason to treat this construct as a declaration later on. +/// When this happens and the name could possibly be valid in some other +/// syntactic context, *InvalidAsDeclSpec is set to 'true'. The current cases +/// that trigger this are: +/// +/// * When parsing X::Y (with no 'typename') where X is dependent +/// * When parsing X<Y> where X is undeclared /// /// decl-specifier: /// storage-class-specifier @@ -1281,7 +1286,7 @@ public: /// Parser::TPResult Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, - bool *HasMissingTypename) { + bool *InvalidAsDeclSpec) { switch (Tok.getKind()) { case tok::identifier: { // Check for need to substitute AltiVec __vector keyword @@ -1321,7 +1326,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // argument is an error, and was probably intended to be a type. return GreaterThanIsOperator ? TPResult::True : TPResult::False; case ANK_Unresolved: - return HasMissingTypename ? TPResult::Ambiguous : TPResult::False; + return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False; case ANK_Success: break; } @@ -1342,7 +1347,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, } // We annotated this token as something. Recurse to handle whatever we got. - return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); + return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); } case tok::kw_typename: // typename T::type @@ -1350,7 +1355,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; - return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); + return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); case tok::coloncolon: { // ::foo::bar const Token &Next = NextToken(); @@ -1365,7 +1370,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; - return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); + return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); // decl-specifier: // storage-class-specifier @@ -1471,6 +1476,16 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + // If lookup for the template-name found nothing, don't assume we have a + // definitive disambiguation result yet. + if (TemplateId->Kind == TNK_Undeclared_template && InvalidAsDeclSpec) { + // 'template-id(' can be a valid expression but not a valid decl spec if + // the template-name is not declared, but we don't consider this to be a + // definitive disambiguation. In any other context, it's an error either + // way. + *InvalidAsDeclSpec = NextToken().is(tok::l_paren); + return TPResult::Ambiguous; + } if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; @@ -1499,19 +1514,19 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, TPResult TPR = TPResult::False; if (!isIdentifier) TPR = isCXXDeclarationSpecifier(BracedCastResult, - HasMissingTypename); + InvalidAsDeclSpec); if (isIdentifier || TPR == TPResult::True || TPR == TPResult::Error) return TPResult::Error; - if (HasMissingTypename) { + if (InvalidAsDeclSpec) { // We can't tell whether this is a missing 'typename' or a valid // expression. - *HasMissingTypename = true; + *InvalidAsDeclSpec = true; return TPResult::Ambiguous; } else { - // In MS mode, if HasMissingTypename is not provided, and the tokens + // In MS mode, if InvalidAsDeclSpec is not provided, and the tokens // are or the form *) or &) *> or &> &&>, this can't be an expression. // The typename must be missing. if (getLangOpts().MSVCCompat) { @@ -1547,8 +1562,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, ? TPResult::True : TPResult::False; case ANK_Unresolved: - return HasMissingTypename ? TPResult::Ambiguous - : TPResult::False; + return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False; case ANK_Success: break; } @@ -1556,8 +1570,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // Annotated it, check again. assert(Tok.isNot(tok::annot_cxxscope) || NextToken().isNot(tok::identifier)); - return isCXXDeclarationSpecifier(BracedCastResult, - HasMissingTypename); + return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); } } return TPResult::False; @@ -2029,3 +2042,56 @@ Parser::TPResult Parser::TryParseBracketDeclarator() { return TPResult::Ambiguous; } + +/// Determine whether we might be looking at the '<' template-argument-list '>' +/// of a template-id or simple-template-id, rather than a less-than comparison. +/// This will often fail and produce an ambiguity, but should never be wrong +/// if it returns True or False. +Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { + if (!TokensToSkip) { + if (Tok.isNot(tok::less)) + return TPResult::False; + if (NextToken().is(tok::greater)) + return TPResult::True; + } + + RevertingTentativeParsingAction PA(*this); + + while (TokensToSkip) { + ConsumeAnyToken(); + --TokensToSkip; + } + + if (!TryConsumeToken(tok::less)) + return TPResult::False; + + bool InvalidAsTemplateArgumentList = false; + while (true) { + // We can't do much to tell an expression apart from a template-argument, + // but one good distinguishing factor is that a "decl-specifier" not + // followed by '(' or '{' can't appear in an expression. + if (isCXXDeclarationSpecifier( + TPResult::False, &InvalidAsTemplateArgumentList) == TPResult::True) + return TPResult::True; + + // That didn't help, try the next template-argument. + SkipUntil({tok::comma, tok::greater, tok::greatergreater, + tok::greatergreatergreater}, + StopAtSemi | StopBeforeMatch); + switch (Tok.getKind()) { + case tok::comma: + ConsumeToken(); + break; + + case tok::greater: + case tok::greatergreater: + case tok::greatergreatergreater: + if (InvalidAsTemplateArgumentList) + return TPResult::False; + return TPResult::Ambiguous; + + default: + return TPResult::False; + } + } +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 6b25d6c038b..0c8e203caf4 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -315,6 +315,14 @@ bool Parser::SkipUntil(ArrayRef<tok::TokenKind> Toks, SkipUntilFlags Flags) { else SkipUntil(tok::r_brace); break; + case tok::question: + // Recursively skip ? ... : pairs; these function as brackets. But + // still stop at a semicolon if requested. + ConsumeToken(); + SkipUntil(tok::colon, + SkipUntilFlags(unsigned(Flags) & + unsigned(StopAtCodeCompletion | StopAtSemi))); + break; // Okay, we found a ']' or '}' or ')', which we think should be balanced. // Since the user wasn't looking for this token (if they were, it would @@ -1600,6 +1608,20 @@ Parser::TryAnnotateName(bool IsAddressOfOperand, Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next, IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr); + // If name lookup found nothing and we guessed that this was a template name, + // double-check before committing to that interpretation. C++20 requires that + // we interpret this as a template-id if it can be, but if it can't be, then + // this is an error recovery case. + if (Classification.getKind() == Sema::NC_UndeclaredTemplate && + isTemplateArgumentList(1) == TPResult::False) { + // It's not a template-id; re-classify without the '<' as a hint. + Token FakeNext = Next; + FakeNext.setKind(tok::unknown); + Classification = + Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, FakeNext, + IsAddressOfOperand, SS.isEmpty() ? CCC : nullptr); + } + switch (Classification.getKind()) { case Sema::NC_Error: return ANK_Error; @@ -1668,7 +1690,8 @@ Parser::TryAnnotateName(bool IsAddressOfOperand, } LLVM_FALLTHROUGH; case Sema::NC_VarTemplate: - case Sema::NC_FunctionTemplate: { + case Sema::NC_FunctionTemplate: + case Sema::NC_UndeclaredTemplate: { // We have a type, variable or function template followed by '<'. ConsumeToken(); UnqualifiedId Id; @@ -1791,7 +1814,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() { } else if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind != TNK_Type_template && - TemplateId->Kind != TNK_Dependent_template_name) { + TemplateId->Kind != TNK_Dependent_template_name && + TemplateId->Kind != TNK_Undeclared_template) { Diag(Tok, diag::err_typename_refers_to_non_type_template) << Tok.getAnnotationRange(); return true; @@ -1890,6 +1914,8 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, } // If this is a template-id, annotate with a template-id or type token. + // FIXME: This appears to be dead code. We already have formed template-id + // tokens when parsing the scope specifier; this can never form a new one. if (NextToken().is(tok::less)) { TemplateTy Template; UnqualifiedId TemplateName; @@ -1900,14 +1926,19 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, /*hasTemplateKeyword=*/false, TemplateName, /*ObjectType=*/nullptr, /*EnteringContext*/false, Template, MemberOfUnknownSpecialization)) { - // Consume the identifier. - ConsumeToken(); - if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), - TemplateName)) { - // If an unrecoverable error occurred, we need to return true here, - // because the token stream is in a damaged state. We may not return - // a valid identifier. - return true; + // Only annotate an undeclared template name as a template-id if the + // following tokens have the form of a template argument list. + if (TNK != TNK_Undeclared_template || + isTemplateArgumentList(1) != TPResult::False) { + // Consume the identifier. + ConsumeToken(); + if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), + TemplateName)) { + // If an unrecoverable error occurred, we need to return true here, + // because the token stream is in a damaged state. We may not + // return a valid identifier. + return true; + } } } } |