[clang] [Clang] Fix pack size handling when building alias deduction guides (PR #227964)
Akash Manna via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 30 23:00:20 PDT 2026
https://github.com/akash-manna-sky created https://github.com/llvm/llvm-project/pull/227964
Fixes #193217
When a deduction guide is built for an alias template, a pack that stays open is rewritten to a new pack, represented as a one-element argument pack holding a pack expansion. Its real length isn't known, but `getNumArgumentsInExpansionFromUnexpanded` took its size as 1. With `using AA = A<U>` the class pack `Ts` is empty, so `Ts (*...qs)[Ns]` saw sizes 0 and 1 and hit the "inconsistent pack sizes" assertion. On current trunk the rewrite of `Ts ...Ns` itself asserts even earlier, because it only handles packs renamed to other packs.
Such a pack is now treated as having no definite size, both in `getNumArgumentsInExpansionFromUnexpanded` and in `CheckParameterPacksForExpansion`, which also stops recording the placeholder 1 as the number of expansions. A constructor pack declared over a class pack of known size is expanded when the guide is built, as template instantiation does. A rewritten pack that is substituted without being expanded now refers to the new pack directly, so the enclosing expansion can still expand it later, and an expanded pack that is deduced empty is no longer substituted through its original type.
WIP.
>From ce0031655ff408b170cfe97cd36588c498009a22 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 3c6acf353f93f6f..cca6e9e6e89888a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -647,6 +647,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 5896cc721668d0d..57794c440a4da6c 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 eb3b908351448d5..721a7fcf05eeccd 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14730,6 +14730,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 1616e7e457dcea0..f985d5278676391 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 653240092e64a73..a921a7de2d3202f 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2989,8 +2989,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 fa0918ae30282bc..a3241aeed5298f7 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 06ea12ca7bd73bd..cb503aed1743a28 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 7e29de5be2e9704..b6ca3051fcc0316 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 bbac53b5b2acf27..11f18a7e0c0b33f 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 6517e88a8a83120..1055d8c4c86e19d 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 78911cbaed67be1..5b6ce1971c377ac 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 b3104609994a4e1..8b8db2889ce84fb 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