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/Sema/SemaTemplate.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/Sema/SemaTemplate.cpp')
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 134 |
1 files changed, 117 insertions, 17 deletions
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 58ad439747..c58c446e52 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -130,10 +130,15 @@ void Sema::FilterAcceptableTemplateNames(LookupResult &R, bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R, bool AllowFunctionTemplates, - bool AllowDependent) { - for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) + bool AllowDependent, + bool AllowNonTemplateFunctions) { + for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) { if (getAsTemplateNameDecl(*I, AllowFunctionTemplates, AllowDependent)) return true; + if (AllowNonTemplateFunctions && + isa<FunctionDecl>((*I)->getUnderlyingDecl())) + return true; + } return false; } @@ -171,11 +176,25 @@ TemplateNameKind Sema::isTemplateName(Scope *S, QualType ObjectType = ObjectTypePtr.get(); + AssumedTemplateKind AssumedTemplate; LookupResult R(*this, TName, Name.getBeginLoc(), LookupOrdinaryName); if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, - MemberOfUnknownSpecialization)) + MemberOfUnknownSpecialization, SourceLocation(), + &AssumedTemplate)) + return TNK_Non_template; + + if (AssumedTemplate != AssumedTemplateKind::None) { + TemplateResult = TemplateTy::make(Context.getAssumedTemplateName(TName)); + // Let the parser know whether we found nothing or found functions; if we + // found nothing, we want to more carefully check whether this is actually + // a function template name versus some other kind of undeclared identifier. + return AssumedTemplate == AssumedTemplateKind::FoundNothing + ? TNK_Undeclared_template + : TNK_Function_template; + } + + if (R.empty()) return TNK_Non_template; - if (R.empty()) return TNK_Non_template; NamedDecl *D = nullptr; if (R.isAmbiguous()) { @@ -325,7 +344,11 @@ bool Sema::LookupTemplateName(LookupResult &Found, QualType ObjectType, bool EnteringContext, bool &MemberOfUnknownSpecialization, - SourceLocation TemplateKWLoc) { + SourceLocation TemplateKWLoc, + AssumedTemplateKind *ATK) { + if (ATK) + *ATK = AssumedTemplateKind::None; + Found.setTemplateNameLookup(true); // Determine where to perform name lookup @@ -405,6 +428,32 @@ bool Sema::LookupTemplateName(LookupResult &Found, if (Found.isAmbiguous()) return false; + if (ATK && !SS.isSet() && ObjectType.isNull() && TemplateKWLoc.isInvalid()) { + // C++2a [temp.names]p2: + // A name is also considered to refer to a template if it is an + // unqualified-id followed by a < and name lookup finds either one or more + // functions or finds nothing. + // + // To keep our behavior consistent, we apply the "finds nothing" part in + // all language modes, and diagnose the empty lookup in ActOnCallExpr if we + // successfully form a call to an undeclared template-id. + bool AllFunctions = + getLangOpts().CPlusPlus2a && + std::all_of(Found.begin(), Found.end(), [](NamedDecl *ND) { + return isa<FunctionDecl>(ND->getUnderlyingDecl()); + }); + if (AllFunctions || (Found.empty() && !IsDependent)) { + // If lookup found any functions, or if this is a name that can only be + // used for a function, then strongly assume this is a function + // template-id. + *ATK = (Found.empty() && Found.getLookupName().isIdentifier()) + ? AssumedTemplateKind::FoundNothing + : AssumedTemplateKind::FoundFunctions; + Found.clear(); + return false; + } + } + if (Found.empty() && !IsDependent) { // If we did not find any names, attempt to correct any typos. DeclarationName Name = Found.getLookupName(); @@ -418,13 +467,13 @@ bool Sema::LookupTemplateName(LookupResult &Found, if (TypoCorrection Corrected = CorrectTypo(Found.getLookupNameInfo(), Found.getLookupKind(), S, &SS, FilterCCC, CTK_ErrorRecovery, LookupCtx)) { - Found.setLookupName(Corrected.getCorrection()); if (auto *ND = Corrected.getFoundDecl()) Found.addDecl(ND); FilterAcceptableTemplateNames(Found); if (Found.isAmbiguous()) { Found.clear(); } else if (!Found.empty()) { + Found.setLookupName(Corrected.getCorrection()); if (LookupCtx) { std::string CorrectedStr(Corrected.getAsString(getLangOpts())); bool DroppedSpecifier = Corrected.WillReplaceSpecifier() && @@ -436,8 +485,6 @@ bool Sema::LookupTemplateName(LookupResult &Found, diagnoseTypo(Corrected, PDiag(diag::err_no_template_suggest) << Name); } } - } else { - Found.setLookupName(Name); } } @@ -3348,14 +3395,65 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, return Context.getTemplateSpecializationType(Name, TemplateArgs, CanonType); } -TypeResult -Sema::ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - TemplateTy TemplateD, IdentifierInfo *TemplateII, - SourceLocation TemplateIILoc, - SourceLocation LAngleLoc, - ASTTemplateArgsPtr TemplateArgsIn, - SourceLocation RAngleLoc, - bool IsCtorOrDtorName, bool IsClassName) { +void Sema::ActOnUndeclaredTypeTemplateName(Scope *S, TemplateTy &ParsedName, + TemplateNameKind &TNK, + SourceLocation NameLoc, + IdentifierInfo *&II) { + assert(TNK == TNK_Undeclared_template && "not an undeclared template name"); + + TemplateName Name = ParsedName.get(); + auto *ATN = Name.getAsAssumedTemplateName(); + assert(ATN && "not an assumed template name"); + II = ATN->getDeclName().getAsIdentifierInfo(); + + if (!resolveAssumedTemplateNameAsType(S, Name, NameLoc, /*Diagnose*/false)) { + // Resolved to a type template name. + ParsedName = TemplateTy::make(Name); + TNK = TNK_Type_template; + } +} + +bool Sema::resolveAssumedTemplateNameAsType(Scope *S, TemplateName &Name, + SourceLocation NameLoc, + bool Diagnose) { + // We assumed this undeclared identifier to be an (ADL-only) function + // template name, but it was used in a context where a type was required. + // Try to typo-correct it now. + AssumedTemplateStorage *ATN = Name.getAsAssumedTemplateName(); + assert(ATN && "not an assumed template name"); + + LookupResult R(*this, ATN->getDeclName(), NameLoc, LookupOrdinaryName); + struct CandidateCallback : CorrectionCandidateCallback { + bool ValidateCandidate(const TypoCorrection &TC) override { + return TC.getCorrectionDecl() && + getAsTypeTemplateDecl(TC.getCorrectionDecl()); + } + std::unique_ptr<CorrectionCandidateCallback> clone() override { + return llvm::make_unique<CandidateCallback>(*this); + } + } FilterCCC; + + TypoCorrection Corrected = + CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), S, nullptr, + FilterCCC, CTK_ErrorRecovery); + if (Corrected && Corrected.getFoundDecl()) { + diagnoseTypo(Corrected, PDiag(diag::err_no_template_suggest) + << ATN->getDeclName()); + Name = TemplateName(Corrected.getCorrectionDeclAs<TemplateDecl>()); + return false; + } + + if (Diagnose) + Diag(R.getNameLoc(), diag::err_no_template) << R.getLookupName(); + return true; +} + +TypeResult Sema::ActOnTemplateIdType( + Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + TemplateTy TemplateD, IdentifierInfo *TemplateII, + SourceLocation TemplateIILoc, SourceLocation LAngleLoc, + ASTTemplateArgsPtr TemplateArgsIn, SourceLocation RAngleLoc, + bool IsCtorOrDtorName, bool IsClassName) { if (SS.isInvalid()) return true; @@ -3396,6 +3494,9 @@ Sema::ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc, } TemplateName Template = TemplateD.get(); + if (Template.getAsAssumedTemplateName() && + resolveAssumedTemplateNameAsType(S, Template, TemplateIILoc)) + return true; // Translate the parser's template argument list in our AST format. TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc); @@ -4141,7 +4242,6 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, // vs template<class T, class U> void f(U); // These should be filtered out by our callers. - assert(!R.empty() && "empty lookup results when building templateid"); assert(!R.isAmbiguous() && "ambiguous lookup when building templateid"); // Non-function templates require a template argument list. |