diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a09157cad0ac..dcae9a7cd899 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1977,6 +1977,7 @@ public: TemplateName getOverloadedTemplateName(UnresolvedSetIterator Begin, UnresolvedSetIterator End) const; + TemplateName getAssumedTemplateName(DeclarationName Name) const; TemplateName getQualifiedTemplateName(NestedNameSpecifier *NNS, bool TemplateKeyword, diff --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h index e5d34399f8df..90449147637d 100644 --- a/clang/include/clang/AST/DeclarationName.h +++ b/clang/include/clang/AST/DeclarationName.h @@ -863,4 +863,24 @@ struct DenseMapInfo { } // namespace llvm +// The definition of AssumedTemplateStorage is factored out of TemplateName to +// resolve a cyclic dependency between it and DeclarationName (via Type). +namespace clang { + +/// A structure for storing the information associated with a name that has +/// been assumed to be a template name (despite finding no TemplateDecls). +class AssumedTemplateStorage : public UncommonTemplateNameStorage { + friend class ASTContext; + + AssumedTemplateStorage(DeclarationName Name) + : UncommonTemplateNameStorage(Assumed, 0), Name(Name) {} + DeclarationName Name; + +public: + /// Get the name of the template. + DeclarationName getDeclName() const { return Name; } +}; + +} // namespace clang + #endif // LLVM_CLANG_AST_DECLARATIONNAME_H diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index 5190557cbdba..4cc6f5e6ce82 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -31,6 +31,7 @@ class NamedDecl; class NestedNameSpecifier; enum OverloadedOperatorKind : int; class OverloadedTemplateStorage; +class AssumedTemplateStorage; class PartialDiagnostic; struct PrintingPolicy; class QualifiedTemplateName; @@ -46,6 +47,7 @@ class UncommonTemplateNameStorage { protected: enum Kind { Overloaded, + Assumed, // defined in DeclarationName.h SubstTemplateTemplateParm, SubstTemplateTemplateParmPack }; @@ -78,6 +80,12 @@ public: : nullptr; } + AssumedTemplateStorage *getAsAssumedTemplateName() { + return Bits.Kind == Assumed + ? reinterpret_cast(this) + : nullptr; + } + SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() { return Bits.Kind == SubstTemplateTemplateParm ? reinterpret_cast(this) @@ -194,6 +202,10 @@ public: /// A set of overloaded template declarations. OverloadedTemplate, + /// An unqualified-id that has been assumed to name a function template + /// that will be found by ADL. + AssumedTemplate, + /// A qualified template name, where the qualification is kept /// to describe the source code as written. QualifiedTemplate, @@ -215,6 +227,7 @@ public: TemplateName() = default; explicit TemplateName(TemplateDecl *Template); explicit TemplateName(OverloadedTemplateStorage *Storage); + explicit TemplateName(AssumedTemplateStorage *Storage); explicit TemplateName(SubstTemplateTemplateParmStorage *Storage); explicit TemplateName(SubstTemplateTemplateParmPackStorage *Storage); explicit TemplateName(QualifiedTemplateName *Qual); @@ -236,7 +249,7 @@ public: TemplateDecl *getAsTemplateDecl() const; /// Retrieve the underlying, overloaded function template - // declarations that this template name refers to, if known. + /// declarations that this template name refers to, if known. /// /// \returns The set of overloaded function templates that this template /// name refers to, if known. If the template name does not refer to a @@ -244,6 +257,10 @@ public: /// refers to a single template, returns NULL. OverloadedTemplateStorage *getAsOverloadedTemplate() const; + /// Retrieve information on a name that has been assumed to be a + /// template-name in order to permit a call via ADL. + AssumedTemplateStorage *getAsAssumedTemplateName() const; + /// Retrieve the substituted template template parameter, if /// known. /// diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 84069e4b4fcb..86b2e95471fe 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -744,7 +744,8 @@ def err_typename_refers_to_non_type_template : Error< def err_expected_type_name_after_typename : Error< "expected an identifier or template-id after '::'">; def err_explicit_spec_non_template : Error< - "explicit %select{specialization|instantiation}0 of non-template %1 %2">; + "explicit %select{specialization|instantiation}0 of " + "%select{non-|undeclared }3template %1 %2">; def err_default_template_template_parameter_not_template : Error< "default template argument for a template template parameter must be a class " diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ba376d5f1095..cf3fcf070c04 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3963,6 +3963,15 @@ def err_template_member_noparams : Error< def err_template_tag_noparams : Error< "extraneous 'template<>' in declaration of %0 %1">; +def warn_cxx17_compat_adl_only_template_id : Warning< + "use of function template name with no prior function template " + "declaration in function call with explicit template arguments " + "is incompatible with C++ standards before C++2a">, + InGroup, DefaultIgnore; +def ext_adl_only_template_id : ExtWarn< + "use of function template name with no prior declaration in function call " + "with explicit template arguments is a C++2a extension">, InGroup; + // C++ Template Argument Lists def err_template_missing_args : Error< "use of " diff --git a/clang/include/clang/Basic/TemplateKinds.h b/clang/include/clang/Basic/TemplateKinds.h index cfed09f2d18c..ee7e2f52e43a 100644 --- a/clang/include/clang/Basic/TemplateKinds.h +++ b/clang/include/clang/Basic/TemplateKinds.h @@ -21,7 +21,8 @@ enum TemplateNameKind { /// The name does not refer to a template. TNK_Non_template = 0, /// The name refers to a function template or a set of overloaded - /// functions that includes at least one function template. + /// functions that includes at least one function template, or (in C++20) + /// refers to a set of non-template functions but is followed by a '<'. TNK_Function_template, /// The name refers to a template whose specialization produces a /// type. The template itself could be a class template, template @@ -42,7 +43,11 @@ enum TemplateNameKind { /// whether the template name is assumed to refer to a type template or a /// function template depends on the context in which the template /// name occurs. - TNK_Dependent_template_name + TNK_Dependent_template_name, + /// Lookup for the name failed, but we're assuming it was a template name + /// anyway. In C++20, this is mandatory in order to parse ADL-only function + /// template specialization calls. + TNK_Undeclared_template, }; } diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 38b6f55554fc..85c1033a6e5d 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2303,13 +2303,18 @@ private: /// Doesn't consume tokens. TPResult isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False, - bool *HasMissingTypename = nullptr); + bool *InvalidAsDeclSpec = nullptr); /// Given that isCXXDeclarationSpecifier returns \c TPResult::True or /// \c TPResult::Ambiguous, determine whether the decl-specifier would be /// a type-specifier other than a cv-qualifier. bool isCXXDeclarationSpecifierAType(); + /// Determine whether the current token sequence might be + /// '<' template-argument-list '>' + /// rather than a less-than expression. + TPResult isTemplateArgumentList(unsigned TokensToSkip); + /// Determine whether an identifier has been tentatively declared as a /// non-type. Such tentative declarations should not be found to name a type /// during a tentative parse, but also should not be annotated as a non-type. @@ -2994,7 +2999,6 @@ private: UnqualifiedId &TemplateName, bool AllowTypeAnnotation = true); void AnnotateTemplateIdTokenAsType(bool IsClassName = false); - bool IsTemplateArgumentList(unsigned Skip = 0); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); ParsedTemplateArgument ParseTemplateTemplateArgument(); ParsedTemplateArgument ParseTemplateArgument(); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 5f29fb82443b..0988649689be 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1823,7 +1823,8 @@ public: NC_NestedNameSpecifier, NC_TypeTemplate, NC_VarTemplate, - NC_FunctionTemplate + NC_FunctionTemplate, + NC_UndeclaredTemplate, }; class NameClassification { @@ -1871,6 +1872,12 @@ public: return Result; } + static NameClassification UndeclaredTemplate(TemplateName Name) { + NameClassification Result(NC_UndeclaredTemplate); + Result.Template = Name; + return Result; + } + NameClassificationKind getKind() const { return Kind; } ParsedType getType() const { @@ -1885,7 +1892,7 @@ public: TemplateName getTemplateName() const { assert(Kind == NC_TypeTemplate || Kind == NC_FunctionTemplate || - Kind == NC_VarTemplate); + Kind == NC_VarTemplate || Kind == NC_UndeclaredTemplate); return Template; } @@ -1897,6 +1904,8 @@ public: return TNK_Function_template; case NC_VarTemplate: return TNK_Var_template; + case NC_UndeclaredTemplate: + return TNK_Undeclared_template; default: llvm_unreachable("unsupported name classification."); } @@ -6256,7 +6265,8 @@ public: bool AllowDependent = true); bool hasAnyAcceptableTemplateNames(LookupResult &R, bool AllowFunctionTemplates = true, - bool AllowDependent = true); + bool AllowDependent = true, + bool AllowNonTemplateFunctions = false); /// Try to interpret the lookup result D as a template-name. /// /// \param D A declaration found by name lookup. @@ -6268,10 +6278,20 @@ public: bool AllowFunctionTemplates = true, bool AllowDependent = true); + enum class AssumedTemplateKind { + /// This is not assumed to be a template name. + None, + /// This is assumed to be a template name because lookup found nothing. + FoundNothing, + /// This is assumed to be a template name because lookup found one or more + /// functions (but no function templates). + FoundFunctions, + }; bool LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, QualType ObjectType, bool EnteringContext, bool &MemberOfUnknownSpecialization, - SourceLocation TemplateKWLoc = SourceLocation()); + SourceLocation TemplateKWLoc = SourceLocation(), + AssumedTemplateKind *ATK = nullptr); TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, @@ -6282,6 +6302,20 @@ public: TemplateTy &Template, bool &MemberOfUnknownSpecialization); + /// Try to resolve an undeclared template name as a type template. + /// + /// Sets II to the identifier corresponding to the template name, and updates + /// Name to a corresponding (typo-corrected) type template name and TNK to + /// the corresponding kind, if possible. + void ActOnUndeclaredTypeTemplateName(Scope *S, TemplateTy &Name, + TemplateNameKind &TNK, + SourceLocation NameLoc, + IdentifierInfo *&II); + + bool resolveAssumedTemplateNameAsType(Scope *S, TemplateName &Name, + SourceLocation NameLoc, + bool Diagnose = true); + /// Determine whether a particular identifier might be the name in a C++1z /// deduction-guide declaration. bool isDeductionGuideName(Scope *S, const IdentifierInfo &Name, @@ -6391,14 +6425,11 @@ public: TemplateArgumentListInfo &TemplateArgs); TypeResult - ActOnTemplateIdType(CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + ActOnTemplateIdType(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, TemplateTy Template, IdentifierInfo *TemplateII, - SourceLocation TemplateIILoc, - SourceLocation LAngleLoc, - ASTTemplateArgsPtr TemplateArgs, - SourceLocation RAngleLoc, - bool IsCtorOrDtorName = false, - bool IsClassName = false); + SourceLocation TemplateIILoc, SourceLocation LAngleLoc, + ASTTemplateArgsPtr TemplateArgs, SourceLocation RAngleLoc, + bool IsCtorOrDtorName = false, bool IsClassName = false); /// Parsed an elaborated-type-specifier that refers to a template-id, /// such as \c class T::template apply. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 63488853759c..c144e01626eb 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 7ce2abee6ce4..d10db32e8725 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8126,6 +8126,14 @@ Expected 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 bf084e325cc9..3eafa3d9508f 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 1dba7ccc90af..8bc19a34e6fe 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 "); 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 9d484bd5de54..ba3b2ee49c54 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 2474d6278b21..06e1dcec7449 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(); 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()) + 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 f5c404f7e622..9c9a9f467982 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 c4fe23c60c30..e88488416352 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(TemplateInfo).Kind = + ParsedTemplateInfo::NonTemplate; + } + } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation) { + // Pretend this is just a forward declaration. + TemplateParams = nullptr; + const_cast(TemplateInfo).Kind = + ParsedTemplateInfo::NonTemplate; + const_cast(TemplateInfo).TemplateLoc = + SourceLocation(); + const_cast(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(TemplateInfo).Kind - = ParsedTemplateInfo::NonTemplate; - } - } else if (TemplateInfo.Kind - == ParsedTemplateInfo::ExplicitInstantiation) { - // Pretend this is just a forward declaration. - TemplateParams = nullptr; - const_cast(TemplateInfo).Kind - = ParsedTemplateInfo::NonTemplate; - const_cast(TemplateInfo).TemplateLoc - = SourceLocation(); - const_cast(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(TemplateId->Kind) << Range; + << TemplateId->Name << static_cast(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 fbafb436880a..c6f457fb1498 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, 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(), 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 d028c8f4c39e..b53e6ea09bb5 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 0504f0ddcbe2..2d2705d84711 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 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 6b25d6c038be..0c8e203caf40 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -315,6 +315,14 @@ bool Parser::SkipUntil(ArrayRef 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 93912dae03bc..c473856f0b07 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(TD) || isa(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(TD)) << Template.get() << R; - NoteAllFoundTemplates(Template.get()); + << (TD && isa(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()) { 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 3ac5a3749d63..ea6675f15bda 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(getAsTemplateNameDecl( - *Result.begin(), /*AllowFunctionTemplates=*/true, - /*AllowDependent=*/false)); - IsFunctionTemplate = isa(TD); - IsVarTemplate = isa(TD); + 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(getAsTemplateNameDecl( + *Result.begin(), /*AllowFunctionTemplates=*/true, + /*AllowDependent=*/false)); + IsFunctionTemplate = isa(TD); + IsVarTemplate = isa(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(); - - return NameClassification::FunctionTemplate(Template); - } - - return IsVarTemplate ? NameClassification::VarTemplate(Template) - : NameClassification::TypeTemplate(Template); + 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()); } + + 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 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 a47c40493117..04192e3566cf 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 f365d82047b7..31d00dc2ed2a 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(UnderlyingND) || isa(UnderlyingND); + AcceptableWithoutRecovery = isa(UnderlyingND) || + getAsTypeTemplateDecl(UnderlyingND) || + isa(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(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 881e0f6546dc..6af59d5c4312 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 3fbd63560706..19ac2bf33aa9 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(*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 58ad439747e1..c58c446e52c6 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((*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. diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 64af0fa8a1d1..a09507f6daa3 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 accd8ccdfaf9..5012febd74f5 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()); diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp index abcc6eee94a7..0138b88578f6 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.unqual/p3.cpp @@ -1,25 +1,67 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// expected-no-diagnostics +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++2a -fsyntax-only -verify %s -typedef int f; +typedef int fn; namespace N0 { - struct A { - friend void f(); + struct A { + friend void fn(); void g() { - int i = f(1); + int i = fn(1); } }; } namespace N1 { - struct A { - friend void f(A &); + struct A { + friend void fn(A &); operator int(); void g(A a) { - // ADL should not apply to the lookup of 'f', it refers to the typedef + // ADL should not apply to the lookup of 'fn', it refers to the typedef // above. - int i = f(a); + int i = fn(a); } }; } + +namespace std_example { + int h; // expected-note {{non-template declaration}} + void g(); +#if __cplusplus <= 201703L + // expected-note@-2 {{non-template declaration}} +#endif + namespace N { + struct A {}; + template int f(T); + template int g(T); +#if __cplusplus <= 201703L + // expected-note@-2 {{here}} +#endif + template int h(T); // expected-note {{here}} + } + + int x = f(N::A()); +#if __cplusplus <= 201703L + // expected-warning@-2 {{C++2a extension}} +#endif + int y = g(N::A()); +#if __cplusplus <= 201703L + // expected-error@-2 {{'g' does not name a template but is followed by template arguments; did you mean 'N::g'?}} +#endif + int z = h(N::A()); // expected-error {{'h' does not name a template but is followed by template arguments; did you mean 'N::h'?}} +} + +namespace AnnexD_example { + struct A {}; + void operator<(void (*fp)(), A); + void f() {} + int main() { + A a; + f < a; +#if __cplusplus > 201703L + // expected-error@-2 {{expected '>'}} +#endif + (f) < a; + } +} diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp index b69c014b587f..c7ffa77d30de 100644 --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // PR13819 -- __SIZE_TYPE__ is incompatible. typedef __SIZE_TYPE__ size_t; // expected-error 0-1 {{extension}} @@ -434,7 +435,7 @@ namespace dr239 { // dr239: yes namespace dr241 { // dr241: yes namespace A { struct B {}; - template void f(); // expected-note 2{{candidate}} + template void f(); // expected-note 3{{candidate}} template void g(B); } namespace C { @@ -442,8 +443,8 @@ namespace dr241 { // dr241: yes template void g(T t); // expected-note {{candidate}} } void h(A::B b) { - f<3>(b); // expected-error {{undeclared identifier}} - g<3>(b); // expected-error {{undeclared identifier}} + f<3>(b); // expected-error 0-1{{C++2a extension}} expected-error {{no matching}} + g<3>(b); // expected-error 0-1{{C++2a extension}} A::f<3>(b); // expected-error {{no matching}} A::g<3>(b); C::f<3>(b); // expected-error {{no matching}} diff --git a/clang/test/CXX/drs/dr6xx.cpp b/clang/test/CXX/drs/dr6xx.cpp index b4247b22607d..318096c29969 100644 --- a/clang/test/CXX/drs/dr6xx.cpp +++ b/clang/test/CXX/drs/dr6xx.cpp @@ -1048,10 +1048,13 @@ namespace dr686 { // dr686: yes template struct Y; // expected-error {{cannot be defined in a type specifier}} } -namespace dr687 { // dr687 still open +namespace dr687 { // dr687 (9 c++20, but the issue is still considered open) template void f(T a) { - // FIXME: This is valid in C++20. - g(a); // expected-error {{undeclared}} expected-error {{'('}} + // This is valid in C++20. + g(a); +#if __cplusplus <= 201703L + // expected-error@-2 {{C++2a extension}} +#endif // This is not. template g(a); // expected-error {{expected expression}} diff --git a/clang/test/CXX/temp/temp.spec/temp.explicit/p5.cpp b/clang/test/CXX/temp/temp.spec/temp.explicit/p5.cpp index ca1f9a391edd..5f0c4547cf26 100644 --- a/clang/test/CXX/temp/temp.spec/temp.explicit/p5.cpp +++ b/clang/test/CXX/temp/temp.spec/temp.explicit/p5.cpp @@ -8,7 +8,10 @@ namespace N { }; } -template class Z; // expected-error{{explicit instantiation of non-template class 'Z'}} +template class Z; // expected-error{{explicit instantiation of undeclared template class 'Z'}} + +struct Q; +template class Q; // expected-error{{explicit instantiation of non-template class 'Q'}} // FIXME: This example from the standard is wrong; note posted to CWG reflector // on 10/27/2009 diff --git a/clang/test/FixIt/typo-crash.cpp b/clang/test/FixIt/typo-crash.cpp index 4ea63c542a89..5130e3e0e6df 100644 --- a/clang/test/FixIt/typo-crash.cpp +++ b/clang/test/FixIt/typo-crash.cpp @@ -1,14 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// FIXME: The diagnostics and recovery here are very, very poor. - // PR10355 -template void template_id1() { // expected-note {{'template_id1' declared here}} \ - // expected-note {{possible target for call}} - template_id2<> t; // expected-error {{no template named 'template_id2'; did you mean 'template_id1'?}} \ - // expected-error {{expected ';' after expression}} \ - // expected-error {{reference to overloaded function could not be resolved; did you mean to call it?}} \ - // expected-error {{use of undeclared identifier 't'}} +template void template_id1() { + template_id2<> t; // expected-error-re {{no template named 'template_id2'{{$}}}} } // FIXME: It would be nice if we could get this correction right. diff --git a/clang/test/Misc/diag-template-diffing.cpp b/clang/test/Misc/diag-template-diffing.cpp index 5c85df434df1..7d3e21533fcf 100644 --- a/clang/test/Misc/diag-template-diffing.cpp +++ b/clang/test/Misc/diag-template-diffing.cpp @@ -394,7 +394,7 @@ template class class_ptrs {}; void set13(class_ptrs<&a13, &b13>) {} void test13() { set13(class_ptrs<&c13>()); - set13(class_ptrss<&a13, &b13, &d13>()); + set13(class_ptrs<&a13, &b13, &d13>()); } // CHECK-ELIDE-NOTREE: no matching function for call to 'set13' // CHECK-ELIDE-NOTREE: candidate function not viable: no known conversion from 'class_ptrs<&c13, (no argument)>' to 'class_ptrs<&a13, &b13>' for 1st argument diff --git a/clang/test/Modules/module-private.cpp b/clang/test/Modules/module-private.cpp index 42ab185760bb..30957865d1cd 100644 --- a/clang/test/Modules/module-private.cpp +++ b/clang/test/Modules/module-private.cpp @@ -18,9 +18,7 @@ int test_broken() { int *ip = 0; f1(ip); // expected-error{{use of undeclared identifier 'f1'}} - vector vec; // expected-error{{use of undeclared identifier 'vector'}} \ - // expected-error{{expected '(' for function-style cast or type construction}} \ - // expected-error{{use of undeclared identifier 'vec'}} + vector vec; // expected-error{{no template named 'vector'}} VisibleStruct vs; vs.field = 0; // expected-error{{no member named 'field' in 'VisibleStruct'}} diff --git a/clang/test/Modules/submodules-merge-defs.cpp b/clang/test/Modules/submodules-merge-defs.cpp index 795150208c95..0f7637de5732 100644 --- a/clang/test/Modules/submodules-merge-defs.cpp +++ b/clang/test/Modules/submodules-merge-defs.cpp @@ -29,7 +29,7 @@ B::Inner2 pre_bi; // expected-error +{{must be imported}} // expected-note@defs.h:4 +{{here}} // expected-note@defs.h:17 +{{here}} void pre_bfi(B b) { // expected-error +{{must be imported}} - b.f(); // expected-error +{{}} + b.f(); } C_Base<1> pre_cb1; // expected-error +{{must be imported}} @@ -48,7 +48,7 @@ int pre_e = E(0); // expected-error {{must be imported}} // expected-note@defs.h:32 +{{here}} int pre_ff = F().f(); // expected-error +{{must be imported}} -int pre_fg = F().g(); // expected-error +{{must be imported}} expected-error 2{{expected}} +int pre_fg = F().g(); // expected-error +{{must be imported}} // expected-note@defs.h:34 +{{here}} G::A pre_ga // expected-error +{{must be imported}} diff --git a/clang/test/Parser/cxx-ambig-init-templ.cpp b/clang/test/Parser/cxx-ambig-init-templ.cpp index 89ed5e570bff..a64760f3f447 100644 --- a/clang/test/Parser/cxx-ambig-init-templ.cpp +++ b/clang/test/Parser/cxx-ambig-init-templ.cpp @@ -31,7 +31,7 @@ struct S { void f2a( // T3 here is a parameter type, so must be declared before it is used. - int k1 = c < b, T3 < int > x = 0 // expected-error {{unexpected end of default argument expression}} + int k1 = c < b, T3 < int > x = 0 // expected-error {{no template named 'T3'}} ); template struct T3 { T3(int); operator int(); }; diff --git a/clang/test/Parser/cxx-template-argument.cpp b/clang/test/Parser/cxx-template-argument.cpp index 963356eaaba3..b3a9071785b5 100644 --- a/clang/test/Parser/cxx-template-argument.cpp +++ b/clang/test/Parser/cxx-template-argument.cpp @@ -60,33 +60,32 @@ namespace pr16225add { template struct ABC2 {}; template struct foo : - UnknownBase // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase // expected-error {{no template named 'UnknownBase'}} { }; template struct foo2 : - UnknownBase, // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase, // expected-error {{no template named 'UnknownBase'}} Known // expected-error {{too few template arguments for class template 'Known'}} { }; template struct foo3 : - UnknownBase > // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase > // expected-error {{no template named 'UnknownBase'}} { }; template struct foo4 : - UnknownBase >, // expected-error {{unknown template name 'UnknownBase'}} \ - // expected-error {{too few template arguments for class template 'ABC'}} + UnknownBase >, // expected-error {{too few template arguments for class template 'ABC'}} Known // expected-error {{too few template arguments for class template 'Known'}} { }; template struct foo5 : - UnknownBase> // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase> // expected-error {{no template named 'UnknownBase'}} #if __cplusplus <= 199711L // expected-error@-2 {{use '> >'}} #endif { }; template struct foo6 : - UnknownBase>, // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase>, // expected-error {{no template named 'UnknownBase'}} #if __cplusplus <= 199711L // expected-error@-2 {{use '> >'}} #endif @@ -94,32 +93,32 @@ namespace pr16225add { { }; template struct foo7 : - UnknownBase1)> // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase1)> // expected-error {{no template named 'UnknownBase'}} { }; template struct foo8 : - UnknownBase,X> // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase,X> // expected-error {{no template named 'UnknownBase'}} #if __cplusplus <= 199711L // expected-error@-2 {{use '> >'}} #endif { }; template struct foo9 : - UnknownBase,X> // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase,X> // expected-error {{no template named 'UnknownBase'}} #if __cplusplus <= 199711L // expected-error@-2 {{use '> >'}} #endif { }; template struct foo10 : - UnknownBase,X>> // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase,X>> // expected-error {{no template named 'UnknownBase'}} #if __cplusplus <= 199711L // expected-error@-2 {{use '> >'}} #endif { }; template struct foo11 : - UnknownBase<2 // expected-error {{unknown template name 'UnknownBase'}} + UnknownBase<2 // expected-error {{no template named 'UnknownBase'}} { }; } diff --git a/clang/test/Parser/cxx-template-decl.cpp b/clang/test/Parser/cxx-template-decl.cpp index 9ad422e7ab71..64c485fc0ec5 100644 --- a/clang/test/Parser/cxx-template-decl.cpp +++ b/clang/test/Parser/cxx-template-decl.cpp @@ -128,8 +128,13 @@ void f2() { // PR3844 -template <> struct S { }; // expected-error{{explicit specialization of non-template struct 'S'}} -template <> union U { }; // expected-error{{explicit specialization of non-template union 'U'}} +template <> struct S { }; // expected-error{{explicit specialization of undeclared template struct 'S'}} +template <> union U { }; // expected-error{{explicit specialization of undeclared template union 'U'}} + +struct SS; +union UU; +template <> struct SS { }; // expected-error{{explicit specialization of non-template struct 'SS'}} +template <> union UU { }; // expected-error{{explicit specialization of non-template union 'UU'}} namespace PR6184 { namespace N { @@ -230,11 +235,11 @@ struct base { }; struct t1 : base using handler_t = void (*)(T); extern handler_t ignore; extern void (*ignore)(int); - // FIXME: we recover as if cell is an undeclared variable. the diagnostics are terrible! - template using cell = pair*>; // expected-error {{use of undeclared identifier 'cell'}} \ - expected-error {{'T' does not refer to a value}} \ - expected-note {{declared here}} \ - expected-error {{expected ';' after alias declaration}} + // FIXME: we recover as if cell is an undeclared variable template + template using cell = pair*>; // expected-error {{use of undeclared identifier 'cell'}} expected-error {{expected expression}} } namespace Access { diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp index a3593689b5fb..b571370911db 100644 --- a/clang/test/SemaCXX/class.cpp +++ b/clang/test/SemaCXX/class.cpp @@ -138,7 +138,7 @@ struct S // Don't crash on this bogus code. namespace pr6629 { template struct foo : - bogus > // expected-error {{unknown template name 'bogus'}} + bogus > // expected-error {{no template named 'bogus'}} { }; template<> struct foo { // expected-error {{undeclared identifier 'unknown'}} diff --git a/clang/test/SemaCXX/cxx2a-adl-only-template-id.cpp b/clang/test/SemaCXX/cxx2a-adl-only-template-id.cpp new file mode 100644 index 000000000000..4bd9a22f5f44 --- /dev/null +++ b/clang/test/SemaCXX/cxx2a-adl-only-template-id.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +namespace N { + struct Q {}; + template int f(Q); + template int f(Q); + template int g(Q); + template int g(Q); + + template int some_long_name(Q); // expected-note {{here}} +} +N::Q q; +int g(); + +int h(); +template int h(...); +int h(int); + +// OK, these find the above functions by ADL. +int a = f(q); +int b(f(q)); +int c(f<0>(q)); +int d = g(q); + +int e = h<0>(q); // ok, found by unqualified lookup + +void fn() { + f<0>(q); + int f; + f<0>(q); // expected-error {{invalid operands to binary expression}} +} + +void disambig() { + // FIXME: It's unclear whether ending the template argument at the > inside the ?: is correct here (see DR579). + f 2 : 3>(q); // expected-error {{expected ':'}} expected-note {{to match}} expected-error {{expected expression}} + + f < 1 + 3 > (q); // ok, function call +} + +bool typo(int something) { // expected-note 4{{declared here}} + // FIXME: We shouldn't suggest the N:: for an ADL call if the candidate can be found by ADL. + some_logn_name<3>(q); // expected-error {{did you mean 'N::some_long_name'?}} + somethign < 3 ? h() > 4 : h(0); // expected-error {{did you mean 'something'}} + // This is parsed as a comparison on the left of a ?: expression. + somethign < 3 ? h() + 4 : h(0); // expected-error {{did you mean 'something'}} + // This is parsed as an ADL-only template-id call. + somethign < 3 ? h() + 4 : h(0) >(0); // expected-error {{undeclared identifier 'somethign'}} + bool k(somethign < 3); // expected-error {{did you mean 'something'}} + return somethign < 3; // expected-error {{did you mean 'something'}} +} + +// Ensure that treating undeclared identifiers as template names doesn't cause +// problems. +struct W {}; // expected-error {{undeclared template struct 'W'}} +X::Y xy; // expected-error {{no template named 'X'}} +void xf(X x); // expected-error {{no template named 'X'}} +struct A : X { // expected-error {{no template named 'X'}} + A() : X() {} // expected-error {{no template named 'X'}} +}; + +// Similarly for treating overload sets of functions as template names. +struct g {}; // expected-error {{'g' refers to a function template}} +g::Y xy; // expected-error {{no template named 'g'}} FIXME lies +void xf(g x); // expected-error {{variable has incomplete type 'void'}} expected-error 1+{{}} expected-note {{}} +struct B : g { // expected-error {{expected class name}} + B() : g() {} // expected-error {{expected class member or base class name}} +}; diff --git a/clang/test/SemaCXX/destructor.cpp b/clang/test/SemaCXX/destructor.cpp index 00d2fc552978..2859953a0280 100644 --- a/clang/test/SemaCXX/destructor.cpp +++ b/clang/test/SemaCXX/destructor.cpp @@ -99,7 +99,7 @@ struct Y { namespace PR6421 { class T; // expected-note{{forward declaration}} - class QGenericArgument // expected-note{{declared here}} + class QGenericArgument { template void foo(T t) // expected-error{{variable has incomplete type}} @@ -108,8 +108,7 @@ namespace PR6421 { void disconnect() { T* t; - bob(t); // expected-error{{undeclared identifier 'bob'}} \ - // expected-error{{does not refer to a value}} + bob(t); // expected-error{{undeclared identifier 'bob'}} } }; } diff --git a/clang/test/SemaCXX/invalid-member-expr.cpp b/clang/test/SemaCXX/invalid-member-expr.cpp index fd50d328da67..920d92380e69 100644 --- a/clang/test/SemaCXX/invalid-member-expr.cpp +++ b/clang/test/SemaCXX/invalid-member-expr.cpp @@ -53,7 +53,10 @@ namespace test3 { namespace rdar11293995 { struct Length { - explicit Length(PassRefPtr); // expected-error {{no template named 'PassRefPtr}} expected-error {{undeclared identifier 'CalculationValue'}} + // FIXME: We try to annotate the template-id here during tentative parsing, + // and fail, then try again during the actual parse. This results in the same + // diagnostic being produced twice. :( + explicit Length(PassRefPtr); // expected-error 2{{undeclared identifier 'CalculationValue'}} }; struct LengthSize { diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index ddd43c08091c..33dea4d36b3e 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -438,7 +438,7 @@ namespace PR17394 { long zzzzzzzzzz; }; class B : private A {}; - B zzzzzzzzzy<>; // expected-error {{expected ';' after top level declarator}}{} + B zzzzzzzzzy<>; // expected-error {{template specialization requires 'template<>'}} expected-error {{no variable template matches specialization}} } namespace correct_fields_in_member_funcs { diff --git a/clang/test/SemaTemplate/dependent-base-classes.cpp b/clang/test/SemaTemplate/dependent-base-classes.cpp index 6d342f984d53..bb4f3ca1f17c 100644 --- a/clang/test/SemaTemplate/dependent-base-classes.cpp +++ b/clang/test/SemaTemplate/dependent-base-classes.cpp @@ -9,7 +9,7 @@ template struct X1 : T::apply { }; // expected-error{{use 'template' keyword to treat 'apply' as a dependent template name}} template -struct X2 : vector { }; // expected-error{{unknown template name 'vector'}} +struct X2 : vector { }; // expected-error{{no template named 'vector'}} namespace PR6031 { template diff --git a/clang/test/SemaTemplate/dependent-template-recover.cpp b/clang/test/SemaTemplate/dependent-template-recover.cpp index 37a8faa705f7..90ba3cd8999e 100644 --- a/clang/test/SemaTemplate/dependent-template-recover.cpp +++ b/clang/test/SemaTemplate/dependent-template-recover.cpp @@ -7,7 +7,7 @@ struct X { t->operator+(1); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}} t->f1(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}} - t->f1<3, int const>(1); // expected-error{{missing 'template' keyword prior to dependent template name 'f1'}} + t->f1<3, int const>(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}} T::getAs(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}} t->T::getAs(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}} @@ -15,7 +15,7 @@ struct X { (*t).f2(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} (*t).f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} T::f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} - T::f2<0, int>(0); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} + T::f2<0, int>(0); // expected-error{{use 'template' keyword to treat 'f2' as a dependent template name}} T::foo= 4>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}} @@ -24,7 +24,7 @@ struct X { T::foo(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}} T::foo < T::bar<1>(); // expected-error{{missing 'template' keyword prior to dependent template name 'bar'}} - // Prefer to diagonse a missing 'template' keyword rather than finding a non-template name. + // Prefer to diagnose a missing 'template' keyword rather than finding a non-template name. xyz < T::foo < 1 > (); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}} T::foo < xyz < 1 > (); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}} @@ -83,12 +83,12 @@ template void f(T t) { T::g(0); // ... but this one must be a template-id. - T::g(0); // expected-error {{missing 'template' keyword prior to dependent template name 'g'}} + T::g(0); // expected-error {{use 'template' keyword to treat 'g' as a dependent template name}} expected-error {{no matching function}} } struct Y { template void f(int); - template static void g(int); // expected-warning 0-1{{extension}} + template static void g(int); // expected-warning 0-1{{extension}} expected-note {{candidate}} }; void q() { void (*p)(int) = Y::g; } template void f<0>(Y); // expected-note {{in instantiation of}} diff --git a/clang/test/SemaTemplate/rdar9173693.cpp b/clang/test/SemaTemplate/rdar9173693.cpp index 86b49545a308..ed09a64a0ddd 100644 --- a/clang/test/SemaTemplate/rdar9173693.cpp +++ b/clang/test/SemaTemplate/rdar9173693.cpp @@ -2,5 +2,8 @@ // template< bool C > struct assert { }; -template< bool > struct assert_arg_pred_impl { }; // expected-note 3 {{declared here}} -template< typename Pred > assert assert_not_arg( void (*)(Pred), typename assert_arg_pred::type ); // expected-error 5 {{}} +// FIXME: We diagnose the same problem multiple times here because we have no +// way to indicate in the token stream that we already tried to annotate a +// template-id and we failed. +template< bool > struct assert_arg_pred_impl { }; // expected-note 4 {{declared here}} +template< typename Pred > assert assert_not_arg( void (*)(Pred), typename assert_arg_pred::type ); // expected-error 6 {{}} diff --git a/clang/test/SemaTemplate/recovery-crash.cpp b/clang/test/SemaTemplate/recovery-crash.cpp index c8e783f47b45..ec44747ce5a0 100644 --- a/clang/test/SemaTemplate/recovery-crash.cpp +++ b/clang/test/SemaTemplate/recovery-crash.cpp @@ -28,12 +28,12 @@ namespace PR16134 { namespace PR16225 { template void f(); template void g(C*) { - struct LocalStruct : UnknownBase { }; // expected-error {{unknown template name 'UnknownBase'}} \ - // expected-error {{use of undeclared identifier 'Mumble'}} + struct LocalStruct : UnknownBase { }; // expected-error {{use of undeclared identifier 'Mumble'}} f(); #if __cplusplus <= 199711L // expected-warning@-2 {{template argument uses local type 'LocalStruct'}} #endif + struct LocalStruct2 : UnknownBase { }; // expected-error {{no template named 'UnknownBase'}} } struct S; void h() { diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 3ed6542808e3..715618c3c0f0 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1430,6 +1430,10 @@ bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) { return false; + case TemplateName::AssumedTemplate: + // FIXME: Visit DeclarationName? + return false; + case TemplateName::DependentTemplate: // FIXME: Visit nested-name-specifier. return false; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index dd6399da6c3c..052ba0b2c678 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -893,7 +893,7 @@ as the draft C++2a standard evolves. ADL and function templates that are not visible P0846R0 - No + SVN const mismatch with defaulted copy constructor