[clang] [clang-tools-extra] [clang] Implement CWG2398 provisional TTP matching to class templates (PR #94981)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 11 17:48:10 PDT 2024
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/94981
>From d12c7d50b67cd669f09b3701ccf34154876786c9 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Mon, 20 May 2024 01:15:03 -0300
Subject: [PATCH] [clang] Implement CWG2398 provisional TTP matching to class
templates
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.
---
clang-tools-extra/clangd/DumpAST.cpp | 1 +
.../clangd/SemanticHighlighting.cpp | 1 +
clang/include/clang/AST/ASTContext.h | 8 +-
clang/include/clang/AST/ASTImporter.h | 5 +
clang/include/clang/AST/DependenceFlags.h | 5 +
clang/include/clang/AST/PropertiesBase.td | 17 ++
clang/include/clang/AST/TemplateName.h | 59 ++++++-
clang/include/clang/Sema/Sema.h | 10 +-
clang/lib/AST/ASTContext.cpp | 129 ++++++++++++--
clang/lib/AST/ASTDiagnostic.cpp | 24 +--
clang/lib/AST/ASTImporter.cpp | 15 ++
clang/lib/AST/ASTStructuralEquivalence.cpp | 3 +
clang/lib/AST/ItaniumMangle.cpp | 11 ++
clang/lib/AST/ODRHash.cpp | 1 +
clang/lib/AST/TemplateName.cpp | 157 ++++++++++++++----
clang/lib/AST/TextNodeDumper.cpp | 12 ++
clang/lib/AST/Type.cpp | 3 +-
clang/lib/Sema/SemaTemplate.cpp | 63 +++++--
clang/lib/Sema/SemaTemplateDeduction.cpp | 128 ++++----------
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 24 +--
.../CXX/temp/temp.decls/temp.alias/p2.cpp | 5 +-
clang/test/CodeGenCXX/mangle-cwg2398.cpp | 11 ++
clang/test/SemaTemplate/cwg2398.cpp | 60 +++++--
clang/tools/libclang/CIndex.cpp | 3 +
clang/unittests/AST/ASTImporterTest.cpp | 17 ++
25 files changed, 564 insertions(+), 208 deletions(-)
create mode 100644 clang/test/CodeGenCXX/mangle-cwg2398.cpp
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 template arguments.
- static const TemplateSpecializationType *GetTemplateSpecializationType(
- ASTContext &Context, QualType Ty) {
+ static const TemplateSpecializationType *
+ GetTemplateSpecializationType(ASTContext &Context, QualType Ty) {
if (const TemplateSpecializationType *TST =
Ty->getAs<TemplateSpecializationType>())
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;
@@ -1427,20 +1427,22 @@ 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()) ==
+ Context.getCanonicalTemplateName(ToTST->getTemplateName());
}
/// 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 +1457,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 +2125,7 @@ class TemplateDiff {
return;
// Different base templates.
- if (!hasSameTemplate(FromOrigTST, ToOrigTST)) {
+ if (!hasSameTemplate(Context, FromOrigTST, ToOrigTST)) {
return;
}
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3b9080e09b331..a4cc58206e205 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -9887,6 +9887,21 @@ Expected<TemplateName> ASTImporter::Import(TemplateName From) {
return UsingOrError.takeError();
return TemplateName(cast<UsingShadowDecl>(*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<TemplateArgument, 8> 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 d56bf21b459e0..8f651f29a0d3f 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -636,6 +636,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/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index ed9e6eeb36c75..ae9bd194fecb0 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: {
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 1249531eab09f..2366cd5a441a6 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 11544dbb56e31..071043725f782 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<TemplateArgument *>(this + 1));
+}
+
+void DeducedTemplateStorage::Profile(llvm::FoldingSetNodeID &ID,
+ ASTContext &Context) {
+ Profile(ID, Context, Underlying, getDefaultArguments());
+}
+
+void DeducedTemplateStorage::Profile(llvm::FoldingSetNodeID &ID,
+ 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,61 @@ 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<Decl *>()) {
- if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(TemplateOrUsing))
- return cast<TemplateDecl>(USD->getTargetDecl());
+ TemplateName Name = *this;
+ while (std::optional<TemplateName> UnderlyingOrNone =
+ Name.desugar(/*IgnoreDeduced=*/false))
+ Name = *UnderlyingOrNone;
- assert(isa<TemplateDecl>(TemplateOrUsing));
+ if (Decl *TemplateOrUsing = Name.Storage.dyn_cast<Decl *>())
return cast<TemplateDecl>(TemplateOrUsing);
+ return nullptr;
+}
+
+std::pair<TemplateDecl *, DefaultArguments>
+TemplateName::getTemplateDeclAndDefaultArgs() const {
+ DefaultArguments DefArgs;
+ for (TemplateName Name = *this; /**/; /**/) {
+ if (!DefArgs && Name.getKind() == TemplateName::DeducedTemplate) {
+ DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName();
+ Name = DTS->getUnderlying();
+ DefArgs = DTS->getDefaultArguments();
+ continue;
+ }
+ if (std::optional<TemplateName> UnderlyingOrNone =
+ Name.desugar(/*IgnoreDeduced=*/DefArgs)) {
+ Name = *UnderlyingOrNone;
+ continue;
+ }
+ TemplateDecl *TD = Name.getAsTemplateDecl();
+ if (TD && DefArgs != 0)
+ assert(DefArgs.StartPos + DefArgs.Args.size() <=
+ TD->getTemplateParameters()->size());
+ return {TD, DefArgs};
}
+}
+std::optional<TemplateName> TemplateName::desugar(bool IgnoreDeduced) const {
+ if (Decl *D = Storage.dyn_cast<Decl *>()) {
+ if (auto *USD = dyn_cast<UsingShadowDecl>(D))
+ return TemplateName(USD->getTargetDecl());
+ return std::nullopt;
+ }
if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName())
- return QTN->getUnderlyingTemplate().getAsTemplateDecl();
-
- if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm())
- return sub->getReplacement().getAsTemplateDecl();
-
- if (UsingShadowDecl *USD = getAsUsingShadowDecl())
- return cast<TemplateDecl>(USD->getTargetDecl());
-
- return nullptr;
+ 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 +273,20 @@ UsingShadowDecl *TemplateName::getAsUsingShadowDecl() const {
return nullptr;
}
+DeducedTemplateStorage *TemplateName::getAsDeducedTemplateName() const {
+ if (UncommonTemplateNameStorage *Uncommon =
+ Storage.dyn_cast<UncommonTemplateNameStorage *>())
+ 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<TemplateTemplateParmDecl>(Template)) {
D |= TemplateNameDependence::DependentInstantiation;
if (TTP->isParameterPack())
@@ -246,10 +299,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 {
@@ -340,6 +424,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 1076dcd40a694..1dff796449421 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 33acae2cbafac..7efa89b4625e2 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4289,7 +4289,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<TemplateArgument *>(this + 1);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 40a759ea330de..fab43e7a1c0a6 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<FunctionTemplateDecl>(Template) ||
isa<VarTemplateDecl>(Template) || isa<ConceptDecl>(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<TemplateArgument, 4> 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<TemplateArgument, 4> 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<TemplateArgument, 4> SugaredConverted, CanonicalConverted;
if (CheckTemplateArgumentList(
Template, TemplateNameLoc,
- const_cast<TemplateArgumentListInfo &>(TemplateArgs), false,
- SugaredConverted, CanonicalConverted,
+ const_cast<TemplateArgumentListInfo &>(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<TemplateArgumentListInfo &>(*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<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied,
@@ -6638,9 +6643,29 @@ bool Sema::CheckTemplateArgumentList(
SmallVector<TemplateArgument, 2> 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<unsigned> 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<TemplateArgument, 4> 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<TemplateArgument, 4> 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 befeb38e1fe5b..bda9872f36e54 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -509,61 +509,6 @@ 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<TemplateTypeParmDecl>(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<NonTypeTemplateParmDecl>(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<TemplateTemplateParmDecl>(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,
@@ -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<TemplateTemplateParmDecl>(
- Arg.getAsTemplateDecl())) {
- assert(!TempArg->isExpandedParameterPack());
-
- TemplateParameterList *As = TempArg->getTemplateParameters();
- if (DefaultArguments.size() != 0) {
- assert(DefaultArguments.size() <= As->size());
- SmallVector<NamedDecl *, 4> 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<NamedDecl *> 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()];
@@ -3194,7 +3125,7 @@ FinishTemplateArgumentDeduction(
SmallVector<TemplateArgument, 4> 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 +3447,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 +5058,9 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
TemplateArgs.addArgument(TypeLoc.getArgLoc(I));
llvm::SmallVector<TemplateArgument, 4> 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);
@@ -6361,8 +6292,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
@@ -6410,8 +6341,9 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
// If the rewrite produces an invalid type, then P is not at least as
// specialized as A.
SmallVector<TemplateArgument, 4> 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 0681520764d9a..26b2fa793b48a 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<TemplateArgument, 4> 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<TemplateArgument, 4> 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<TemplateArgument, 4> 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<TemplateArgument, 4> 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 a5b39fe5c51f7..bc39431253880 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<template<class> class TT>
- void f(TT<int>); // expected-note {{candidate template ignored}}
+ void f(TT<int>);
template<template<class,class> class TT>
void g(TT<int, Alloc<int>>);
int h() {
- f(v); // expected-error {{no matching function for call to 'f'}}
+ f(v); // OK: TT = vector, Alloc<int> 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 0000000000000..a8a0aed17c70f
--- /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<class T, class U> struct A {};
+
+template<template<class> class TT> void f(TT<int>);
+
+// CHECK-LABEL: define{{.*}} void @_Z1zv(
+void z() {
+ f(A<int, double>());
+ // CHECK: call void @_Z1fITtTyE1A1IdEEvT_IiE()
+}
diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp
index 7675d4287cb88..3548a8f26f024 100644
--- a/clang/test/SemaTemplate/cwg2398.cpp
+++ b/clang/test/SemaTemplate/cwg2398.cpp
@@ -4,12 +4,11 @@
namespace issue1 {
template<class T, class U = T> class B {};
template<template<class> class P, class T> void f(P<T>);
- // new-note at -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 at -2 2{{template template argument has different template parameters}}
+ // old-note at -1 2{{template template argument has different template parameters}}
void g() {
f(B<int>()); // old-error {{no matching function for call}}
- f(B<int,float>()); // expected-error {{no matching function for call}}
+ f(B<int,float>()); // old-error {{no matching function for call}}
}
} // namespace issue1
@@ -65,13 +64,10 @@ namespace class_template {
template <class T3> struct B;
template <template <class T4> class TT1, class T5> struct B<TT1<T5>>;
- // new-note at -1 {{partial specialization matches}}
template <class T6, class T7> struct B<A<T6, T7>> {};
- // new-note at -1 {{partial specialization matches}}
template struct B<A<int>>;
- // new-error at -1 {{ambiguous partial specialization}}
} // namespace class_template
namespace type_pack1 {
@@ -272,16 +268,17 @@ namespace classes {
template<template<class> class TT> auto f(TT<int> a) { return a; }
// old-note at -1 2{{template template argument has different template parameters}}
- // new-note at -2 2{{substitution failure: too few template arguments}}
A<int, float> v1;
A<int, double> v2;
using X = decltype(f(v1));
- // expected-error at -1 {{no matching function for call}}
+ // old-error at -1 {{no matching function for call}}
+ // new-note at -2 {{previous definition is here}}
using X = decltype(f(v2));
- // expected-error at -1 {{no matching function for call}}
+ // old-error at -1 {{no matching function for call}}
+ // new-error at -2 {{different types ('decltype(f(v2))' (aka 'A:1<double><int>') vs 'decltype(f(v1))' (aka 'A:1<float><int>'))}}
} // namespace canon
namespace expr {
template <class T1, int E1> struct A {
@@ -289,12 +286,11 @@ namespace classes {
};
template <template <class T3> class TT> void f(TT<int> v) {
// old-note at -1 {{template template argument has different template parameters}}
- // new-note at -2 {{substitution failure: too few template arguments}}
static_assert(v.val == 3);
};
void test() {
f(A<int, 3>());
- // expected-error at -1 {{no matching function for call}}
+ // old-error at -1 {{no matching function for call}}
}
} // namespace expr
namespace packs {
@@ -304,14 +300,54 @@ namespace classes {
template <template <class T3> class TT> void f(TT<int> v) {
// old-note at -1 {{template template argument has different template parameters}}
- // new-note at -2 {{deduced type 'A<[...], (no argument), (no argument), (no argument)>' of 1st parameter does not match adjusted type 'A<[...], void, void, void>' of argument [with TT = A]}}
+ // new-note at -2 {{deduced type 'A:1<void, void, void><int>' of 1st parameter does not match adjusted type 'A<int, void, void, void>' of argument [with TT = A:1<void, void, void>]}}
static_assert(v.val == 3);
};
void test() {
+ // FIXME: Needs deduction of defaulted template parameter packs.
f(A<int, void, void, void>());
// expected-error at -1 {{no matching function for call}}
}
} // namespace packs
+ namespace nested {
+ template <class T1, int V1, int V2> struct A {
+ using type = T1;
+ static constexpr int v1 = V1, v2 = V2;
+ };
+
+ template <template <class T1> class TT1> auto f(TT1<int>) {
+ return TT1<float>();
+ }
+
+ template <template <class T2, int V3> class TT2> auto g(TT2<double, 1>) {
+ // old-note at -1 {{template template argument has different template parameters}}
+ return f(TT2<int, 2>());
+ }
+
+ using B = decltype(g(A<double, 1, 3>()));
+ // old-error at -1 {{no matching function for call}}
+
+ using X = B::type; // old-error {{undeclared identifier 'B'}}
+ using X = float;
+ static_assert(B::v1 == 2); // old-error {{undeclared identifier 'B'}}
+ static_assert(B::v2 == 3); // old-error {{undeclared identifier 'B'}}
+ }
+ namespace defaulted {
+ template <class T1, class T2 = T1*> struct A {
+ using type = T2;
+ };
+
+ template <template <class> class TT> TT<float> f(TT<int>);
+ // old-note at -1 2{{template template argument has different template parameters}}
+
+ using X = int*;
+ using X = decltype(f(A<int>()))::type;
+ // old-error at -1 {{no matching function for call}}
+
+ using Y = double*;
+ using Y = decltype(f(A<int, double*>()))::type;
+ // old-error at -1 {{no matching function for call}}
+ } // namespace defaulted
} // namespace classes
namespace regression1 {
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index dcd9555e1bfcc..bc438969d0cc0 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1557,6 +1557,9 @@ bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) {
return Visit(MakeCursorTemplateRef(
Name.getAsSubstTemplateTemplateParmPack()->getParameterPack(), Loc,
TU));
+
+ case TemplateName::DeducedTemplate:
+ llvm_unreachable("DeducedTemplate shouldn't appear in source");
}
llvm_unreachable("Invalid TemplateName::Kind!");
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 92f9bae6cb064..392ef9bd85a4a 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -1002,6 +1002,23 @@ TEST_P(ImportDecl, ImportUsingTemplate) {
hasAnyTemplateArgumentLoc(templateArgumentLoc())))))))));
}
+TEST_P(ImportDecl, ImportDeducedTemplateName) {
+ MatchVerifier<Decl> Verifier;
+ testImport(
+ R"(
+template <class, class> class A {};
+template <template <class> class TT> TT<char> f(TT<int>);
+void declToImport() {
+ using X = decltype(f(A<int, float>()));
+}
+)",
+ Lang_CXX17, "", Lang_CXX17, Verifier,
+ functionDecl(hasName("declToImport"),
+ hasDescendant(typeAliasDecl(
+ hasName("X"), hasUnderlyingType(hasCanonicalType(
+ asString("class A<char, float>")))))));
+}
+
TEST_P(ImportDecl, ImportUsingEnumDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { enum bar { baz, toto, quux }; }"
More information about the cfe-commits
mailing list