[clang] [Clang] Fix pack size handling when building alias deduction guides (PR #227964)
Akash Manna via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 23:39:59 PDT 2026
https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/227964
>From cf02221a4f9e01c48fe6a9c9c2c4d818667c6de2 Mon Sep 17 00:00:00 2001
From: Akash Manna <akash.manna.mymail at gmail.com>
Date: Thu, 1 Oct 2026 11:28:03 +0530
Subject: [PATCH] [Clang] Fix pack size handling when building alias deduction
guides
A pack that is rewritten to another pack is a one-element argument pack
holding an expansion of unknown length. Treat its size as unknown instead
of 1, expand constructor packs declared over a class pack of known size,
and refer to the rewritten pack directly when it is substituted without
being expanded.
Fixes #193217
---
clang/docs/ReleaseNotes.md | 14 ++
clang/include/clang/AST/TemplateBase.h | 4 +
clang/include/clang/Sema/Sema.h | 9 ++
clang/lib/AST/TemplateBase.cpp | 26 +++
clang/lib/Sema/SemaTemplateDeduction.cpp | 5 +-
clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 149 +++++++++++++++--
clang/lib/Sema/SemaTemplateInstantiate.cpp | 111 ++++++++-----
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 20 +--
clang/lib/Sema/SemaTemplateVariadic.cpp | 152 +++++++++++-------
clang/test/SemaCXX/ctad.cpp | 10 ++
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 54 +++++++
clang/test/SemaTemplate/pack-deduction.cpp | 8 +
12 files changed, 436 insertions(+), 126 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 6afaa6675be7cd..245decca6d56ee 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -672,6 +672,20 @@ features cannot lower the translation-unit ABI level;
- Fixed concept evaluation bugs where some declarations were not added to
the current instantiation scope. (#GH198052)
+- Fixed crashes when building the deduction guides of an alias template from a
+ constructor whose own template parameter packs are declared over, or expanded
+ together with, a parameter pack of the class template that the alias template
+ specifies. (#GH193217)
+
+- Fixed an assertion failure when an empty pack is deduced for a template
+ parameter pack that was itself expanded from another pack, such as `Ns` in
+ `template <Ts... Ns>` in a member template of a specialization whose `Ts` is
+ empty.
+
+- Fixed a rejected-valid case in CTAD from a constructor template with a
+ parameter such as `tuple<pair<Ts, Us>...>`, whose nested pack expansion
+ names packs of both the class template and the constructor template.
+
- Fixed a crash when a lambda parameter pack was given a default argument that
is a pack expansion referencing an enclosing function's parameter pack (e.g.
`[](Types... = args...) {}`). Clang now diagnoses the illegal default
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index f161f6561e742f..24e8b32f678b43 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -453,6 +453,10 @@ class TemplateArgument {
/// the pattern of the pack expansion.
TemplateArgument getPackExpansionPattern() const;
+ /// When the template argument is a pack expansion, returns the number of
+ /// expansions that it will produce, if known.
+ UnsignedOrNone getNumExpansions() const;
+
/// Print this template argument to the given output stream.
void print(const PrintingPolicy &Policy, raw_ostream &Out,
bool IncludeType) const;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 85ebe0cfdba1e8..badbb02bdd073e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14752,6 +14752,15 @@ class Sema final : public SemaBase {
void collectUnexpandedParameterPacks(
TypeLoc TL, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
+ /// Collect the set of unexpanded parameter packs within the given
+ /// template parameter list.
+ ///
+ /// \param Params The template parameter list that will be traversed to find
+ /// unexpanded parameter packs.
+ void collectUnexpandedParameterPacks(
+ TemplateParameterList *Params,
+ SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
+
/// Collect the set of unexpanded parameter packs within the given
/// nested-name-specifier.
///
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 9356bbadc7a48c..14dde2b9cb6d4d 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -536,6 +536,32 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const {
llvm_unreachable("Invalid TemplateArgument Kind!");
}
+UnsignedOrNone TemplateArgument::getNumExpansions() const {
+ assert(isPackExpansion());
+
+ switch (getKind()) {
+ case Type:
+ return getAsType()->castAs<PackExpansionType>()->getNumExpansions();
+
+ case Expression:
+ return cast<PackExpansionExpr>(getAsExpr())->getNumExpansions();
+
+ case TemplateExpansion:
+ return getNumTemplateExpansions();
+
+ case Declaration:
+ case Integral:
+ case StructuralValue:
+ case Pack:
+ case Null:
+ case Template:
+ case NullPtr:
+ return std::nullopt;
+ }
+
+ llvm_unreachable("Invalid TemplateArgument Kind!");
+}
+
void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
bool IncludeType) const {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 2257964a4310bb..b5724cdbc386d5 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2999,8 +2999,9 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param,
}
// If the pack is empty, we still need to substitute into the parameter
- // itself, in case that substitution fails.
- if (SugaredPackedArgsBuilder.empty()) {
+ // itself, in case that substitution fails. An expanded parameter pack was
+ // already checked when it was expanded.
+ if (SugaredPackedArgsBuilder.empty() && !getExpandedPackSize(Param)) {
LocalInstantiationScope Scope(S);
MultiLevelTemplateArgumentList Args(Template, CTAI.SugaredConverted,
/*Final=*/true);
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index fa0918ae30282b..a3241aeed5298f 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -337,16 +337,44 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC,
return NewTTP;
}
+// Whether all of the packs in `Unexpanded` have arguments of known size in
+// `Args`, as opposed to being rewritten to refer to another parameter pack.
+bool packsHaveKnownSize(ArrayRef<UnexpandedParameterPack> Unexpanded,
+ const MultiLevelTemplateArgumentList &Args) {
+ if (Unexpanded.empty())
+ return false;
+ for (UnexpandedParameterPack UPP : Unexpanded) {
+ if (isa_and_present<VarDecl>(dyn_cast<NamedDecl *>(UPP.first)))
+ return false;
+ std::optional<std::pair<unsigned, unsigned>> DepthAndIndex =
+ getDepthAndIndex(UPP);
+ if (!DepthAndIndex)
+ return false;
+ auto [Depth, Index] = *DepthAndIndex;
+ if (Depth >= Args.getNumLevels() || !Args.hasTemplateArgument(Depth, Index))
+ return false;
+ const TemplateArgument &Arg = Args(Depth, Index);
+ if (Arg.getKind() != TemplateArgument::Pack ||
+ llvm::any_of(Arg.pack_elements(), [](const TemplateArgument &TA) {
+ return TA.isPackExpansion() && !TA.getNumExpansions();
+ }))
+ return false;
+ }
+ return true;
+}
+
NonTypeTemplateParmDecl *
transformTemplateParam(Sema &SemaRef, DeclContext *DC,
NonTypeTemplateParmDecl *TTP, unsigned NewDepth,
unsigned NewIndex,
MultiLevelTemplateArgumentList &Args) {
- NonTypeTemplateParmDecl *NewTTP;
+ TypeSourceInfo *TSI = TTP->getTypeSourceInfo();
+ SmallVector<TypeSourceInfo *, 4> ExpandedTypeSourceInfos;
+ SmallVector<QualType, 4> ExpandedTypes;
+ bool IsExpandedParameterPack = false;
if (TTP->isExpandedParameterPack()) {
- SmallVector<TypeSourceInfo *, 4> ExpandedTypeSourceInfos(
- TTP->getNumExpansionTypes());
- SmallVector<QualType, 4> ExpandedTypes(TTP->getNumExpansionTypes());
+ ExpandedTypeSourceInfos.resize(TTP->getNumExpansionTypes());
+ ExpandedTypes.resize(TTP->getNumExpansionTypes());
for (unsigned I = 0, N = TTP->getNumExpansionTypes(); I != N; ++I) {
TypeSourceInfo *NewTSI =
SemaRef.SubstType(TTP->getExpansionTypeSourceInfo(I), Args,
@@ -360,13 +388,55 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC,
ExpandedTypeSourceInfos[I] = NewTSI;
ExpandedTypes[I] = NewT;
}
+ IsExpandedParameterPack = true;
+ } else if (TTP->isPackExpansion()) {
+ // If the packs named by the parameter's type have arguments of known size,
+ // expand the parameter as template instantiation would.
+ PackExpansionTypeLoc ExpansionTL =
+ TSI->getTypeLoc().castAs<PackExpansionTypeLoc>();
+ TypeLoc Pattern = ExpansionTL.getPatternLoc();
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ if (packsHaveKnownSize(Unexpanded, Args)) {
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions =
+ ExpansionTL.getTypePtr()->getNumExpansions();
+ if (SemaRef.CheckParameterPacksForExpansion(
+ ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(),
+ Unexpanded, Args, /*FailOnPackProducingTemplates=*/true, Expand,
+ RetainExpansion, NumExpansions))
+ return nullptr;
+ if (Expand) {
+ for (unsigned I = 0; I != *NumExpansions; ++I) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
+ TypeSourceInfo *NewTSI = SemaRef.SubstType(
+ Pattern, Args, TTP->getLocation(), TTP->getDeclName());
+ if (!NewTSI)
+ return nullptr;
+
+ QualType NewT = SemaRef.CheckNonTypeTemplateParameterType(
+ NewTSI, TTP->getLocation());
+ if (NewT.isNull())
+ return nullptr;
+
+ ExpandedTypeSourceInfos.push_back(NewTSI);
+ ExpandedTypes.push_back(NewT);
+ }
+ IsExpandedParameterPack = true;
+ }
+ }
+ }
+
+ NonTypeTemplateParmDecl *NewTTP;
+ if (IsExpandedParameterPack) {
NewTTP = NonTypeTemplateParmDecl::Create(
SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(), NewDepth,
- NewIndex, TTP->getIdentifier(), TTP->getType(),
- TTP->getTypeSourceInfo(), ExpandedTypes, ExpandedTypeSourceInfos);
+ NewIndex, TTP->getIdentifier(), TTP->getType(), TSI, ExpandedTypes,
+ ExpandedTypeSourceInfos);
} else {
- TypeSourceInfo *NewTSI = SemaRef.SubstType(
- TTP->getTypeSourceInfo(), Args, TTP->getLocation(), TTP->getDeclName());
+ TypeSourceInfo *NewTSI =
+ SemaRef.SubstType(TSI, Args, TTP->getLocation(), TTP->getDeclName());
assert(NewTSI);
QualType NewT =
@@ -378,11 +448,10 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC,
NewIndex, TTP->getIdentifier(), NewT, TTP->isParameterPack(), NewTSI);
}
- if (TypeSourceInfo *TSI = TTP->getTypeSourceInfo();
- AutoTypeLoc AutoLoc = TSI->getTypeLoc().getContainedAutoTypeLoc()) {
+ if (AutoTypeLoc AutoLoc = TSI->getTypeLoc().getContainedAutoTypeLoc()) {
if (AutoLoc.isConstrained()) {
SourceLocation EllipsisLoc;
- if (TTP->isExpandedParameterPack())
+ if (IsExpandedParameterPack)
EllipsisLoc =
TSI->getTypeLoc().getAs<PackExpansionTypeLoc>().getEllipsisLoc();
else if (auto *Constraint = dyn_cast_if_present<CXXFoldExpr>(
@@ -424,15 +493,53 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC,
TemplateTemplateParmDecl *TTP, unsigned NewDepth,
unsigned NewIndex, MultiLevelTemplateArgumentList &Args,
bool EvaluateConstraint) {
- TemplateTemplateParmDecl *NewTTP;
+ SmallVector<TemplateParameterList *, 4> ExpandedTPLs;
+ bool IsExpandedParameterPack = false;
if (TTP->isExpandedParameterPack()) {
- SmallVector<TemplateParameterList *, 4> ExpandedTPLs(
- TTP->getNumExpansionTemplateParameters());
+ ExpandedTPLs.resize(TTP->getNumExpansionTemplateParameters());
for (unsigned I = 0, N = TTP->getNumExpansionTemplateParameters(); I != N;
- ++I)
+ ++I) {
ExpandedTPLs[I] = transformTemplateParameters(
SemaRef, DC, TTP->getExpansionTemplateParameters(I), Args,
NewDepth + 1, EvaluateConstraint);
+ if (!ExpandedTPLs[I])
+ return nullptr;
+ }
+ IsExpandedParameterPack = true;
+ } else if (TTP->isPackExpansion()) {
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ SemaRef.collectUnexpandedParameterPacks(TTP->getTemplateParameters(),
+ Unexpanded);
+ if (packsHaveKnownSize(Unexpanded, Args)) {
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions = std::nullopt;
+ if (SemaRef.CheckParameterPacksForExpansion(
+ TTP->getLocation(),
+ TTP->getTemplateParameters()->getSourceRange(), Unexpanded, Args,
+ /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion,
+ NumExpansions))
+ return nullptr;
+ if (Expand) {
+ for (unsigned I = 0; I != *NumExpansions; ++I) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(SemaRef, I);
+ // The same template parameters are transformed for each expansion.
+ LocalInstantiationScope Scope(SemaRef,
+ /*CombineWithOuterScope=*/true);
+ TemplateParameterList *Expansion = transformTemplateParameters(
+ SemaRef, DC, TTP->getTemplateParameters(), Args, NewDepth + 1,
+ EvaluateConstraint);
+ if (!Expansion)
+ return nullptr;
+ ExpandedTPLs.push_back(Expansion);
+ }
+ IsExpandedParameterPack = true;
+ }
+ }
+ }
+
+ TemplateTemplateParmDecl *NewTTP;
+ if (IsExpandedParameterPack) {
NewTTP = TemplateTemplateParmDecl::Create(
SemaRef.Context, DC, TTP->getLocation(), NewDepth, NewIndex,
TTP->getIdentifier(), TTP->templateParameterKind(),
@@ -442,6 +549,8 @@ transformTemplateParam(Sema &SemaRef, DeclContext *DC,
TemplateParameterList *NewTPL =
transformTemplateParameters(SemaRef, DC, TTP->getTemplateParameters(),
Args, NewDepth + 1, EvaluateConstraint);
+ if (!NewTPL)
+ return nullptr;
NewTTP = TemplateTemplateParmDecl::Create(
SemaRef.Context, DC, TTP->getLocation(), NewDepth, NewIndex,
TTP->isParameterPack(), TTP->getIdentifier(),
@@ -489,6 +598,8 @@ transformTemplateParameters(Sema &SemaRef, DeclContext *DC,
Params[I] = transformTemplateParameter(SemaRef, DC, TPL->getParam(I), Args,
/*NewIndex=*/I, NewDepth,
EvaluateConstraint);
+ if (!Params[I])
+ return nullptr;
}
return TemplateParameterList::Create(
SemaRef.Context, TPL->getTemplateLoc(), TPL->getLAngleLoc(), Params,
@@ -1028,6 +1139,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
/*NewIndex=*/N++, getDepthAndIndex(TP).first + AdjustDepth);
+ if (!NewParam)
+ return nullptr;
TemplateArgument NewTemplateArgument =
Context.getInjectedTemplateArg(NewParam);
@@ -1054,6 +1167,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, F->getDeclContext(), TP, Args,
/*NewIndex=*/N++, getDepthAndIndex(TP).first + AdjustDepth);
+ if (!NewParam)
+ return nullptr;
assert(TemplateArgsForBuildingRC[Index].isNull());
TemplateArgsForBuildingRC[Index] =
Context.getInjectedTemplateArg(NewParam);
@@ -1352,6 +1467,8 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias(
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, AliasTemplate->getDeclContext(), TP, Args,
/*NewIndex=*/FPrimeTemplateParams.size(), getDepthAndIndex(TP).first);
+ if (!NewParam)
+ return nullptr;
FPrimeTemplateParams.push_back(NewParam);
TemplateArgument NewTemplateArgument =
@@ -1428,6 +1545,8 @@ CXXDeductionGuideDecl *BuildDeductionGuideForTypeAlias(
NamedDecl *NewParam = transformTemplateParameter(
SemaRef, F->getDeclContext(), TP, Args, FPrimeTemplateParams.size(),
getDepthAndIndex(TP).first);
+ if (!NewParam)
+ return nullptr;
FPrimeTemplateParams.push_back(NewParam);
assert(TemplateArgsForBuildingFPrime[FTemplateParamIdx].isNull() &&
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 06ea12ca7bd73b..cb503aed1743a2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2123,6 +2123,20 @@ VarDecl *TemplateInstantiator::RebuildObjCExceptionDecl(VarDecl *ExceptionDecl,
return Var;
}
+/// If the given argument pack is a single pack expansion of unknown length,
+/// returns its pattern. A reference to the parameter pack that isn't being
+/// expanded is substituted by that pattern, as the enclosing pack expansion
+/// expands the packs it names.
+static std::optional<TemplateArgument>
+getLoneUnexpandedPackExpansionPattern(const TemplateArgument &Pack) {
+ if (Pack.pack_size() != 1)
+ return std::nullopt;
+ const TemplateArgument &Arg = *Pack.pack_begin();
+ if (!Arg.isPackExpansion() || Arg.getNumExpansions())
+ return std::nullopt;
+ return Arg.getPackExpansionPattern();
+}
+
TemplateName TemplateInstantiator::TransformTemplateName(
NestedNameSpecifierLoc &QualifierLoc, SourceLocation TemplateKWLoc,
TemplateName Name, SourceLocation NameLoc, QualType ObjectType,
@@ -2161,15 +2175,20 @@ TemplateName TemplateInstantiator::TransformTemplateName(
"Missing argument pack");
if (!getSema().ArgPackSubstIndex) {
- // We have the template argument pack to substitute, but we're not
- // actually expanding the enclosing pack expansion yet. So, just
- // keep the entire argument pack.
- return getSema().Context.getSubstTemplateTemplateParmPack(
- Arg, AssociatedDecl, TTP->getIndex(), Final);
+ std::optional<TemplateArgument> Pattern =
+ getLoneUnexpandedPackExpansionPattern(Arg);
+ if (!Pattern) {
+ // We have the template argument pack to substitute, but we're not
+ // actually expanding the enclosing pack expansion yet. So, just
+ // keep the entire argument pack.
+ return getSema().Context.getSubstTemplateTemplateParmPack(
+ Arg, AssociatedDecl, TTP->getIndex(), Final);
+ }
+ Arg = *Pattern;
+ } else {
+ PackIndex = SemaRef.getPackIndex(Arg);
+ Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
}
-
- PackIndex = SemaRef.getPackIndex(Arg);
- Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
}
TemplateName Template = Arg.getAsTemplate();
@@ -2234,11 +2253,28 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
return Arg.getAsExpr();
}
- QualType ParamType = NTTP->isExpandedParameterPack()
- ? NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex)
- : NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex
- ? NTTP->getType().getNonPackExpansionType()
- : NTTP->getType();
+ bool IsParameterPack =
+ NTTP->isParameterPack() ||
+ // In concept parameter mapping for fold expressions, packs that aren't
+ // expanded in place are treated as having non-pack dependency, so that
+ // a PackExpansionType won't prevent expanding the packs outside the
+ // TreeTransform. However, we still need to unpack the arguments during
+ // any template argument substitution, so we also check its FoundDecl.
+ (E->getFoundDecl() && E->getFoundDecl() != E->getDecl() &&
+ E->getFoundDecl()->isParameterPack());
+
+ std::optional<TemplateArgument> PackPattern;
+ if (IsParameterPack && !SemaRef.ArgPackSubstIndex) {
+ assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack");
+ PackPattern = getLoneUnexpandedPackExpansionPattern(Arg);
+ }
+
+ QualType ParamType =
+ NTTP->isExpandedParameterPack()
+ ? NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex)
+ : NTTP->isParameterPack() && (SemaRef.ArgPackSubstIndex || PackPattern)
+ ? NTTP->getType().getNonPackExpansionType()
+ : NTTP->getType();
ParamType = SemaRef.SubstType(ParamType, TemplateArgs, E->getLocation(),
NTTP->getDeclName());
assert(!ParamType.isNull() && "Shouldn't substitute to an invalid type");
@@ -2246,17 +2282,12 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
auto [AssociatedDecl, Final] =
TemplateArgs.getAssociatedDecl(NTTP->getDepth());
UnsignedOrNone PackIndex = std::nullopt;
- if (NTTP->isParameterPack() ||
- // In concept parameter mapping for fold expressions, packs that aren't
- // expanded in place are treated as having non-pack dependency, so that
- // a PackExpansionType won't prevent expanding the packs outside the
- // TreeTransform. However, we still need to unpack the arguments during
- // any template argument substitution, so we also check its FoundDecl.
- (E->getFoundDecl() && E->getFoundDecl() != E->getDecl() &&
- E->getFoundDecl()->isParameterPack())) {
+ if (IsParameterPack) {
assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack");
- if (!getSema().ArgPackSubstIndex) {
+ if (PackPattern) {
+ Arg = *PackPattern;
+ } else if (!getSema().ArgPackSubstIndex) {
// We have an argument pack, but we can't select a particular argument
// out of it yet. Therefore, we'll build an expression to hold on to that
// argument pack.
@@ -2266,9 +2297,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
return new (SemaRef.Context) SubstNonTypeTemplateParmPackExpr(
ExprType, ParamType->isReferenceType() ? VK_LValue : VK_PRValue,
E->getLocation(), Arg, AssociatedDecl, NTTP->getPosition(), Final);
+ } else {
+ PackIndex = SemaRef.getPackIndex(Arg);
+ Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
}
- PackIndex = SemaRef.getPackIndex(Arg);
- Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
}
return SemaRef.BuildSubstNonTypeTemplateParmExpr(
AssociatedDecl, NTTP->getPosition(), ParamType, E->getLocation(), Arg,
@@ -2592,20 +2624,25 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
"Missing argument pack");
if (!getSema().ArgPackSubstIndex) {
- // We have the template argument pack, but we're not expanding the
- // enclosing pack expansion yet. Just save the template argument
- // pack for later substitution.
- QualType Result = getSema().Context.getSubstTemplateTypeParmPackType(
- AssociatedDecl, T->getIndex(), Final, Arg);
- SubstTemplateTypeParmPackTypeLoc NewTL
- = TLB.push<SubstTemplateTypeParmPackTypeLoc>(Result);
- NewTL.setNameLoc(TL.getNameLoc());
- return Result;
+ std::optional<TemplateArgument> Pattern =
+ getLoneUnexpandedPackExpansionPattern(Arg);
+ if (!Pattern) {
+ // We have the template argument pack, but we're not expanding the
+ // enclosing pack expansion yet. Just save the template argument
+ // pack for later substitution.
+ QualType Result = getSema().Context.getSubstTemplateTypeParmPackType(
+ AssociatedDecl, T->getIndex(), Final, Arg);
+ SubstTemplateTypeParmPackTypeLoc NewTL =
+ TLB.push<SubstTemplateTypeParmPackTypeLoc>(Result);
+ NewTL.setNameLoc(TL.getNameLoc());
+ return Result;
+ }
+ Arg = *Pattern;
+ } else {
+ // PackIndex starts from last element.
+ PackIndex = SemaRef.getPackIndex(Arg);
+ Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
}
-
- // PackIndex starts from last element.
- PackIndex = SemaRef.getPackIndex(Arg);
- Arg = SemaRef.getPackSubstitutedTemplateArgument(Arg);
}
assert(Arg.getKind() == TemplateArgument::Type &&
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7e29de5be2e970..b6ca3051fcc031 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4037,22 +4037,6 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl(
return Param;
}
-static void collectUnexpandedParameterPacks(
- Sema &S,
- TemplateParameterList *Params,
- SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
- for (const auto &P : *Params) {
- if (P->isTemplateParameterPack())
- continue;
- if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P))
- S.collectUnexpandedParameterPacks(NTTP->getTypeSourceInfo()->getTypeLoc(),
- Unexpanded);
- if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(P))
- collectUnexpandedParameterPacks(S, TTP->getTemplateParameters(),
- Unexpanded);
- }
-}
-
Decl *
TemplateDeclInstantiator::VisitTemplateTemplateParmDecl(
TemplateTemplateParmDecl *D) {
@@ -4085,8 +4069,8 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl(
// template parameters. Determine whether we need to expand this parameter
// pack into separate parameters.
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- collectUnexpandedParameterPacks(SemaRef, D->getTemplateParameters(),
- Unexpanded);
+ SemaRef.collectUnexpandedParameterPacks(D->getTemplateParameters(),
+ Unexpanded);
// Determine whether the set of unexpanded parameter packs can and should
// be expanded.
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 2d6e75bd77b402..83d72a3f19a993 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -688,6 +688,20 @@ void Sema::collectUnexpandedParameterPacks(QualType T,
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(T);
}
+void Sema::collectUnexpandedParameterPacks(
+ TemplateParameterList *Params,
+ SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
+ for (NamedDecl *P : *Params) {
+ if (P->isTemplateParameterPack())
+ continue;
+ if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P))
+ collectUnexpandedParameterPacks(NTTP->getTypeSourceInfo()->getTypeLoc(),
+ Unexpanded);
+ else if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P))
+ collectUnexpandedParameterPacks(TTP->getTemplateParameters(), Unexpanded);
+ }
+}
+
void Sema::collectUnexpandedParameterPacks(
TemplateName Template,
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
@@ -863,6 +877,10 @@ bool Sema::CheckParameterPacksForExpansion(
RetainExpansion = false;
IdentifierLoc FirstPack;
bool HaveFirstPack = false;
+ UnsignedOrNone OrigNumExpansions = NumExpansions;
+ // Set if NumExpansions comes from a pack with pack expansions of unknown
+ // length: the least number of arguments that pack expands to.
+ UnsignedOrNone LeastTentativeNumExpansions = std::nullopt;
UnsignedOrNone NumPartialExpansions = std::nullopt;
SourceLocation PartiallySubstitutedPackLoc;
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
@@ -960,19 +978,7 @@ bool Sema::CheckParameterPacksForExpansion(
NewPackSize = Pack.size();
PendingPackExpansionSize =
llvm::count_if(Pack, [](const TemplateArgument &TA) {
- if (!TA.isPackExpansion())
- return false;
-
- if (TA.getKind() == TemplateArgument::Type)
- return !TA.getAsType()
- ->castAs<PackExpansionType>()
- ->getNumExpansions();
-
- if (TA.getKind() == TemplateArgument::Expression)
- return !cast<PackExpansionExpr>(TA.getAsExpr())
- ->getNumExpansions();
-
- return !TA.getNumTemplateExpansions();
+ return TA.isPackExpansion() && !TA.getNumExpansions();
});
}
@@ -1001,6 +1007,8 @@ bool Sema::CheckParameterPacksForExpansion(
NumExpansions = NewPackSize;
FirstPack = IdentifierLoc(ParmPack.second, Name);
HaveFirstPack = true;
+ if (PendingPackExpansionSize)
+ LeastTentativeNumExpansions = NewPackSize - PendingPackExpansionSize;
continue;
}
@@ -1029,6 +1037,19 @@ bool Sema::CheckParameterPacksForExpansion(
unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
if (PendingPackExpansionSize && LeastNewPackSize <= *NumExpansions) {
ShouldExpand = false;
+ if (LeastTentativeNumExpansions)
+ LeastTentativeNumExpansions =
+ std::max(*LeastTentativeNumExpansions, LeastNewPackSize);
+ continue;
+ }
+ // Likewise if the pack NumExpansions comes from may still match this one.
+ if (LeastTentativeNumExpansions &&
+ *LeastTentativeNumExpansions <= NewPackSize) {
+ ShouldExpand = false;
+ if (!PendingPackExpansionSize) {
+ NumExpansions = NewPackSize;
+ LeastTentativeNumExpansions = std::nullopt;
+ }
continue;
}
// C++0x [temp.variadic]p5:
@@ -1036,19 +1057,32 @@ bool Sema::CheckParameterPacksForExpansion(
// the same number of arguments specified.
if (!Diagnose)
;
- else if (HaveFirstPack)
+ else if (!HaveFirstPack)
+ Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
+ << Name << *NumExpansions << (LeastNewPackSize != NewPackSize)
+ << LeastNewPackSize << SourceRange(ParmPack.second);
+ else if (LeastTentativeNumExpansions)
+ Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
+ << Name << FirstPack.getIdentifierInfo() << NewPackSize
+ << (*LeastTentativeNumExpansions != *NumExpansions)
+ << *LeastTentativeNumExpansions << SourceRange(ParmPack.second)
+ << SourceRange(FirstPack.getLoc());
+ else
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
<< FirstPack.getIdentifierInfo() << Name << *NumExpansions
<< (LeastNewPackSize != NewPackSize) << LeastNewPackSize
<< SourceRange(FirstPack.getLoc()) << SourceRange(ParmPack.second);
- else
- Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
- << Name << *NumExpansions << (LeastNewPackSize != NewPackSize)
- << LeastNewPackSize << SourceRange(ParmPack.second);
return true;
}
+
+ if (!PendingPackExpansionSize)
+ LeastTentativeNumExpansions = std::nullopt;
}
+ // Only report a tentative NumExpansions when expanding.
+ if (!ShouldExpand && LeastTentativeNumExpansions)
+ NumExpansions = OrigNumExpansions;
+
// If we're performing a partial expansion but we also have a full expansion,
// expand to the number of common arguments. For example, given:
//
@@ -1080,59 +1114,69 @@ UnsignedOrNone Sema::getNumArgumentsInExpansionFromUnexpanded(
llvm::ArrayRef<UnexpandedParameterPack> Unexpanded,
const MultiLevelTemplateArgumentList &TemplateArgs) {
UnsignedOrNone Result = std::nullopt;
+ bool ResultIsDefinite = false;
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
- // Compute the depth and index for this parameter pack.
- unsigned Depth;
- unsigned Index;
+ unsigned Size;
+ bool SizeIsDefinite = true;
- if (const TemplateTypeParmType *TTP =
- dyn_cast<const TemplateTypeParmType *>(Unexpanded[I].first)) {
- Depth = TTP->getDepth();
- Index = TTP->getIndex();
- } else if (auto *TST = dyn_cast<const TemplateSpecializationType *>(
- Unexpanded[I].first)) {
+ if (auto *TST =
+ dyn_cast<const TemplateSpecializationType *>(Unexpanded[I].first)) {
// This is a dependent pack, we are not ready to expand it yet.
assert(isPackProducingBuiltinTemplateName(TST->getTemplateName()));
(void)TST;
return std::nullopt;
} else if (auto *PST = dyn_cast<const SubstBuiltinTemplatePackType *>(
Unexpanded[I].first)) {
- assert((!Result || *Result == PST->getNumArgs()) &&
- "inconsistent pack sizes");
- Result = PST->getNumArgs();
- continue;
- } else {
- NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
- if (isa<VarDecl>(ND)) {
- // Function parameter pack or init-capture pack.
- typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
+ Size = PST->getNumArgs();
+ } else if (auto *ND = dyn_cast<NamedDecl *>(Unexpanded[I].first);
+ ND && isa<VarDecl>(ND)) {
+ // Function parameter pack or init-capture pack.
+ typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
- llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
- CurrentInstantiationScope->findInstantiationOf(
- cast<NamedDecl *>(Unexpanded[I].first));
- if (isa<Decl *>(*Instantiation))
- // The pattern refers to an unexpanded pack. We're not ready to expand
- // this pack yet.
- return std::nullopt;
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
+ CurrentInstantiationScope->findInstantiationOf(ND);
+ if (isa<Decl *>(*Instantiation))
+ // The pattern refers to an unexpanded pack. We're not ready to expand
+ // this pack yet.
+ return std::nullopt;
- unsigned Size = cast<DeclArgumentPack *>(*Instantiation)->size();
- assert((!Result || *Result == Size) && "inconsistent pack sizes");
- Result = Size;
- continue;
+ Size = cast<DeclArgumentPack *>(*Instantiation)->size();
+ } else {
+ // Compute the depth and index for this parameter pack.
+ unsigned Depth;
+ unsigned Index;
+ if (const TemplateTypeParmType *TTP =
+ dyn_cast<const TemplateTypeParmType *>(Unexpanded[I].first)) {
+ Depth = TTP->getDepth();
+ Index = TTP->getIndex();
+ } else {
+ std::tie(Depth, Index) =
+ getDepthAndIndex(cast<NamedDecl *>(Unexpanded[I].first));
}
- std::tie(Depth, Index) = getDepthAndIndex(ND);
+ if (Depth >= TemplateArgs.getNumLevels() ||
+ !TemplateArgs.hasTemplateArgument(Depth, Index))
+ // The pattern refers to an unknown template argument. We're not ready
+ // to expand this pack yet.
+ return std::nullopt;
+
+ // Determine the size of the argument pack. It isn't definite if the
+ // pack contains pack expansions of unknown length.
+ ArrayRef<TemplateArgument> Pack =
+ TemplateArgs(Depth, Index).getPackAsArray();
+ Size = Pack.size();
+ SizeIsDefinite = llvm::none_of(Pack, [](const TemplateArgument &TA) {
+ return TA.isPackExpansion() && !TA.getNumExpansions();
+ });
}
- if (Depth >= TemplateArgs.getNumLevels() ||
- !TemplateArgs.hasTemplateArgument(Depth, Index))
- // The pattern refers to an unknown template argument. We're not ready to
- // expand this pack yet.
+
+ // A pack of indefinite size may still match the others once substituted.
+ if (Result && *Result != Size && (!SizeIsDefinite || !ResultIsDefinite))
return std::nullopt;
- // Determine the size of the argument pack.
- unsigned Size = TemplateArgs(Depth, Index).pack_size();
assert((!Result || *Result == Size) && "inconsistent pack sizes");
Result = Size;
+ ResultIsDefinite |= SizeIsDefinite;
}
return Result;
diff --git a/clang/test/SemaCXX/ctad.cpp b/clang/test/SemaCXX/ctad.cpp
index 6517e88a8a8312..1055d8c4c86e19 100644
--- a/clang/test/SemaCXX/ctad.cpp
+++ b/clang/test/SemaCXX/ctad.cpp
@@ -226,3 +226,13 @@ using AliasName = A<T, Ts...>;
AliasName aa([](int){}, 0);
}
+
+namespace NestedPackExpansionInConstructor {
+template <class...> struct tuple {};
+template <class, class> struct pair {};
+template <class... Ts> struct A {
+ template <class... Us> A(tuple<pair<Ts, Us>...>);
+};
+A a(tuple<pair<int, char>, pair<long, float>>{});
+static_assert(__is_same(decltype(a), A<int, long>));
+} // namespace NestedPackExpansionInConstructor
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 78911cbaed67be..5b6ce1971c377a 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -629,3 +629,57 @@ template <typename T> using S3 = S2<T>; // expected-note {{candidate function no
// expected-note {{cannot deduce template arguments for 'GH190517::S3' from 'GH190517::S1<char>'}}
S3 foo(42); // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'S3'}}
}
+
+namespace GH193217 {
+
+template <auto...> struct X {};
+
+template <typename T, typename... Ts> struct A {
+ template <Ts... Ns, T *...Ps> A(X<Ps...>, Ts (*...qs)[Ns]);
+};
+
+template <class T = int, class U = T> using AA = A<U>; // #GH193217_AA
+
+AA a{};
+// expected-error at -1 {{no viable constructor or deduction guide for deduction of template arguments of 'AA'}}
+// expected-note@#GH193217_AA 2{{candidate function template not viable}}
+// expected-note@#GH193217_AA 2{{implicit deduction guide declared as}}
+
+int *p;
+AA a1{X<&p>{}};
+static_assert(__is_same(decltype(a1), A<int *>));
+
+template <class T = int, class U = T> using AB = A<U, int>;
+int arr[3];
+AB b{X<&p>{}, &arr};
+static_assert(__is_same(decltype(b), A<int *, int>));
+
+template <class T = int, class U = T> using AC = A<U, int, long>;
+long arr2[4];
+AC c{X<&p>{}, &arr, &arr2};
+static_assert(__is_same(decltype(c), A<int *, int, long>));
+
+template <auto, class> struct Y {};
+template <class T, class... Ts> struct B {
+ template <Ts... Ns> B(T, Y<Ns, Ts>...);
+};
+template <class U> using BB = B<U>;
+BB bb{0};
+static_assert(__is_same(decltype(bb), B<int>));
+
+template <class, class> struct P {};
+template <class T, class... Ts> struct C {
+ template <class... Us> C(T, P<Ts, Us>...);
+};
+template <class U> using CC = C<U>;
+CC cc{0};
+static_assert(__is_same(decltype(cc), C<int>));
+
+template <class T, class... Ts> struct D {
+ template <template <Ts> class... TTs> D(T);
+};
+template <class U> using DD = D<U>;
+DD dd{0};
+static_assert(__is_same(decltype(dd), D<int>));
+
+} // namespace GH193217
diff --git a/clang/test/SemaTemplate/pack-deduction.cpp b/clang/test/SemaTemplate/pack-deduction.cpp
index b3104609994a4e..8b8db2889ce84f 100644
--- a/clang/test/SemaTemplate/pack-deduction.cpp
+++ b/clang/test/SemaTemplate/pack-deduction.cpp
@@ -258,3 +258,11 @@ template <int... Args> using Alias = Nttp<1, 2, 3>::B<Args...>;
}
}
+
+namespace ExpandedPackDeducedEmpty {
+template <int...> struct X {};
+template <typename... Ts> struct A {
+ template <int N, Ts... Ns> A(X<N>, X<Ns...>);
+};
+A<> a{X<1>{}, X<>{}};
+}
More information about the cfe-commits
mailing list