diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index 9a525efb938e8d..e605f82e91fe41 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -187,6 +187,7 @@ class DumpVisitor : public RecursiveASTVisitor { TEMPLATE_KIND(SubstTemplateTemplateParm); TEMPLATE_KIND(SubstTemplateTemplateParmPack); TEMPLATE_KIND(UsingTemplate); + TEMPLATE_KIND(DeducedTemplate); #undef TEMPLATE_KIND } llvm_unreachable("Unhandled NameKind enum"); diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index a366f1331c2d3d..e6d16af2495fec 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -1120,6 +1120,7 @@ class CollectExtraHighlightings case TemplateName::SubstTemplateTemplateParm: case TemplateName::SubstTemplateTemplateParmPack: case TemplateName::UsingTemplate: + case TemplateName::DeducedTemplate: // Names that could be resolved to a TemplateDecl are handled elsewhere. break; } diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index de86cb5e9d7fcf..837bcacbc0bfcf 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -262,6 +262,8 @@ class ASTContext : public RefCountedBase { mutable llvm::ContextualFoldingSet SubstTemplateTemplateParmPacks; + mutable llvm::ContextualFoldingSet + DeducedTemplates; mutable llvm::ContextualFoldingSet ArrayParameterTypes; @@ -2256,6 +2258,9 @@ class ASTContext : public RefCountedBase { unsigned Index, bool Final) const; + TemplateName getDeducedTemplateName(TemplateName Underlying, + DefaultArguments DefaultArgs) const; + enum GetBuiltinTypeError { /// No error GE_None, @@ -2735,11 +2740,13 @@ class ASTContext : public RefCountedBase { /// template name uses the shortest form of the dependent /// nested-name-specifier, which itself contains all canonical /// types, values, and templates. - TemplateName getCanonicalTemplateName(const TemplateName &Name) const; + TemplateName getCanonicalTemplateName(TemplateName Name, + bool IgnoreDeduced = false) const; /// Determine whether the given template names refer to the same /// template. - bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const; + bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y, + bool IgnoreDeduced = false) const; /// Determine whether the two declarations refer to the same entity. bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const; diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 4ffd9138465757..7b890bdf492fa1 100644 --- a/clang/include/clang/AST/ASTImporter.h +++ b/clang/include/clang/AST/ASTImporter.h @@ -485,6 +485,11 @@ class TypeSourceInfo; /// the declarations it contains. [[nodiscard]] llvm::Error ImportDefinition(Decl *From); + llvm::Error + ImportTemplateArguments(ArrayRef FromArgs, + SmallVectorImpl &ToArgs); + Expected Import(const TemplateArgument &From); + /// Cope with a name conflict when importing a declaration into the /// given context. /// diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h index 3b3c1afb096add..bdcaabc143cc40 100644 --- a/clang/include/clang/AST/DependenceFlags.h +++ b/clang/include/clang/AST/DependenceFlags.h @@ -315,6 +315,11 @@ toTemplateNameDependence(NestedNameSpecifierDependence D) { return Dependence(D).templateName(); } +inline TemplateNameDependence +toTemplateNameDependence(TemplateArgumentDependence D) { + return Dependence(D).templateName(); +} + LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); } // namespace clang diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 6df1d93a7ba2eb..bd0b316a4958ae 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -750,6 +750,23 @@ let Class = PropertyTypeCase in { return ctx.getSubstTemplateTemplateParmPack(argumentPack, associatedDecl, index, final); }]>; } +let Class = PropertyTypeCase in { + def : ReadHelper<[{ + auto DTS = node.getAsDeducedTemplateName(); + }]>; + def : Property<"underlying", TemplateName> { + let Read = [{ DTS->getUnderlying() }]; + } + def : Property<"startPos", UInt32> { + let Read = [{ DTS->getDefaultArguments().StartPos }]; + } + def : Property<"defaultArgs", Array> { + let Read = [{ DTS->getDefaultArguments().Args }]; + } + def : Creator<[{ + return ctx.getDeducedTemplateName(underlying, {startPos, defaultArgs}); + }]>; +} // Type cases for TemplateArgument. def : PropertyTypeKind(this) + : nullptr; + } + SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() { return Bits.Kind == SubstTemplateTemplateParm ? reinterpret_cast(this) @@ -172,6 +180,15 @@ class SubstTemplateTemplateParmPackStorage : public UncommonTemplateNameStorage, unsigned Index, bool Final); }; +struct DefaultArguments { + // The position in the template parameter list + // the first argument corresponds to. + unsigned StartPos; + ArrayRef Args; + + operator bool() const { return !Args.empty(); } +}; + /// Represents a C++ template name within the type system. /// /// A C++ template name refers to a template within the C++ type @@ -245,6 +262,10 @@ class TemplateName { /// A template name that refers to a template declaration found through a /// specific using shadow declaration. UsingTemplate, + + /// A template name that refers to another TemplateName with deduced default + /// arguments. + DeducedTemplate, }; TemplateName() = default; @@ -256,6 +277,7 @@ class TemplateName { explicit TemplateName(QualifiedTemplateName *Qual); explicit TemplateName(DependentTemplateName *Dep); explicit TemplateName(UsingShadowDecl *Using); + explicit TemplateName(DeducedTemplateStorage *Deduced); /// Determine whether this template name is NULL. bool isNull() const; @@ -270,7 +292,13 @@ class TemplateName { /// to, if any. If the template name does not refer to a specific /// declaration because it is a dependent name, or if it refers to a /// set of function templates, returns NULL. - TemplateDecl *getAsTemplateDecl() const; + TemplateDecl *getAsTemplateDecl(bool IgnoreDeduced = false) const; + + /// Retrieves the underlying template declaration that + /// this template name refers to, along with the + /// deduced default arguments, if any. + std::pair + getTemplateDeclAndDefaultArgs() const; /// Retrieve the underlying, overloaded function template /// declarations that this template name refers to, if known. @@ -312,6 +340,11 @@ class TemplateName { /// template declaration is introduced, if any. UsingShadowDecl *getAsUsingShadowDecl() const; + /// Retrieve the deduced template info, if any. + DeducedTemplateStorage *getAsDeducedTemplateName() const; + + std::optional desugar(bool IgnoreDeduced) const; + TemplateName getUnderlying() const; TemplateNameDependence getDependence() const; @@ -411,6 +444,30 @@ class SubstTemplateTemplateParmStorage std::optional PackIndex); }; +class DeducedTemplateStorage : public UncommonTemplateNameStorage, + public llvm::FoldingSetNode { + friend class ASTContext; + + TemplateName Underlying; + + DeducedTemplateStorage(TemplateName Underlying, + const DefaultArguments &DefArgs); + +public: + TemplateName getUnderlying() const { return Underlying; } + + DefaultArguments getDefaultArguments() const { + return {/*StartPos=*/Bits.Index, + /*Args=*/{reinterpret_cast(this + 1), + Bits.Data}}; + } + + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const; + + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + TemplateName Underlying, const DefaultArguments &DefArgs); +}; + inline TemplateName TemplateName::getUnderlying() const { if (SubstTemplateTemplateParmStorage *subst = getAsSubstTemplateTemplateParm()) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 174b9dbc6d980c..a847fb8fe5500e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9216,6 +9216,9 @@ class Sema final : public SemaBase { /// receive true if the cause for the error is the associated constraints of /// the template not being satisfied by the template arguments. /// + /// \param DefaultArgs any default arguments from template specialization + /// deduction. + /// /// \param PartialOrderingTTP If true, assume these template arguments are /// the injected template arguments for a template template parameter. /// This will relax the requirement that all its possible uses are valid: @@ -9225,7 +9228,8 @@ class Sema final : public SemaBase { /// \returns true if an error occurred, false otherwise. bool CheckTemplateArgumentList( TemplateDecl *Template, SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, + TemplateArgumentListInfo &TemplateArgs, + const DefaultArguments &DefaultArgs, bool PartialTemplateArgs, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions = true, @@ -9724,8 +9728,8 @@ class Sema final : public SemaBase { sema::TemplateDeductionInfo &Info); bool isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc, - bool IsDeduced); + TemplateParameterList *PParam, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced); void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced, unsigned Depth, llvm::SmallBitVector &Used); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index a4e6d3b108c8a5..d291a0a936df7a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -880,8 +880,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()), - ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()), - SourceMgr(SM), LangOpts(LOpts), + DeducedTemplates(this_()), ArrayParameterTypes(this_()), + CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), NoSanitizeL(new NoSanitizeList(LangOpts.NoSanitizeFiles, SM)), XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles, LangOpts.XRayNeverInstrumentFiles, @@ -5022,7 +5022,7 @@ ASTContext::getTemplateSpecializationType(TemplateName Template, assert(!Template.getAsDependentTemplateName() && "No dependent template names here!"); - const auto *TD = Template.getAsTemplateDecl(); + const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true); bool IsTypeAlias = TD && TD->isTypeAlias(); QualType CanonType; if (!Underlying.isNull()) @@ -5057,7 +5057,12 @@ QualType ASTContext::getCanonicalTemplateSpecializationType( "No dependent template names here!"); // Build the canonical template specialization type. - TemplateName CanonTemplate = getCanonicalTemplateName(Template); + // Any DeducedTemplateNames are ignored, because the effective name of a TST + // accounts for the TST arguments laid over any default arguments contained in + // its name. + TemplateName CanonTemplate = + getCanonicalTemplateName(Template, /*IgnoreDeduced=*/true); + bool AnyNonCanonArgs = false; auto CanonArgs = ::getCanonicalTemplateArguments(*this, Args, AnyNonCanonArgs); @@ -6354,16 +6359,41 @@ ASTContext::getNameForTemplate(TemplateName Name, case TemplateName::UsingTemplate: return DeclarationNameInfo(Name.getAsUsingShadowDecl()->getDeclName(), NameLoc); + case TemplateName::DeducedTemplate: { + DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName(); + return getNameForTemplate(DTS->getUnderlying(), NameLoc); + } } llvm_unreachable("bad template name kind!"); } -TemplateName -ASTContext::getCanonicalTemplateName(const TemplateName &Name) const { +static const TemplateArgument * +getDefaultTemplateArgumentOrNone(const NamedDecl *P) { + auto handleParam = [](auto *TP) -> const TemplateArgument * { + if (!TP->hasDefaultArgument()) + return nullptr; + return &TP->getDefaultArgument().getArgument(); + }; + switch (P->getKind()) { + case NamedDecl::TemplateTypeParm: + return handleParam(cast(P)); + case NamedDecl::NonTypeTemplateParm: + return handleParam(cast(P)); + case NamedDecl::TemplateTemplateParm: + return handleParam(cast(P)); + default: + llvm_unreachable("Unexpected template parameter kind"); + } +} + +TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name, + bool IgnoreDeduced) const { + while (std::optional UnderlyingOrNone = + Name.desugar(IgnoreDeduced)) + Name = *UnderlyingOrNone; + switch (Name.getKind()) { - case TemplateName::UsingTemplate: - case TemplateName::QualifiedTemplate: case TemplateName::Template: { TemplateDecl *Template = Name.getAsTemplateDecl(); if (auto *TTP = dyn_cast(Template)) @@ -6383,12 +6413,6 @@ ASTContext::getCanonicalTemplateName(const TemplateName &Name) const { return DTN->CanonicalTemplateName; } - case TemplateName::SubstTemplateTemplateParm: { - SubstTemplateTemplateParmStorage *subst - = Name.getAsSubstTemplateTemplateParm(); - return getCanonicalTemplateName(subst->getReplacement()); - } - case TemplateName::SubstTemplateTemplateParmPack: { SubstTemplateTemplateParmPackStorage *subst = Name.getAsSubstTemplateTemplateParmPack(); @@ -6398,15 +6422,58 @@ ASTContext::getCanonicalTemplateName(const TemplateName &Name) const { canonArgPack, subst->getAssociatedDecl()->getCanonicalDecl(), subst->getFinal(), subst->getIndex()); } + case TemplateName::DeducedTemplate: { + assert(IgnoreDeduced == false); + DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName(); + DefaultArguments DefArgs = DTS->getDefaultArguments(); + TemplateName Underlying = DTS->getUnderlying(); + + TemplateName CanonUnderlying = + getCanonicalTemplateName(Underlying, /*IgnoreDeduced=*/true); + bool NonCanonical = CanonUnderlying != Underlying; + auto CanonArgs = + getCanonicalTemplateArguments(*this, DefArgs.Args, NonCanonical); + + ArrayRef Params = + CanonUnderlying.getAsTemplateDecl()->getTemplateParameters()->asArray(); + assert(CanonArgs.size() <= Params.size()); + // A deduced template name which deduces the same default arguments already + // declared in the underlying template, is the same template as the + // underlying template. We need need to note any arguments which differ from + // the corresponding declaration. If they are not the same, we must build a + // deduced template name. + for (int I = CanonArgs.size() - 1; I >= 0; --I) { + const TemplateArgument *A = getDefaultTemplateArgumentOrNone(Params[I]); + if (!A) + break; + auto CanonParamDefArg = getCanonicalTemplateArgument(*A); + TemplateArgument &CanonDefArg = CanonArgs[I]; + if (CanonDefArg.structurallyEquals(CanonParamDefArg)) + continue; + // Keep popping from the back any deault arguments which are the same. + if (I == int(CanonArgs.size() - 1)) + CanonArgs.pop_back(); + NonCanonical = true; + } + return NonCanonical ? getDeducedTemplateName( + CanonUnderlying, + /*DefaultArgs=*/{DefArgs.StartPos, CanonArgs}) + : Name; + } + case TemplateName::UsingTemplate: + case TemplateName::QualifiedTemplate: + case TemplateName::SubstTemplateTemplateParm: + llvm_unreachable("always sugar node"); } llvm_unreachable("bad template name!"); } bool ASTContext::hasSameTemplateName(const TemplateName &X, - const TemplateName &Y) const { - return getCanonicalTemplateName(X).getAsVoidPointer() == - getCanonicalTemplateName(Y).getAsVoidPointer(); + const TemplateName &Y, + bool IgnoreDeduced) const { + return getCanonicalTemplateName(X, IgnoreDeduced) == + getCanonicalTemplateName(Y, IgnoreDeduced); } bool ASTContext::isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const { @@ -6895,7 +6962,7 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { case TemplateArgument::StructuralValue: return TemplateArgument(*this, getCanonicalType(Arg.getStructuralValueType()), - Arg.getAsStructuralValue()); + Arg.getAsStructuralValue(), Arg.getIsDefaulted()); case TemplateArgument::Type: return TemplateArgument(getCanonicalType(Arg.getAsType()), @@ -6907,8 +6974,10 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { *this, Arg.pack_elements(), AnyNonCanonArgs); if (!AnyNonCanonArgs) return Arg; - return TemplateArgument::CreatePackCopy(const_cast(*this), - CanonArgs); + auto NewArg = TemplateArgument::CreatePackCopy( + const_cast(*this), CanonArgs); + NewArg.setIsDefaulted(Arg.getIsDefaulted()); + return NewArg; } } @@ -9463,6 +9532,30 @@ ASTContext::getSubstTemplateTemplateParmPack(const TemplateArgument &ArgPack, return TemplateName(Subst); } +/// Retrieve the template name that represents a template name +/// deduced from a specialization. +TemplateName +ASTContext::getDeducedTemplateName(TemplateName Underlying, + DefaultArguments DefaultArgs) const { + if (!DefaultArgs) + return Underlying; + + llvm::FoldingSetNodeID ID; + DeducedTemplateStorage::Profile(ID, *this, Underlying, DefaultArgs); + + void *InsertPos = nullptr; + DeducedTemplateStorage *DTS = + DeducedTemplates.FindNodeOrInsertPos(ID, InsertPos); + if (!DTS) { + void *Mem = Allocate(sizeof(DeducedTemplateStorage) + + sizeof(TemplateArgument) * DefaultArgs.Args.size(), + alignof(DeducedTemplateStorage)); + DTS = new (Mem) DeducedTemplateStorage(Underlying, DefaultArgs); + DeducedTemplates.InsertNode(DTS, InsertPos); + } + return TemplateName(DTS); +} + /// getFromTargetType - Given one of the integer types provided by /// TargetInfo, produce the corresponding type. The unsigned @p Type /// is actually a value of type @c TargetInfo::IntType. @@ -12567,22 +12660,24 @@ static T *getCommonDeclChecked(T *X, T *Y) { } static TemplateName getCommonTemplateName(ASTContext &Ctx, TemplateName X, - TemplateName Y) { + TemplateName Y, + bool IgnoreDeduced = false) { if (X.getAsVoidPointer() == Y.getAsVoidPointer()) return X; // FIXME: There are cases here where we could find a common template name // with more sugar. For example one could be a SubstTemplateTemplate* // replacing the other. - TemplateName CX = Ctx.getCanonicalTemplateName(X); + TemplateName CX = Ctx.getCanonicalTemplateName(X, IgnoreDeduced); if (CX.getAsVoidPointer() != Ctx.getCanonicalTemplateName(Y).getAsVoidPointer()) return TemplateName(); return CX; } -static TemplateName -getCommonTemplateNameChecked(ASTContext &Ctx, TemplateName X, TemplateName Y) { - TemplateName R = getCommonTemplateName(Ctx, X, Y); +static TemplateName getCommonTemplateNameChecked(ASTContext &Ctx, + TemplateName X, TemplateName Y, + bool IgnoreDeduced) { + TemplateName R = getCommonTemplateName(Ctx, X, Y, IgnoreDeduced); assert(R.getAsVoidPointer() != nullptr); return R; } @@ -13069,7 +13164,8 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, TY->template_arguments()); return Ctx.getTemplateSpecializationType( ::getCommonTemplateNameChecked(Ctx, TX->getTemplateName(), - TY->getTemplateName()), + TY->getTemplateName(), + /*IgnoreDeduced=*/true), As, X->getCanonicalTypeInternal()); } case Type::Decltype: { @@ -13297,8 +13393,9 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, case Type::TemplateSpecialization: { const auto *TX = cast(X), *TY = cast(Y); - TemplateName CTN = ::getCommonTemplateName(Ctx, TX->getTemplateName(), - TY->getTemplateName()); + TemplateName CTN = + ::getCommonTemplateName(Ctx, TX->getTemplateName(), + TY->getTemplateName(), /*IgnoreDeduced=*/true); if (!CTN.getAsVoidPointer()) return QualType(); SmallVector Args; diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 0680ff5e3a3851..15c3efe4212719 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -1114,8 +1114,8 @@ class TemplateDiff { // These functions build up the template diff tree, including functions to // retrieve and compare template arguments. - static const TemplateSpecializationType *GetTemplateSpecializationType( - ASTContext &Context, QualType Ty) { + static const TemplateSpecializationType * + GetTemplateSpecializationType(ASTContext &Context, QualType Ty) { if (const TemplateSpecializationType *TST = Ty->getAs()) return TST; @@ -1159,7 +1159,7 @@ class TemplateDiff { if (!FromArgTST || !ToArgTST) return true; - if (!hasSameTemplate(FromArgTST, ToArgTST)) + if (!hasSameTemplate(Context, FromArgTST, ToArgTST)) return true; return false; @@ -1371,11 +1371,17 @@ class TemplateDiff { /// argument info into a tree. void DiffTemplate(const TemplateSpecializationType *FromTST, const TemplateSpecializationType *ToTST) { + // FIXME: With P3310R0, A TST formed from a DeducedTemplateName might + // differ in template arguments which were not written. // Begin descent into diffing template tree. TemplateParameterList *ParamsFrom = - FromTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); + FromTST->getTemplateName() + .getAsTemplateDecl(/*IgnoreDeduced=*/true) + ->getTemplateParameters(); TemplateParameterList *ParamsTo = - ToTST->getTemplateName().getAsTemplateDecl()->getTemplateParameters(); + ToTST->getTemplateName() + .getAsTemplateDecl(/*IgnoreDeduced=*/true) + ->getTemplateParameters(); unsigned TotalArgs = 0; for (TSTiterator FromIter(Context, FromTST), ToIter(Context, ToTST); !FromIter.isEnd() || !ToIter.isEnd(); ++TotalArgs) { @@ -1427,20 +1433,24 @@ class TemplateDiff { /// hasSameBaseTemplate - Returns true when the base templates are the same, /// even if the template arguments are not. - static bool hasSameBaseTemplate(const TemplateSpecializationType *FromTST, + static bool hasSameBaseTemplate(ASTContext &Context, + const TemplateSpecializationType *FromTST, const TemplateSpecializationType *ToTST) { - return FromTST->getTemplateName().getAsTemplateDecl()->getCanonicalDecl() == - ToTST->getTemplateName().getAsTemplateDecl()->getCanonicalDecl(); + return Context.getCanonicalTemplateName(FromTST->getTemplateName(), + /*IgnoreDeduced=*/true) == + Context.getCanonicalTemplateName(ToTST->getTemplateName(), + /*IgnoreDeduced=*/true); } /// hasSameTemplate - Returns true if both types are specialized from the /// same template declaration. If they come from different template aliases, /// do a parallel ascension search to determine the highest template alias in /// common and set the arguments to them. - static bool hasSameTemplate(const TemplateSpecializationType *&FromTST, + static bool hasSameTemplate(ASTContext &Context, + const TemplateSpecializationType *&FromTST, const TemplateSpecializationType *&ToTST) { // Check the top templates if they are the same. - if (hasSameBaseTemplate(FromTST, ToTST)) + if (hasSameBaseTemplate(Context, FromTST, ToTST)) return true; // Create vectors of template aliases. @@ -1455,14 +1465,14 @@ class TemplateDiff { ToIter = ToTemplateList.rbegin(), ToEnd = ToTemplateList.rend(); // Check if the lowest template types are the same. If not, return. - if (!hasSameBaseTemplate(*FromIter, *ToIter)) + if (!hasSameBaseTemplate(Context, *FromIter, *ToIter)) return false; // Begin searching up the template aliases. The bottom most template // matches so move up until one pair does not match. Use the template // right before that one. for (; FromIter != FromEnd && ToIter != ToEnd; ++FromIter, ++ToIter) { - if (!hasSameBaseTemplate(*FromIter, *ToIter)) + if (!hasSameBaseTemplate(Context, *FromIter, *ToIter)) break; } @@ -2123,7 +2133,7 @@ class TemplateDiff { return; // Different base templates. - if (!hasSameTemplate(FromOrigTST, ToOrigTST)) { + if (!hasSameTemplate(Context, FromOrigTST, ToOrigTST)) { return; } @@ -2131,10 +2141,11 @@ class TemplateDiff { ToQual -= QualType(ToOrigTST, 0).getQualifiers(); // Same base template, but different arguments. - Tree.SetTemplateDiff(FromOrigTST->getTemplateName().getAsTemplateDecl(), - ToOrigTST->getTemplateName().getAsTemplateDecl(), - FromQual, ToQual, false /*FromDefault*/, - false /*ToDefault*/); + Tree.SetTemplateDiff( + FromOrigTST->getTemplateName().getAsTemplateDecl( + /*IgnoreDeduced=*/true), + ToOrigTST->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true), + FromQual, ToQual, false /*FromDefault*/, false /*ToDefault*/); DiffTemplate(FromOrigTST, ToOrigTST); } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 1b67feaae88748..3061d7abefbd97 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -9891,6 +9891,21 @@ Expected ASTImporter::Import(TemplateName From) { return UsingOrError.takeError(); return TemplateName(cast(*UsingOrError)); } + case TemplateName::DeducedTemplate: { + DeducedTemplateStorage *S = From.getAsDeducedTemplateName(); + auto UnderlyingOrError = Import(S->getUnderlying()); + if (!UnderlyingOrError) + return UnderlyingOrError.takeError(); + + ASTNodeImporter Importer(*this); + DefaultArguments FromDefArgs = S->getDefaultArguments(); + SmallVector ToTemplateArgs; + if (Error Err = + Importer.ImportTemplateArguments(FromDefArgs.Args, ToTemplateArgs)) + return std::move(Err); + return ToContext.getDeducedTemplateName( + *UnderlyingOrError, {FromDefArgs.StartPos, ToTemplateArgs}); + } } llvm_unreachable("Invalid template name kind"); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 37555c324282fe..d81f45d5720a4e 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -645,6 +645,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, // It is sufficient to check value of getAsTemplateDecl. break; + case TemplateName::DeducedTemplate: + // FIXME: We can't reach here. + llvm_unreachable("unimplemented"); } return true; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 9d0a835a12c458..24229b78f3e8eb 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -350,7 +350,8 @@ LinkageComputer::getLVForTemplateArgumentList(ArrayRef Args, case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: if (TemplateDecl *Template = - Arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl()) + Arg.getAsTemplateOrTemplatePattern().getAsTemplateDecl( + /*IgnoreDeduced=*/true)) LV.merge(getLVForDecl(Template, computation)); continue; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 203db72c437331..7916ab97b58152 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2385,6 +2385,16 @@ void CXXNameMangler::mangleType(TemplateName TN) { Out << "_SUBSTPACK_"; break; } + case TemplateName::DeducedTemplate: { + DeducedTemplateStorage *S = TN.getAsDeducedTemplateName(); + mangleType(S->getUnderlying()); + auto [StartPos, Args] = S->getDefaultArguments(); + mangleNumber(StartPos); + Out << 'I'; + for (unsigned I = 0; I != Args.size(); ++I) + mangleTemplateArg(Args[I], /*NeedExactType=*/true); + Out << 'E'; + } } addSubstitution(TN); @@ -2502,6 +2512,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case TemplateName::OverloadedTemplate: case TemplateName::AssumedTemplate: case TemplateName::DependentTemplate: + case TemplateName::DeducedTemplate: llvm_unreachable("invalid base for a template specialization type"); case TemplateName::SubstTemplateTemplateParm: { @@ -5892,7 +5903,8 @@ struct CXXNameMangler::TemplateArgManglingInfo { // that of the template. auto *TTP = cast(Param); TemplateName ArgTemplateName = Arg.getAsTemplateOrTemplatePattern(); - const TemplateDecl *ArgTemplate = ArgTemplateName.getAsTemplateDecl(); + const TemplateDecl *ArgTemplate = + ArgTemplateName.getAsTemplateDecl(/*IgnoreDeduced=*/true); if (!ArgTemplate) return true; @@ -6759,9 +6771,6 @@ bool CXXNameMangler::mangleSubstitution(QualType T) { } bool CXXNameMangler::mangleSubstitution(TemplateName Template) { - if (TemplateDecl *TD = Template.getAsTemplateDecl()) - return mangleSubstitution(TD); - Template = Context.getASTContext().getCanonicalTemplateName(Template); return mangleSubstitution( reinterpret_cast(Template.getAsVoidPointer())); @@ -6931,9 +6940,6 @@ void CXXNameMangler::addSubstitution(QualType T) { } void CXXNameMangler::addSubstitution(TemplateName Template) { - if (TemplateDecl *TD = Template.getAsTemplateDecl()) - return addSubstitution(TD); - Template = Context.getASTContext().getCanonicalTemplateName(Template); addSubstitution(reinterpret_cast(Template.getAsVoidPointer())); } diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 1249531eab09fc..2366cd5a441a60 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -161,6 +161,7 @@ void ODRHash::AddTemplateName(TemplateName Name) { case TemplateName::SubstTemplateTemplateParm: case TemplateName::SubstTemplateTemplateParmPack: case TemplateName::UsingTemplate: + case TemplateName::DeducedTemplate: break; } } diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp index d4e8a8971a971a..044a1a92469aca 100644 --- a/clang/lib/AST/TemplateName.cpp +++ b/clang/lib/AST/TemplateName.cpp @@ -34,6 +34,30 @@ using namespace clang; +DeducedTemplateStorage::DeducedTemplateStorage(TemplateName Underlying, + const DefaultArguments &DefArgs) + : UncommonTemplateNameStorage(Deduced, /*Index=*/DefArgs.StartPos, + DefArgs.Args.size()), + Underlying(Underlying) { + llvm::copy(DefArgs.Args, reinterpret_cast(this + 1)); +} + +void DeducedTemplateStorage::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Context) const { + Profile(ID, Context, Underlying, getDefaultArguments()); +} + +void DeducedTemplateStorage::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Context, + TemplateName Underlying, + const DefaultArguments &DefArgs) { + Underlying.Profile(ID); + ID.AddInteger(DefArgs.StartPos); + ID.AddInteger(DefArgs.Args.size()); + for (const TemplateArgument &Arg : DefArgs.Args) + Arg.Profile(ID, Context); +} + TemplateArgument SubstTemplateTemplateParmPackStorage::getArgumentPack() const { return TemplateArgument(llvm::ArrayRef(Arguments, Bits.Data)); @@ -115,6 +139,8 @@ TemplateName::TemplateName(SubstTemplateTemplateParmPackStorage *Storage) TemplateName::TemplateName(QualifiedTemplateName *Qual) : Storage(Qual) {} TemplateName::TemplateName(DependentTemplateName *Dep) : Storage(Dep) {} TemplateName::TemplateName(UsingShadowDecl *Using) : Storage(Using) {} +TemplateName::TemplateName(DeducedTemplateStorage *Deduced) + : Storage(Deduced) {} bool TemplateName::isNull() const { return Storage.isNull(); } @@ -139,28 +165,63 @@ TemplateName::NameKind TemplateName::getKind() const { return AssumedTemplate; if (uncommon->getAsSubstTemplateTemplateParm()) return SubstTemplateTemplateParm; + if (uncommon->getAsDeducedTemplateName()) + return DeducedTemplate; + + assert(uncommon->getAsSubstTemplateTemplateParmPack() != nullptr); return SubstTemplateTemplateParmPack; } -TemplateDecl *TemplateName::getAsTemplateDecl() const { - if (Decl *TemplateOrUsing = Storage.dyn_cast()) { - if (UsingShadowDecl *USD = dyn_cast(TemplateOrUsing)) - return cast(USD->getTargetDecl()); - - assert(isa(TemplateOrUsing)); - return cast(TemplateOrUsing); - } +TemplateDecl *TemplateName::getAsTemplateDecl(bool IgnoreDeduced) const { + TemplateName Name = *this; + while (std::optional UnderlyingOrNone = + Name.desugar(IgnoreDeduced)) + Name = *UnderlyingOrNone; - if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) - return QTN->getUnderlyingTemplate().getAsTemplateDecl(); + if (!IgnoreDeduced) + assert(Name.getAsDeducedTemplateName() == nullptr && + "Unexpected canonical DeducedTemplateName; Did you mean to use " + "getTemplateDeclAndDefaultArgs instead?"); - if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm()) - return sub->getReplacement().getAsTemplateDecl(); + return cast_if_present(Name.Storage.dyn_cast()); +} - if (UsingShadowDecl *USD = getAsUsingShadowDecl()) - return cast(USD->getTargetDecl()); +std::pair +TemplateName::getTemplateDeclAndDefaultArgs() const { + for (TemplateName Name = *this; /**/; /**/) { + if (Name.getKind() == TemplateName::DeducedTemplate) { + DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName(); + TemplateDecl *TD = + DTS->getUnderlying().getAsTemplateDecl(/*IgnoreDeduced=*/true); + DefaultArguments DefArgs = DTS->getDefaultArguments(); + if (TD && DefArgs) + assert(DefArgs.StartPos + DefArgs.Args.size() <= + TD->getTemplateParameters()->size()); + return {TD, DTS->getDefaultArguments()}; + } + if (std::optional UnderlyingOrNone = + Name.desugar(/*IgnoreDeduced=*/false)) { + Name = *UnderlyingOrNone; + continue; + } + return {cast_if_present(Name.Storage.dyn_cast()), {}}; + } +} - return nullptr; +std::optional TemplateName::desugar(bool IgnoreDeduced) const { + if (Decl *D = Storage.dyn_cast()) { + if (auto *USD = dyn_cast(D)) + return TemplateName(USD->getTargetDecl()); + return std::nullopt; + } + if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) + return QTN->getUnderlyingTemplate(); + if (SubstTemplateTemplateParmStorage *S = getAsSubstTemplateTemplateParm()) + return S->getReplacement(); + if (IgnoreDeduced) + if (DeducedTemplateStorage *S = getAsDeducedTemplateName()) + return S->getUnderlying(); + return std::nullopt; } OverloadedTemplateStorage *TemplateName::getAsOverloadedTemplate() const { @@ -214,26 +275,20 @@ UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const { return nullptr; } +DeducedTemplateStorage *TemplateName::getAsDeducedTemplateName() const { + if (UncommonTemplateNameStorage *Uncommon = + Storage.dyn_cast()) + return Uncommon->getAsDeducedTemplateName(); + + return nullptr; +} + TemplateNameDependence TemplateName::getDependence() const { - auto D = TemplateNameDependence::None; switch (getKind()) { - case TemplateName::NameKind::QualifiedTemplate: - if (NestedNameSpecifier *NNS = getAsQualifiedTemplateName()->getQualifier()) - D |= toTemplateNameDependence(NNS->getDependence()); - break; - case TemplateName::NameKind::DependentTemplate: - D |= toTemplateNameDependence( - getAsDependentTemplateName()->getQualifier()->getDependence()); - break; - case TemplateName::NameKind::SubstTemplateTemplateParmPack: - D |= TemplateNameDependence::UnexpandedPack; - break; - case TemplateName::NameKind::OverloadedTemplate: - llvm_unreachable("overloaded templates shouldn't survive to here."); - default: - break; - } - if (TemplateDecl *Template = getAsTemplateDecl()) { + case NameKind::Template: + case NameKind::UsingTemplate: { + TemplateDecl *Template = getAsTemplateDecl(); + auto D = TemplateNameDependence::None; if (auto *TTP = dyn_cast(Template)) { D |= TemplateNameDependence::DependentInstantiation; if (TTP->isParameterPack()) @@ -246,10 +301,41 @@ TemplateNameDependence TemplateName::getDependence() const { if (Template->getDeclContext() && Template->getDeclContext()->isDependentContext()) D |= TemplateNameDependence::DependentInstantiation; - } else { - D |= TemplateNameDependence::DependentInstantiation; + return D; + } + case NameKind::QualifiedTemplate: { + QualifiedTemplateName *S = getAsQualifiedTemplateName(); + TemplateNameDependence D = S->getUnderlyingTemplate().getDependence(); + if (NestedNameSpecifier *NNS = S->getQualifier()) + D |= toTemplateNameDependence(NNS->getDependence()); + return D; + } + case NameKind::DependentTemplate: { + DependentTemplateName *S = getAsDependentTemplateName(); + auto D = TemplateNameDependence::DependentInstantiation; + D |= toTemplateNameDependence(S->getQualifier()->getDependence()); + return D; + } + case NameKind::SubstTemplateTemplateParm: { + auto *S = getAsSubstTemplateTemplateParm(); + return S->getReplacement().getDependence(); + } + case NameKind::SubstTemplateTemplateParmPack: + return TemplateNameDependence::UnexpandedPack | + TemplateNameDependence::DependentInstantiation; + case NameKind::DeducedTemplate: { + DeducedTemplateStorage *DTS = getAsDeducedTemplateName(); + TemplateNameDependence D = DTS->getUnderlying().getDependence(); + for (const TemplateArgument &Arg : DTS->getDefaultArguments().Args) + D |= toTemplateNameDependence(Arg.getDependence()); + return D; + } + case NameKind::AssumedTemplate: + return TemplateNameDependence::DependentInstantiation; + case NameKind::OverloadedTemplate: + llvm_unreachable("overloaded templates shouldn't survive to here."); } - return D; + llvm_unreachable("Unknown TemplateName kind"); } bool TemplateName::isDependent() const { @@ -331,6 +417,11 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy, OS << *SubstPack->getParameterPack(); else if (AssumedTemplateStorage *Assumed = getAsAssumedTemplateName()) { Assumed->getDeclName().print(OS, Policy); + } else if (DeducedTemplateStorage *Deduced = getAsDeducedTemplateName()) { + Deduced->getUnderlying().print(OS, Policy); + DefaultArguments DefArgs = Deduced->getDefaultArguments(); + OS << ":" << DefArgs.StartPos; + printTemplateArgumentList(OS, DefArgs.Args, Policy); } else { assert(getKind() == TemplateName::OverloadedTemplate); OverloadedTemplateStorage *OTS = getAsOverloadedTemplate(); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index bd1e630cd90470..992533672c7782 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1198,6 +1198,18 @@ void TextNodeDumper::dumpBareTemplateName(TemplateName TN) { dumpTemplateName(STS->getReplacement(), "replacement"); return; } + case TemplateName::DeducedTemplate: { + OS << " deduced"; + const DeducedTemplateStorage *DTS = TN.getAsDeducedTemplateName(); + dumpTemplateName(DTS->getUnderlying(), "underlying"); + AddChild("defaults", [=] { + auto [StartPos, Args] = DTS->getDefaultArguments(); + OS << " start " << StartPos; + for (const TemplateArgument &Arg : Args) + AddChild([=] { Visit(Arg, SourceRange()); }); + }); + return; + } // FIXME: Implement these. case TemplateName::OverloadedTemplate: OS << " overloaded"; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 656b733a13b0e9..6c777be6278abe 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -4293,7 +4293,8 @@ TemplateSpecializationType::TemplateSpecializationType( T.getKind() == TemplateName::SubstTemplateTemplateParm || T.getKind() == TemplateName::SubstTemplateTemplateParmPack || T.getKind() == TemplateName::UsingTemplate || - T.getKind() == TemplateName::QualifiedTemplate) && + T.getKind() == TemplateName::QualifiedTemplate || + T.getKind() == TemplateName::DeducedTemplate) && "Unexpected template name for TemplateSpecializationType"); auto *TemplateArgs = reinterpret_cast(this + 1); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 4add4d3af69a30..e995713d01ddd9 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1603,7 +1603,8 @@ void TypePrinter::printTemplateId(const TemplateSpecializationType *T, raw_ostream &OS, bool FullyQualify) { IncludeStrongLifetimeRAII Strong(Policy); - TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl(); + TemplateDecl *TD = + T->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true); // FIXME: Null TD never exercised in test suite. if (FullyQualify && TD) { if (!Policy.SuppressScope) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d38700d56e4ff9..20f89c48498080 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4504,8 +4504,9 @@ Sema::BuildMemInitializer(Decl *ConstructorD, for (auto const &Base : ClassDecl->bases()) { auto BaseTemplate = Base.getType()->getAs(); - if (BaseTemplate && Context.hasSameTemplateName( - BaseTemplate->getTemplateName(), TN)) { + if (BaseTemplate && + Context.hasSameTemplateName(BaseTemplate->getTemplateName(), TN, + /*IgnoreDeduced=*/true)) { Diag(IdLoc, diag::ext_unqualified_base_class) << SourceRange(IdLoc, Init->getSourceRange().getEnd()); BaseType = Base.getType(); @@ -11542,8 +11543,8 @@ bool Sema::CheckDeductionGuideDeclarator(Declarator &D, QualType &R, if (auto RetTST = TSI->getTypeLoc().getAsAdjusted()) { TemplateName SpecifiedName = RetTST.getTypePtr()->getTemplateName(); - bool TemplateMatches = - Context.hasSameTemplateName(SpecifiedName, GuidedTemplate); + bool TemplateMatches = Context.hasSameTemplateName( + SpecifiedName, GuidedTemplate, /*IgnoreDeduced=*/true); const QualifiedTemplateName *Qualifiers = SpecifiedName.getAsQualifiedTemplateName(); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a032e3ec6f6353..2296750cebc727 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4580,8 +4580,8 @@ Sema::findFailedBooleanCondition(Expr *Cond) { QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs) { - DependentTemplateName *DTN - = Name.getUnderlying().getAsDependentTemplateName(); + DependentTemplateName *DTN = + Name.getUnderlying().getAsDependentTemplateName(); if (DTN && DTN->isIdentifier()) // When building a template-id where the template-name is dependent, // assume the template is a type template. Either our assumption is @@ -4592,10 +4592,11 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, TemplateArgs.arguments()); if (Name.getAsAssumedTemplateName() && - resolveAssumedTemplateNameAsType(/*Scope*/nullptr, Name, TemplateLoc)) + resolveAssumedTemplateNameAsType(/*Scope=*/nullptr, Name, TemplateLoc)) return QualType(); - TemplateDecl *Template = Name.getAsTemplateDecl(); + auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs(); + if (!Template || isa(Template) || isa(Template) || isa(Template)) { // We might have a substituted template template parameter pack. If so, @@ -4613,8 +4614,9 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, // Check that the template argument list is well-formed for this // template. SmallVector SugaredConverted, CanonicalConverted; - if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs, false, - SugaredConverted, CanonicalConverted, + if (CheckTemplateArgumentList(Template, TemplateLoc, TemplateArgs, + DefaultArgs, false, SugaredConverted, + CanonicalConverted, /*UpdateArgsWithConversions=*/true)) return QualType(); @@ -5280,7 +5282,8 @@ DeclResult Sema::ActOnVarTemplateSpecialization( // template. SmallVector SugaredConverted, CanonicalConverted; if (CheckTemplateArgumentList(VarTemplate, TemplateNameLoc, TemplateArgs, - false, SugaredConverted, CanonicalConverted, + /*DefaultArgs=*/{}, false, SugaredConverted, + CanonicalConverted, /*UpdateArgsWithConversions=*/true)) return true; @@ -5447,8 +5450,8 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, SmallVector SugaredConverted, CanonicalConverted; if (CheckTemplateArgumentList( Template, TemplateNameLoc, - const_cast(TemplateArgs), false, - SugaredConverted, CanonicalConverted, + const_cast(TemplateArgs), + /*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted, /*UpdateArgsWithConversions=*/true)) return true; @@ -5642,6 +5645,7 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, if (CheckTemplateArgumentList( NamedConcept, ConceptNameInfo.getLoc(), const_cast(*TemplateArgs), + /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, SugaredConverted, CanonicalConverted, /*UpdateArgsWithConversions=*/false)) return ExprError(); @@ -6610,7 +6614,8 @@ static bool diagnoseMissingArgument(Sema &S, SourceLocation Loc, /// for specializing the given template. bool Sema::CheckTemplateArgumentList( TemplateDecl *Template, SourceLocation TemplateLoc, - TemplateArgumentListInfo &TemplateArgs, bool PartialTemplateArgs, + TemplateArgumentListInfo &TemplateArgs, const DefaultArguments &DefaultArgs, + bool PartialTemplateArgs, SmallVectorImpl &SugaredConverted, SmallVectorImpl &CanonicalConverted, bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied, @@ -6638,9 +6643,29 @@ bool Sema::CheckTemplateArgumentList( SmallVector CanonicalArgumentPack; unsigned ArgIdx = 0, NumArgs = NewArgs.size(); LocalInstantiationScope InstScope(*this, true); - for (TemplateParameterList::iterator Param = Params->begin(), - ParamEnd = Params->end(); - Param != ParamEnd; /* increment in loop */) { + for (TemplateParameterList::iterator ParamBegin = Params->begin(), + ParamEnd = Params->end(), + Param = ParamBegin; + Param != ParamEnd; + /* increment in loop */) { + if (size_t ParamIdx = Param - ParamBegin; + DefaultArgs && ParamIdx >= DefaultArgs.StartPos) { + // All written arguments should have been consumed by this point. + assert(ArgIdx == NumArgs && "bad default argument deduction"); + // FIXME: Don't ignore parameter packs. + if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) { + assert(Param + DefaultArgs.Args.size() <= ParamEnd); + // Default arguments from a DeducedTemplateName are already converted. + for (const TemplateArgument &DefArg : DefaultArgs.Args) { + SugaredConverted.push_back(DefArg); + CanonicalConverted.push_back( + Context.getCanonicalTemplateArgument(DefArg)); + ++Param; + } + continue; + } + } + // If we have an expanded parameter pack, make sure we don't have too // many arguments. if (std::optional Expansions = getExpandedPackSize(*Param)) { @@ -6854,6 +6879,7 @@ bool Sema::CheckTemplateArgumentList( CTAK_Specified)) return true; + SugaredConverted.back().setIsDefaulted(true); CanonicalConverted.back().setIsDefaulted(true); // Core issue 150 (assumed resolution): if this is a template template @@ -8454,7 +8480,7 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, TemplateArgumentLoc &Arg, bool IsDeduced) { TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern(); - TemplateDecl *Template = Name.getAsTemplateDecl(); + auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs(); if (!Template) { // Any dependent template name is fine. assert(Name.isDependent() && "Non-dependent template isn't a declaration?"); @@ -8505,7 +8531,7 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, return false; if (isTemplateTemplateParameterAtLeastAsSpecializedAs( - Params, Template, Arg.getLocation(), IsDeduced)) { + Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) { // P2113 // C++20[temp.func.order]p2 // [...] If both deductions succeed, the partial ordering selects the @@ -9591,7 +9617,9 @@ DeclResult Sema::ActOnClassTemplateSpecialization( // template. SmallVector SugaredConverted, CanonicalConverted; if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, TemplateArgs, - false, SugaredConverted, CanonicalConverted, + /*DefaultArgs=*/{}, + /*PartialTemplateArgs=*/false, SugaredConverted, + CanonicalConverted, /*UpdateArgsWithConversions=*/true)) return true; @@ -10963,7 +10991,8 @@ DeclResult Sema::ActOnExplicitInstantiation( // template. SmallVector SugaredConverted, CanonicalConverted; if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, TemplateArgs, - false, SugaredConverted, CanonicalConverted, + /*DefaultArgs=*/{}, false, SugaredConverted, + CanonicalConverted, /*UpdateArgsWithConversions=*/true)) return true; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index a432918cbf5e20..3efc3030fc2617 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -509,67 +509,12 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, DeducedTemplateArgument(New), T, Info, Deduced); } -/// Create a shallow copy of a given template parameter declaration, with -/// empty source locations and using the given TemplateArgument as it's -/// default argument. -/// -/// \returns The new template parameter declaration. -static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A, - TemplateArgument Default) { - switch (A->getKind()) { - case Decl::TemplateTypeParm: { - auto *T = cast(A); - auto *R = TemplateTypeParmDecl::Create( - S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(), - T->getDepth(), T->getIndex(), T->getIdentifier(), - T->wasDeclaredWithTypename(), T->isParameterPack(), - T->hasTypeConstraint()); - R->setDefaultArgument( - S.Context, - S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation())); - if (R->hasTypeConstraint()) { - auto *C = R->getTypeConstraint(); - R->setTypeConstraint(C->getConceptReference(), - C->getImmediatelyDeclaredConstraint()); - } - return R; - } - case Decl::NonTypeTemplateParm: { - auto *T = cast(A); - auto *R = NonTypeTemplateParmDecl::Create( - S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(), - T->getDepth(), T->getIndex(), T->getIdentifier(), T->getType(), - T->isParameterPack(), T->getTypeSourceInfo()); - R->setDefaultArgument(S.Context, - S.getTrivialTemplateArgumentLoc( - Default, Default.getNonTypeTemplateArgumentType(), - SourceLocation())); - if (auto *PTC = T->getPlaceholderTypeConstraint()) - R->setPlaceholderTypeConstraint(PTC); - return R; - } - case Decl::TemplateTemplateParm: { - auto *T = cast(A); - auto *R = TemplateTemplateParmDecl::Create( - S.Context, A->getDeclContext(), SourceLocation(), T->getDepth(), - T->getIndex(), T->isParameterPack(), T->getIdentifier(), - T->wasDeclaredWithTypename(), T->getTemplateParameters()); - R->setDefaultArgument( - S.Context, - S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation())); - return R; - } - default: - llvm_unreachable("Unexpected Decl Kind"); - } -} - static TemplateDeductionResult -DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, - TemplateName Param, TemplateName Arg, - TemplateDeductionInfo &Info, - ArrayRef DefaultArguments, - SmallVectorImpl &Deduced) { +DeduceTemplateNames(Sema &S, TemplateParameterList *TemplateParams, + TemplateName Param, TemplateName Arg, + TemplateDeductionInfo &Info, + ArrayRef DefaultArguments, + SmallVectorImpl &Deduced) { TemplateDecl *ParamDecl = Param.getAsTemplateDecl(); if (!ParamDecl) { // The parameter type is dependent and is not a template template parameter, @@ -582,42 +527,28 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, if (TempParam->getDepth() != Info.getDeducedDepth()) return TemplateDeductionResult::Success; - auto NewDeduced = DeducedTemplateArgument(Arg); - // Provisional resolution for CWG2398: If Arg is also a template template - // param, and it names a template specialization, then we deduce a - // synthesized template template parameter based on A, but using the TS's - // arguments as defaults. - if (auto *TempArg = dyn_cast_or_null( - Arg.getAsTemplateDecl())) { - assert(!TempArg->isExpandedParameterPack()); - - TemplateParameterList *As = TempArg->getTemplateParameters(); - if (DefaultArguments.size() != 0) { - assert(DefaultArguments.size() <= As->size()); - SmallVector Params(As->size()); - for (unsigned I = 0; I < DefaultArguments.size(); ++I) - Params[I] = getTemplateParameterWithDefault(S, As->getParam(I), - DefaultArguments[I]); - for (unsigned I = DefaultArguments.size(); I < As->size(); ++I) - Params[I] = As->getParam(I); - // FIXME: We could unique these, and also the parameters, but we don't - // expect programs to contain a large enough amount of these deductions - // for that to be worthwhile. - auto *TPL = TemplateParameterList::Create( - S.Context, SourceLocation(), SourceLocation(), Params, - SourceLocation(), As->getRequiresClause()); - NewDeduced = DeducedTemplateArgument( - TemplateName(TemplateTemplateParmDecl::Create( - S.Context, TempArg->getDeclContext(), SourceLocation(), - TempArg->getDepth(), TempArg->getPosition(), - TempArg->isParameterPack(), TempArg->getIdentifier(), - TempArg->wasDeclaredWithTypename(), TPL))); + ArrayRef Params = + ParamDecl->getTemplateParameters()->asArray(); + unsigned StartPos = 0; + for (unsigned I = 0, E = std::min(Params.size(), DefaultArguments.size()); + I < E; ++I) { + if (Params[I]->isParameterPack()) { + StartPos = DefaultArguments.size(); + break; } + StartPos = I + 1; } - DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, - Deduced[TempParam->getIndex()], - NewDeduced); + // Provisional resolution for CWG2398: If Arg names a template + // specialization, then we deduce a synthesized template name + // based on A, but using the TS's extra arguments, relative to P, as + // defaults. + DeducedTemplateArgument NewDeduced = + TemplateArgument(S.Context.getDeducedTemplateName( + Arg, {StartPos, DefaultArguments.drop_front(StartPos)})); + + DeducedTemplateArgument Result = checkDeducedTemplateArguments( + S.Context, Deduced[TempParam->getIndex()], NewDeduced); if (Result.isNull()) { Info.Param = TempParam; Info.FirstArg = Deduced[TempParam->getIndex()]; @@ -630,7 +561,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, } // Verify that the two template names are equivalent. - if (S.Context.hasSameTemplateName(Param, Arg)) + if (S.Context.hasSameTemplateName( + Param, Arg, /*IgnoreDeduced=*/DefaultArguments.size() != 0)) return TemplateDeductionResult::Success; // Mismatch of non-dependent template parameter to argument. @@ -720,8 +652,9 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams, ->template_arguments(); // Perform template argument deduction for the template name. - if (auto Result = DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, - AResolved, Deduced); + if (auto Result = + DeduceTemplateNames(S, TemplateParams, TNP, TNA, Info, + /*DefaultArguments=*/AResolved, Deduced); Result != TemplateDeductionResult::Success) return Result; @@ -751,9 +684,9 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams, *NNS, false, TemplateName(SA->getSpecializedTemplate())); // Perform template argument deduction for the template name. - if (auto Result = - DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, - SA->getTemplateArgs().asArray(), Deduced); + if (auto Result = DeduceTemplateNames( + S, TemplateParams, TNP, TNA, Info, + /*DefaultArguments=*/SA->getTemplateArgs().asArray(), Deduced); Result != TemplateDeductionResult::Success) return Result; @@ -2443,9 +2376,9 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, case TemplateArgument::Template: if (A.getKind() == TemplateArgument::Template) - return DeduceTemplateArguments(S, TemplateParams, P.getAsTemplate(), - A.getAsTemplate(), Info, - /*DefaultArguments=*/{}, Deduced); + return DeduceTemplateNames(S, TemplateParams, P.getAsTemplate(), + A.getAsTemplate(), Info, + /*DefaultArguments=*/{}, Deduced); Info.FirstArg = P; Info.SecondArg = A; return TemplateDeductionResult::NonDeducedMismatch; @@ -3194,7 +3127,7 @@ FinishTemplateArgumentDeduction( SmallVector SugaredConvertedInstArgs, CanonicalConvertedInstArgs; if (S.CheckTemplateArgumentList( - Template, Partial->getLocation(), InstArgs, false, + Template, Partial->getLocation(), InstArgs, /*DefaultArgs=*/{}, false, SugaredConvertedInstArgs, CanonicalConvertedInstArgs, /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied)) return ConstraintsNotSatisfied @@ -3516,8 +3449,8 @@ TemplateDeductionResult Sema::SubstituteExplicitTemplateArguments( return TemplateDeductionResult::InstantiationDepth; if (CheckTemplateArgumentList(FunctionTemplate, SourceLocation(), - ExplicitTemplateArgs, true, SugaredBuilder, - CanonicalBuilder, + ExplicitTemplateArgs, /*DefaultArgs=*/{}, true, + SugaredBuilder, CanonicalBuilder, /*UpdateArgsWithConversions=*/false) || Trap.hasErrorOccurred()) { unsigned Index = SugaredBuilder.size(); @@ -5127,9 +5060,9 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, TemplateArgs.addArgument(TypeLoc.getArgLoc(I)); llvm::SmallVector SugaredConverted, CanonicalConverted; - if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, - /*PartialTemplateArgs=*/false, - SugaredConverted, CanonicalConverted)) + if (S.CheckTemplateArgumentList( + Concept, SourceLocation(), TemplateArgs, /*DefaultArgs=*/{}, + /*PartialTemplateArgs=*/false, SugaredConverted, CanonicalConverted)) return true; MultiLevelTemplateArgumentList MLTAL(Concept, CanonicalConverted, /*Final=*/false); @@ -6362,8 +6295,8 @@ bool Sema::isMoreSpecializedThanPrimary( } bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( - TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc, - bool IsDeduced) { + TemplateParameterList *P, TemplateDecl *AArg, + const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced) { // C++1z [temp.arg.template]p4: (DR 150) // A template template-parameter P is at least as specialized as a // template template-argument A if, given the following rewrite to two @@ -6411,8 +6344,9 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( // If the rewrite produces an invalid type, then P is not at least as // specialized as A. SmallVector SugaredPArgs; - if (CheckTemplateArgumentList(AArg, Loc, PArgList, false, SugaredPArgs, - PArgs, /*UpdateArgsWithConversions=*/true, + if (CheckTemplateArgumentList(AArg, Loc, PArgList, DefaultArgs, false, + SugaredPArgs, PArgs, + /*UpdateArgsWithConversions=*/true, /*ConstraintsNotSatisfied=*/nullptr, /*PartialOrderTTP=*/true) || Trap.hasErrorOccurred()) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 0681520764d9a0..26b2fa793b48ac 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3878,10 +3878,10 @@ TemplateDeclInstantiator::VisitClassTemplateSpecializationDecl( // Check that the template argument list is well-formed for this // class template. SmallVector SugaredConverted, CanonicalConverted; - if (SemaRef.CheckTemplateArgumentList(InstClassTemplate, D->getLocation(), - InstTemplateArgs, false, - SugaredConverted, CanonicalConverted, - /*UpdateArgsWithConversions=*/true)) + if (SemaRef.CheckTemplateArgumentList( + InstClassTemplate, D->getLocation(), InstTemplateArgs, + /*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted, + /*UpdateArgsWithConversions=*/true)) return nullptr; // Figure out where to insert this class template explicit specialization @@ -3986,10 +3986,10 @@ Decl *TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl( // Check that the template argument list is well-formed for this template. SmallVector SugaredConverted, CanonicalConverted; - if (SemaRef.CheckTemplateArgumentList(InstVarTemplate, D->getLocation(), - VarTemplateArgsInfo, false, - SugaredConverted, CanonicalConverted, - /*UpdateArgsWithConversions=*/true)) + if (SemaRef.CheckTemplateArgumentList( + InstVarTemplate, D->getLocation(), VarTemplateArgsInfo, + /*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted, + /*UpdateArgsWithConversions=*/true)) return nullptr; // Check whether we've already seen a declaration of this specialization. @@ -4254,6 +4254,7 @@ TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization( SmallVector SugaredConverted, CanonicalConverted; if (SemaRef.CheckTemplateArgumentList( ClassTemplate, PartialSpec->getLocation(), InstTemplateArgs, + /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, SugaredConverted, CanonicalConverted)) return nullptr; @@ -4365,9 +4366,10 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization( // Check that the template argument list is well-formed for this // class template. SmallVector SugaredConverted, CanonicalConverted; - if (SemaRef.CheckTemplateArgumentList( - VarTemplate, PartialSpec->getLocation(), InstTemplateArgs, - /*PartialTemplateArgs=*/false, SugaredConverted, CanonicalConverted)) + if (SemaRef.CheckTemplateArgumentList(VarTemplate, PartialSpec->getLocation(), + InstTemplateArgs, /*DefaultArgs=*/{}, + /*PartialTemplateArgs=*/false, + SugaredConverted, CanonicalConverted)) return nullptr; // Check these arguments are valid for a template partial specialization. diff --git a/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp b/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp index a5b39fe5c51f70..bc39431253880c 100644 --- a/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp @@ -28,13 +28,14 @@ namespace StdExample { { /* ... */ } template class TT> - void f(TT); // expected-note {{candidate template ignored}} + void f(TT); template class TT> void g(TT>); int h() { - f(v); // expected-error {{no matching function for call to 'f'}} + f(v); // OK: TT = vector, Alloc is used as the default argument for the + // second parameter. g(v); // OK: TT = vector } diff --git a/clang/test/CodeGenCXX/mangle-cwg2398.cpp b/clang/test/CodeGenCXX/mangle-cwg2398.cpp new file mode 100644 index 00000000000000..a8a0aed17c70f6 --- /dev/null +++ b/clang/test/CodeGenCXX/mangle-cwg2398.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-pc -emit-llvm -o - %s | FileCheck %s + +template struct A {}; + +template class TT> void f(TT); + +// CHECK-LABEL: define{{.*}} void @_Z1zv( +void z() { + f(A()); + // CHECK: call void @_Z1fITtTyE1A1IdEEvT_IiE() +} diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp index 7675d4287cb88a..51a7c7b3612e44 100644 --- a/clang/test/SemaTemplate/cwg2398.cpp +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -4,12 +4,11 @@ namespace issue1 { template class B {}; template class P, class T> void f(P); - // new-note@-1 {{deduced type 'B<[...], (default) int>' of 1st parameter does not match adjusted type 'B<[...], float>' of argument [with P = B, T = int]}} - // old-note@-2 2{{template template argument has different template parameters}} + // old-note@-1 2{{template template argument has different template parameters}} void g() { f(B()); // old-error {{no matching function for call}} - f(B()); // expected-error {{no matching function for call}} + f(B()); // old-error {{no matching function for call}} } } // namespace issue1 @@ -65,13 +64,10 @@ namespace class_template { template struct B; template