llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-modules Author: Matheus Izvekov (mizvekov) <details> <summary>Changes</summary> This extends default argument deduction to cover class templates as well, and also applies outside of partial ordering, adding to the provisional wording introduced in https://github.com/llvm/llvm-project/pull/89807. This solves some ambuguity introduced in P0522 regarding how template template parameters are partially ordered, and should reduce the negative impact of enabling `-frelaxed-template-template-args` by default. Given the following example: ```C++ template <class T1, class T2 = float> struct A; template <class T3> struct B; template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; // #<!-- -->1 template <class T6, class T7> struct B<A<T6, T7>>; // #<!-- -->2 template struct B<A<int>>; ``` Prior to P0522, `#<!-- -->2` was picked. Afterwards, this became ambiguous. This patch restores the pre-P0522 behavior, `#<!-- -->2` is picked again. As the consequences are not restricted to partial ordering, the following code becomes valid: ```C++ template<class T, class U> struct A {}; A<int, float> v; template<template<class> class TT> void f(TT<int>); // OK: TT picks 'float' as the default argument for the second parameter. void g() { f(v); } ``` Also, since 'f' deduced from `A<int, float>` is different from 'f' deduced from `A<int, double>`, this implements an additional mangling rule. --- Since this changes provisional implementation of CWG2398 which has not been released yet, and already contains a changelog entry, we don't provide a changelog entry here. --- Patch is 73.91 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94981.diff 25 Files Affected: - (modified) clang-tools-extra/clangd/DumpAST.cpp (+1) - (modified) clang-tools-extra/clangd/SemanticHighlighting.cpp (+1) - (modified) clang/include/clang/AST/ASTContext.h (+7-1) - (modified) clang/include/clang/AST/ASTImporter.h (+5) - (modified) clang/include/clang/AST/DependenceFlags.h (+5) - (modified) clang/include/clang/AST/PropertiesBase.td (+17) - (modified) clang/include/clang/AST/TemplateName.h (+57-2) - (modified) clang/include/clang/Sema/Sema.h (+7-3) - (modified) clang/lib/AST/ASTContext.cpp (+113-16) - (modified) clang/lib/AST/ASTDiagnostic.cpp (+13-11) - (modified) clang/lib/AST/ASTImporter.cpp (+45-47) - (modified) clang/lib/AST/ASTStructuralEquivalence.cpp (+3) - (modified) clang/lib/AST/ItaniumMangle.cpp (+11) - (modified) clang/lib/AST/ODRHash.cpp (+1) - (modified) clang/lib/AST/TemplateName.cpp (+123-34) - (modified) clang/lib/AST/TextNodeDumper.cpp (+12) - (modified) clang/lib/AST/Type.cpp (+2-1) - (modified) clang/lib/Sema/SemaTemplate.cpp (+46-17) - (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+35-98) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+13-11) - (modified) clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp (+3-2) - (added) clang/test/CodeGenCXX/mangle-cwg2398.cpp (+11) - (modified) clang/test/SemaTemplate/cwg2398.cpp (+31-12) - (modified) clang/tools/libclang/CIndex.cpp (+3) - (modified) clang/unittests/AST/ASTImporterTest.cpp (+17) ``````````diff diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp index 9a525efb938e8..e605f82e91fe4 100644 --- a/clang-tools-extra/clangd/DumpAST.cpp +++ b/clang-tools-extra/clangd/DumpAST.cpp @@ -187,6 +187,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> { 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 a366f1331c2d3..e6d16af2495fe 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 8bce4812f0d48..8818314de9364 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -262,6 +262,8 @@ class ASTContext : public RefCountedBase<ASTContext> { mutable llvm::ContextualFoldingSet<SubstTemplateTemplateParmPackStorage, ASTContext&> SubstTemplateTemplateParmPacks; + mutable llvm::ContextualFoldingSet<DeducedTemplateStorage, ASTContext &> + DeducedTemplates; mutable llvm::ContextualFoldingSet<ArrayParameterType, ASTContext &> ArrayParameterTypes; @@ -2247,6 +2249,9 @@ class ASTContext : public RefCountedBase<ASTContext> { unsigned Index, bool Final) const; + TemplateName getDeducedTemplateName(TemplateName Underlying, + DefaultArguments DefaultArgs) const; + enum GetBuiltinTypeError { /// No error GE_None, @@ -2726,7 +2731,8 @@ class ASTContext : public RefCountedBase<ASTContext> { /// 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. diff --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h index 4ffd913846575..7b890bdf492fa 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<TemplateArgument> FromArgs, + SmallVectorImpl<TemplateArgument> &ToArgs); + Expected<TemplateArgument> 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 3b3c1afb096ad..bdcaabc143cc4 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 6df1d93a7ba2e..bd0b316a4958a 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -750,6 +750,23 @@ let Class = PropertyTypeCase<TemplateName, "SubstTemplateTemplateParmPack"> in { return ctx.getSubstTemplateTemplateParmPack(argumentPack, associatedDecl, index, final); }]>; } +let Class = PropertyTypeCase<TemplateName, "DeducedTemplate"> 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<TemplateArgument>> { + let Read = [{ DTS->getDefaultArguments().Args }]; + } + def : Creator<[{ + return ctx.getDeducedTemplateName(underlying, {startPos, defaultArgs}); + }]>; +} // Type cases for TemplateArgument. def : PropertyTypeKind<TemplateArgument, TemplateArgumentKind, diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h index 988a55acd2252..bd70954a42e0d 100644 --- a/clang/include/clang/AST/TemplateName.h +++ b/clang/include/clang/AST/TemplateName.h @@ -34,6 +34,7 @@ class NestedNameSpecifier; enum OverloadedOperatorKind : int; class OverloadedTemplateStorage; class AssumedTemplateStorage; +class DeducedTemplateStorage; struct PrintingPolicy; class QualifiedTemplateName; class SubstTemplateTemplateParmPackStorage; @@ -50,16 +51,17 @@ class UncommonTemplateNameStorage { enum Kind { Overloaded, Assumed, // defined in DeclarationName.h + Deduced, SubstTemplateTemplateParm, SubstTemplateTemplateParmPack }; struct BitsTag { LLVM_PREFERRED_TYPE(Kind) - unsigned Kind : 2; + unsigned Kind : 3; // The template parameter index. - unsigned Index : 15; + unsigned Index : 14; /// The pack index, or the number of stored templates /// or template arguments, depending on which subclass we have. @@ -90,6 +92,12 @@ class UncommonTemplateNameStorage { : nullptr; } + DeducedTemplateStorage *getAsDeducedTemplateName() { + return Bits.Kind == Deduced + ? reinterpret_cast<DeducedTemplateStorage *>(this) + : nullptr; + } + SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() { return Bits.Kind == SubstTemplateTemplateParm ? reinterpret_cast<SubstTemplateTemplateParmStorage *>(this) @@ -172,6 +180,13 @@ class SubstTemplateTemplateParmPackStorage : public UncommonTemplateNameStorage, unsigned Index, bool Final); }; +struct DefaultArguments { + unsigned StartPos; + ArrayRef<TemplateArgument> 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 +260,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 +275,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; @@ -272,6 +292,12 @@ class TemplateName { /// set of function templates, returns NULL. TemplateDecl *getAsTemplateDecl() const; + /// Retrieves the underlying template declaration that + /// this template name refers to, along with the + /// deduced default arguments, if any. + std::pair<TemplateDecl *, DefaultArguments> + getTemplateDeclAndDefaultArgs() const; + /// Retrieve the underlying, overloaded function template /// declarations that this template name refers to, if known. /// @@ -312,6 +338,11 @@ class TemplateName { /// template declaration is introduced, if any. UsingShadowDecl *getAsUsingShadowDecl() const; + /// Retrieve the deduced template info, if any. + DeducedTemplateStorage *getAsDeducedTemplateName() const; + + std::optional<TemplateName> desugar(bool IgnoreDeduced) const; + TemplateName getUnderlying() const; TemplateNameDependence getDependence() const; @@ -409,6 +440,30 @@ class SubstTemplateTemplateParmStorage std::optional<unsigned> 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<const TemplateArgument *>(this + 1), + Bits.Data}}; + } + + void Profile(llvm::FoldingSetNodeID &ID, ASTContext &Context); + + static void Profile(llvm::FoldingSetNodeID &ID, 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 4d4579fcfd456..ce7066a76eaec 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9210,6 +9210,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: @@ -9219,7 +9222,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<TemplateArgument> &SugaredConverted, SmallVectorImpl<TemplateArgument> &CanonicalConverted, bool UpdateArgsWithConversions = true, @@ -9718,8 +9722,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 cd76b8aa271da..9c8f92cd60b7b 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, @@ -5043,7 +5043,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); @@ -6327,16 +6332,22 @@ 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 { +TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name, + bool IgnoreDeduced) const { + while (std::optional<TemplateName> 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<TemplateTemplateParmDecl>(Template)) @@ -6356,12 +6367,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(); @@ -6371,6 +6376,70 @@ 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(); + + bool NonCanonical = false; + TemplateName CanonUnderlying = + getCanonicalTemplateName(Underlying, /*IgnoreDeduced=*/true); + NonCanonical |= CanonUnderlying != Underlying; + auto CanonArgs = + getCanonicalTemplateArguments(*this, DefArgs.Args, NonCanonical); + { + unsigned NumArgs = CanonArgs.size() - 1; + auto handleParamDefArg = [&](const TemplateArgument &ParamDefArg, + unsigned I) { + auto CanonParamDefArg = getCanonicalTemplateArgument(ParamDefArg); + TemplateArgument &CanonDefArg = CanonArgs[I]; + if (CanonDefArg.structurallyEquals(CanonParamDefArg)) + return; + if (I == NumArgs) + CanonArgs.pop_back(); + NonCanonical = true; + }; + auto handleParam = [&](auto *TP, int I) -> bool { + if (!TP->hasDefaultArgument()) + return true; + handleParamDefArg(TP->getDefaultArgument().getArgument(), I); + return false; + }; + + ArrayRef<NamedDecl *> Params = CanonUnderlying.getAsTemplateDecl() + ->getTemplateParameters() + ->asArray(); + assert(CanonArgs.size() <= Params.size()); + for (int I = NumArgs; I >= 0; --I) { + switch (auto *Param = Params[I]; Param->getKind()) { + case NamedDecl::TemplateTypeParm: + if (handleParam(cast<TemplateTypeParmDecl>(Param), I)) + break; + continue; + case NamedDecl::NonTypeTemplateParm: + if (handleParam(cast<NonTypeTemplateParmDecl>(Param), I)) + break; + continue; + case NamedDecl::TemplateTemplateParm: + if (handleParam(cast<TemplateTemplateParmDecl>(Param), I)) + break; + continue; + default: + llvm_unreachable("Unexpected template parameter kind"); + } + break; + } + } + 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!"); @@ -6868,7 +6937,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()), @@ -6880,8 +6949,10 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { *this, Arg.pack_elements(), AnyNonCanonArgs); if (!AnyNonCanonArgs) return Arg; - return TemplateArgument::CreatePackCopy(const_cast<ASTContext &>(*this), - CanonArgs); + auto NewArg = TemplateArgument::CreatePackCopy( + const_cast<ASTContext &>(*this), CanonArgs); + NewArg.setIsDefaulted(Arg.getIsDefaulted()); + return NewArg; } } @@ -9434,6 +9505,32 @@ 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; + + auto &Self = const_cast<ASTContext &>(*this); + llvm::FoldingSetNodeID ID; + DeducedTemplateStorage::Profile(ID, Self, 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. diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 0680ff5e3a385..18463d72514b1 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 temp... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/94981 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits