[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
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index c4fe23c..e884884 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -597,10 +597,11 @@
// 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 @@
// 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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
? 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;