summaryrefslogtreecommitdiffstats
path: root/clang/lib/Parse
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-05-09 03:31:27 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-05-09 03:31:27 +0000
commitb23c5e8c3df850177449268c5ca7dbf986157525 (patch)
tree676dabf23d28ebd62d0f453558f8874ea7eafe29 /clang/lib/Parse
parenta438a898b02a93a0179be1dd5ed67fb5b01dcf6b (diff)
downloadbcm5719-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.cpp4
-rw-r--r--clang/lib/Parse/ParseDeclCXX.cpp97
-rw-r--r--clang/lib/Parse/ParseExprCXX.cpp26
-rw-r--r--clang/lib/Parse/ParseTemplate.cpp42
-rw-r--r--clang/lib/Parse/ParseTentative.cpp104
-rw-r--r--clang/lib/Parse/Parser.cpp51
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;
+ }
}
}
}
OpenPOWER on IntegriCloud