diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 15 | ||||
-rw-r--r-- | clang/lib/AST/ASTImporter.cpp | 8 | ||||
-rw-r--r-- | clang/lib/AST/ASTStructuralEquivalence.cpp | 6 | ||||
-rw-r--r-- | clang/lib/AST/ItaniumMangle.cpp | 2 | ||||
-rw-r--r-- | clang/lib/AST/ODRHash.cpp | 1 | ||||
-rw-r--r-- | clang/lib/AST/TemplateName.cpp | 16 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 97 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 26 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTemplate.cpp | 42 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 104 | ||||
-rw-r--r-- | clang/lib/Parse/Parser.cpp | 51 | ||||
-rw-r--r-- | clang/lib/Sema/SemaCXXScopeSpec.cpp | 25 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 88 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 32 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 10 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 134 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 5 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 6 |
21 files changed, 499 insertions, 181 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 63488853759..c144e01626e 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5226,6 +5226,11 @@ ASTContext::getNameForTemplate(TemplateName Name, return DeclarationNameInfo((*Storage->begin())->getDeclName(), NameLoc); } + case TemplateName::AssumedTemplate: { + AssumedTemplateStorage *Storage = Name.getAsAssumedTemplateName(); + return DeclarationNameInfo(Storage->getDeclName(), NameLoc); + } + case TemplateName::DependentTemplate: { DependentTemplateName *DTN = Name.getAsDependentTemplateName(); DeclarationName DName; @@ -5273,7 +5278,8 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name) const { } case TemplateName::OverloadedTemplate: - llvm_unreachable("cannot canonicalize overloaded template"); + case TemplateName::AssumedTemplate: + llvm_unreachable("cannot canonicalize unresolved template"); case TemplateName::DependentTemplate: { DependentTemplateName *DTN = Name.getAsDependentTemplateName(); @@ -7620,6 +7626,13 @@ ASTContext::getOverloadedTemplateName(UnresolvedSetIterator Begin, return TemplateName(OT); } +/// Retrieve a template name representing an unqualified-id that has been +/// assumed to name a template for ADL purposes. +TemplateName ASTContext::getAssumedTemplateName(DeclarationName Name) const { + auto *OT = new (*this) AssumedTemplateStorage(Name); + return TemplateName(OT); +} + /// Retrieve the template name that represents a qualified /// template name such as \c std::vector. TemplateName diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 7ce2abee6ce..d10db32e872 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8126,6 +8126,14 @@ Expected<TemplateName> ASTImporter::Import_New(TemplateName From) { ToTemplates.end()); } + case TemplateName::AssumedTemplate: { + AssumedTemplateStorage *FromStorage = From.getAsAssumedTemplateName(); + auto DeclNameOrErr = Import_New(FromStorage->getDeclName()); + if (!DeclNameOrErr) + return DeclNameOrErr.takeError(); + return ToContext.getAssumedTemplateName(*DeclNameOrErr); + } + case TemplateName::QualifiedTemplate: { QualifiedTemplateName *QTN = From.getAsQualifiedTemplateName(); auto QualifierOrErr = Import_New(QTN->getQualifier()); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index bf084e325cc..3eafa3d9508 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -180,6 +180,12 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return I1 == E1 && I2 == E2; } + case TemplateName::AssumedTemplate: { + AssumedTemplateStorage *TN1 = N1.getAsAssumedTemplateName(), + *TN2 = N1.getAsAssumedTemplateName(); + return TN1->getDeclName() == TN2->getDeclName(); + } + case TemplateName::QualifiedTemplate: { QualifiedTemplateName *QN1 = N1.getAsQualifiedTemplateName(), *QN2 = N2.getAsQualifiedTemplateName(); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 1dba7ccc90a..8bc19a34e6f 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1891,6 +1891,7 @@ void CXXNameMangler::mangleType(TemplateName TN) { break; case TemplateName::OverloadedTemplate: + case TemplateName::AssumedTemplate: llvm_unreachable("can't mangle an overloaded template name as a <type>"); case TemplateName::DependentTemplate: { @@ -2030,6 +2031,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, } case TemplateName::OverloadedTemplate: + case TemplateName::AssumedTemplate: case TemplateName::DependentTemplate: llvm_unreachable("invalid base for a template specialization type"); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 9d484bd5de5..ba3b2ee49c5 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -140,6 +140,7 @@ void ODRHash::AddTemplateName(TemplateName Name) { break; // TODO: Support these cases. case TemplateName::OverloadedTemplate: + case TemplateName::AssumedTemplate: case TemplateName::QualifiedTemplate: case TemplateName::DependentTemplate: case TemplateName::SubstTemplateTemplateParm: diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp index 2474d6278b2..06e1dcec744 100644 --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -66,6 +66,8 @@ TemplateName::TemplateName(void *Ptr) { TemplateName::TemplateName(TemplateDecl *Template) : Storage(Template) {} TemplateName::TemplateName(OverloadedTemplateStorage *Storage) : Storage(Storage) {} +TemplateName::TemplateName(AssumedTemplateStorage *Storage) + : Storage(Storage) {} TemplateName::TemplateName(SubstTemplateTemplateParmStorage *Storage) : Storage(Storage) {} TemplateName::TemplateName(SubstTemplateTemplateParmPackStorage *Storage) @@ -87,6 +89,8 @@ TemplateName::NameKind TemplateName::getKind() const { = Storage.get<UncommonTemplateNameStorage*>(); if (uncommon->getAsOverloadedStorage()) return OverloadedTemplate; + if (uncommon->getAsAssumedTemplateName()) + return AssumedTemplate; if (uncommon->getAsSubstTemplateTemplateParm()) return SubstTemplateTemplateParm; return SubstTemplateTemplateParmPack; @@ -113,6 +117,14 @@ OverloadedTemplateStorage *TemplateName::getAsOverloadedTemplate() const { return nullptr; } +AssumedTemplateStorage *TemplateName::getAsAssumedTemplateName() const { + if (UncommonTemplateNameStorage *Uncommon = + Storage.dyn_cast<UncommonTemplateNameStorage *>()) + return Uncommon->getAsAssumedTemplateName(); + + return nullptr; +} + SubstTemplateTemplateParmStorage * TemplateName::getAsSubstTemplateTemplateParm() const { if (UncommonTemplateNameStorage *uncommon = @@ -230,7 +242,9 @@ TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, } else if (SubstTemplateTemplateParmPackStorage *SubstPack = getAsSubstTemplateTemplateParmPack()) OS << *SubstPack->getParameterPack(); - else { + else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) { + Assumed->getDeclName().print(OS, Policy); + } else { OverloadedTemplateStorage *OTS = getAsOverloadedTemplate(); (*OTS->begin())->printName(OS); } 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; + } } } } diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 93912dae03b..c473856f0b0 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -888,7 +888,7 @@ bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS, bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - TemplateTy Template, + TemplateTy OpaqueTemplate, SourceLocation TemplateNameLoc, SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgsIn, @@ -898,11 +898,13 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, if (SS.isInvalid()) return true; + TemplateName Template = OpaqueTemplate.get(); + // Translate the parser's template argument list in our AST format. TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc); translateTemplateArguments(TemplateArgsIn, TemplateArgs); - DependentTemplateName *DTN = Template.get().getAsDependentTemplateName(); + DependentTemplateName *DTN = Template.getAsDependentTemplateName(); if (DTN && DTN->isIdentifier()) { // Handle a dependent template specialization for which we cannot resolve // the template name. @@ -930,23 +932,28 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, return false; } - TemplateDecl *TD = Template.get().getAsTemplateDecl(); - if (Template.get().getAsOverloadedTemplate() || DTN || + // If we assumed an undeclared identifier was a template name, try to + // typo-correct it now. + if (Template.getAsAssumedTemplateName() && + resolveAssumedTemplateNameAsType(S, Template, TemplateNameLoc)) + return true; + + TemplateDecl *TD = Template.getAsTemplateDecl(); + if (Template.getAsOverloadedTemplate() || DTN || isa<FunctionTemplateDecl>(TD) || isa<VarTemplateDecl>(TD)) { SourceRange R(TemplateNameLoc, RAngleLoc); if (SS.getRange().isValid()) R.setBegin(SS.getRange().getBegin()); Diag(CCLoc, diag::err_non_type_template_in_nested_name_specifier) - << (TD && isa<VarTemplateDecl>(TD)) << Template.get() << R; - NoteAllFoundTemplates(Template.get()); + << (TD && isa<VarTemplateDecl>(TD)) << Template << R; + NoteAllFoundTemplates(Template); return true; } // We were able to resolve the template name to an actual template. // Build an appropriate nested-name-specifier. - QualType T = - CheckTemplateIdType(Template.get(), TemplateNameLoc, TemplateArgs); + QualType T = CheckTemplateIdType(Template, TemplateNameLoc, TemplateArgs); if (T.isNull()) return true; @@ -954,7 +961,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, // nested name specifiers. if (!T->isDependentType() && !T->getAs<TagType>()) { Diag(TemplateNameLoc, diag::err_nested_name_spec_non_tag) << T; - NoteAllFoundTemplates(Template.get()); + NoteAllFoundTemplates(Template); return true; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 3ac5a3749d6..ea6675f15bd 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -917,6 +917,16 @@ Corrected: } } + if (getLangOpts().CPlusPlus2a && !SS.isSet() && NextToken.is(tok::less)) { + // In C++20 onwards, this could be an ADL-only call to a function + // template, and we're required to assume that this is a template name. + // + // FIXME: Find a way to still do typo correction in this case. + TemplateName Template = + Context.getAssumedTemplateName(NameInfo.getName()); + return NameClassification::UndeclaredTemplate(Template); + } + // In C, we first see whether there is a tag type by the same name, in // which case it's likely that the user just forgot to write "enum", // "struct", or "union". @@ -1045,52 +1055,62 @@ Corrected: if (getLangOpts().CPlusPlus && NextToken.is(tok::less) && (IsFilteredTemplateName || - hasAnyAcceptableTemplateNames(Result, /*AllowFunctionTemplates=*/true, - /*AllowDependent=*/false))) { + hasAnyAcceptableTemplateNames( + Result, /*AllowFunctionTemplates=*/true, + /*AllowDependent=*/false, + /*AllowNonTemplateFunctions*/ !SS.isSet() && + getLangOpts().CPlusPlus2a))) { // C++ [temp.names]p3: // After name lookup (3.4) finds that a name is a template-name or that // an operator-function-id or a literal- operator-id refers to a set of // overloaded functions any member of which is a function template if // this is followed by a <, the < is always taken as the delimiter of a // template-argument-list and never as the less-than operator. + // 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. if (!IsFilteredTemplateName) FilterAcceptableTemplateNames(Result); - if (!Result.empty()) { - bool IsFunctionTemplate; - bool IsVarTemplate; - TemplateName Template; - if (Result.end() - Result.begin() > 1) { - IsFunctionTemplate = true; - Template = Context.getOverloadedTemplateName(Result.begin(), - Result.end()); - } else { - auto *TD = cast<TemplateDecl>(getAsTemplateNameDecl( - *Result.begin(), /*AllowFunctionTemplates=*/true, - /*AllowDependent=*/false)); - IsFunctionTemplate = isa<FunctionTemplateDecl>(TD); - IsVarTemplate = isa<VarTemplateDecl>(TD); - - if (SS.isSet() && !SS.isInvalid()) - Template = - Context.getQualifiedTemplateName(SS.getScopeRep(), - /*TemplateKeyword=*/false, TD); - else - Template = TemplateName(TD); - } - - if (IsFunctionTemplate) { - // Function templates always go through overload resolution, at which - // point we'll perform the various checks (e.g., accessibility) we need - // to based on which function we selected. - Result.suppressDiagnostics(); + bool IsFunctionTemplate; + bool IsVarTemplate; + TemplateName Template; + if (Result.end() - Result.begin() > 1) { + IsFunctionTemplate = true; + Template = Context.getOverloadedTemplateName(Result.begin(), + Result.end()); + } else if (!Result.empty()) { + auto *TD = cast<TemplateDecl>(getAsTemplateNameDecl( + *Result.begin(), /*AllowFunctionTemplates=*/true, + /*AllowDependent=*/false)); + IsFunctionTemplate = isa<FunctionTemplateDecl>(TD); + IsVarTemplate = isa<VarTemplateDecl>(TD); + + if (SS.isSet() && !SS.isInvalid()) + Template = + Context.getQualifiedTemplateName(SS.getScopeRep(), + /*TemplateKeyword=*/false, TD); + else + Template = TemplateName(TD); + } else { + // All results were non-template functions. This is a function template + // name. + IsFunctionTemplate = true; + Template = Context.getAssumedTemplateName(NameInfo.getName()); + } - return NameClassification::FunctionTemplate(Template); - } + if (IsFunctionTemplate) { + // Function templates always go through overload resolution, at which + // point we'll perform the various checks (e.g., accessibility) we need + // to based on which function we selected. + Result.suppressDiagnostics(); - return IsVarTemplate ? NameClassification::VarTemplate(Template) - : NameClassification::TypeTemplate(Template); + return NameClassification::FunctionTemplate(Template); } + + return IsVarTemplate ? NameClassification::VarTemplate(Template) + : NameClassification::TypeTemplate(Template); } NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index a47c4049311..04192e3566c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -3881,6 +3881,8 @@ Sema::BuildMemInitializer(Decl *ConstructorD, if (TemplateTypeTy) { BaseType = GetTypeFromParser(TemplateTypeTy, &TInfo); + if (BaseType.isNull()) + return true; } else if (DS.getTypeSpecType() == TST_decltype) { BaseType = BuildDecltypeType(DS.getRepAsExpr(), DS.getTypeSpecTypeLoc()); } else if (DS.getTypeSpecType() == TST_decltype_auto) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f365d82047b..31d00dc2ed2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2065,11 +2065,12 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, // is in the wrong place to recover. Suggest the typo // correction, but don't make it a fix-it since we're not going // to recover well anyway. - AcceptableWithoutRecovery = - isa<TypeDecl>(UnderlyingND) || isa<ObjCInterfaceDecl>(UnderlyingND); + AcceptableWithoutRecovery = isa<TypeDecl>(UnderlyingND) || + getAsTypeTemplateDecl(UnderlyingND) || + isa<ObjCInterfaceDecl>(UnderlyingND); } else { - // FIXME: We found a keyword or a type. Suggest it, but don't provide a - // fix-it because we aren't able to recover. + // FIXME: We found a keyword. Suggest it, but don't provide a fix-it + // because we aren't able to recover. AcceptableWithoutRecovery = true; } @@ -2221,8 +2222,10 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // this becomes a performance hit, we can work harder to preserve those // results until we get here but it's likely not worth it. bool MemberOfUnknownSpecialization; + AssumedTemplateKind AssumedTemplate; if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, - MemberOfUnknownSpecialization, TemplateKWLoc)) + MemberOfUnknownSpecialization, TemplateKWLoc, + &AssumedTemplate)) return ExprError(); if (MemberOfUnknownSpecialization || @@ -5518,7 +5521,24 @@ tryImplicitlyCaptureThisIfImplicitMemberFunctionAccessWithDependentArgs( ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { - return BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + ExprResult Call = + BuildCallExpr(Scope, Fn, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + if (Call.isInvalid()) + return Call; + + // Diagnose uses of the C++20 "ADL-only template-id call" feature in earlier + // language modes. + if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(Fn)) { + if (ULE->hasExplicitTemplateArgs() && + ULE->decls_begin() == ULE->decls_end()) { + Diag(Fn->getExprLoc(), getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_adl_only_template_id + : diag::ext_adl_only_template_id) + << ULE->getName(); + } + } + + return Call; } /// BuildCallExpr - Handle a call to Fn with the specified array of arguments. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 881e0f6546d..6af59d5c431 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7074,7 +7074,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, TemplateIdAnnotation *TemplateId = SecondTypeName.TemplateId; ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); - TypeResult T = ActOnTemplateIdType(TemplateId->SS, + TypeResult T = ActOnTemplateIdType(S, + TemplateId->SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -7126,7 +7127,8 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, TemplateIdAnnotation *TemplateId = FirstTypeName.TemplateId; ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); - TypeResult T = ActOnTemplateIdType(TemplateId->SS, + TypeResult T = ActOnTemplateIdType(S, + TemplateId->SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 3fbd6356070..19ac2bf33aa 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -12017,7 +12017,8 @@ bool Sema::buildOverloadedCallSet(Scope *S, Expr *Fn, // We don't perform ADL for implicit declarations of builtins. // Verify that this was correctly set up. FunctionDecl *F; - if (ULE->decls_begin() + 1 == ULE->decls_end() && + if (ULE->decls_begin() != ULE->decls_end() && + ULE->decls_begin() + 1 == ULE->decls_end() && (F = dyn_cast<FunctionDecl>(*ULE->decls_begin())) && F->getBuiltinID() && F->isImplicit()) llvm_unreachable("performing ADL for builtin"); @@ -12201,10 +12202,9 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, OverloadingResult OverloadResult = CandidateSet.BestViableFunction(*this, Fn->getBeginLoc(), Best); - return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args, - RParenLoc, ExecConfig, &CandidateSet, - &Best, OverloadResult, - AllowTypoCorrection); + return FinishOverloadedCallExpr(*this, S, Fn, ULE, LParenLoc, Args, RParenLoc, + ExecConfig, &CandidateSet, &Best, + OverloadResult, AllowTypoCorrection); } static bool IsOverloaded(const UnresolvedSetImpl &Functions) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 58ad439747e..c58c446e52c 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/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. diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 64af0fa8a1d..a09507f6daa 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -8745,6 +8745,11 @@ ASTReader::ReadTemplateName(ModuleFile &F, const RecordData &Record, return Context.getOverloadedTemplateName(Decls.begin(), Decls.end()); } + case TemplateName::AssumedTemplate: { + DeclarationName Name = ReadDeclarationName(F, Record, Idx); + return Context.getAssumedTemplateName(Name); + } + case TemplateName::QualifiedTemplate: { NestedNameSpecifier *NNS = ReadNestedNameSpecifier(F, Record, Idx); bool hasTemplKeyword = Record[Idx++]; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index accd8ccdfaf..5012febd74f 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5877,6 +5877,12 @@ void ASTRecordWriter::AddTemplateName(TemplateName Name) { break; } + case TemplateName::AssumedTemplate: { + AssumedTemplateStorage *ADLT = Name.getAsAssumedTemplateName(); + AddDeclarationName(ADLT->getDeclName()); + break; + } + case TemplateName::QualifiedTemplate: { QualifiedTemplateName *QualT = Name.getAsQualifiedTemplateName(); AddNestedNameSpecifier(QualT->getQualifier()); |