From fb98e7c13da639f34f34830a4e675367e0816cb6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 9 May 2019 03:31:27 +0000 Subject: [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 --- lib/Sema/SemaTemplate.cpp | 134 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 17 deletions(-) (limited to 'lib/Sema/SemaTemplate.cpp') 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((*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(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 clone() override { + return llvm::make_unique(*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()); + 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 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. -- cgit v1.2.1