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 | fb98e7c13da639f34f34830a4e675367e0816cb6 (patch) | |
tree | 70e025e9b4a31559d1f061e72439b5414537ad3b /lib/Parse/ParseDeclCXX.cpp | |
parent | 70cd574857c6023b8c60e325c4029a23f716c2dd (diff) | |
download | clang-fb98e7c13da639f34f34830a4e675367e0816cb6.tar.gz |
[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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@360308 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Parse/ParseDeclCXX.cpp')
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 97 |
1 files changed, 61 insertions, 36 deletions
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index c4fe23c60c..e884884163 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/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; |