[clang] Reapply "[Clang] Improve diagnostics for expansion length mismatch" (PR #121044)
Younan Zhang via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 31 05:17:56 PDT 2025
https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/121044
>From 77537d523bc164a86b46e83651500a4b37c0c3bf Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 24 Dec 2024 13:06:44 +0800
Subject: [PATCH 1/9] Reapply "[Clang] Improve diagnostics for expansion length
mismatch"
... and "[Clang] fix missing initialization of original number of expansions"
This reverts commit acecf68c8b7c3c625cfa00f00f8ddc8f15baae44.
Co-authored-by: Matheus Izvekov <mizvekov at gmail.com>
---
clang/include/clang/Sema/Sema.h | 8 +-
clang/include/clang/Sema/SemaInternal.h | 2 +-
clang/lib/AST/ExprCXX.cpp | 2 +-
clang/lib/Sema/SemaDeclCXX.cpp | 2 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 7 +-
clang/lib/Sema/SemaTemplateVariadic.cpp | 340 +++++++++---------
clang/lib/Sema/TreeTransform.h | 1 +
.../CXX/temp/temp.decls/temp.variadic/p5.cpp | 50 +++
.../SemaTemplate/cxx1z-fold-expressions.cpp | 2 +-
clang/test/SemaTemplate/pack-deduction.cpp | 51 ++-
10 files changed, 285 insertions(+), 180 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9ac26d8728446..34f99b8adf467 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -232,9 +232,11 @@ void threadSafetyCleanup(BeforeSet *Cache);
// FIXME: No way to easily map from TemplateTypeParmTypes to
// TemplateTypeParmDecls, so we have this horrible PointerUnion.
-typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *>,
- SourceLocation>
- UnexpandedParameterPack;
+using UnexpandedParameterPack = std::pair<
+ llvm::PointerUnion<
+ const TemplateTypeParmType *, const SubstTemplateTypeParmPackType *,
+ const SubstNonTypeTemplateParmPackExpr *, const NamedDecl *>,
+ SourceLocation>;
/// Describes whether we've seen any nullability information for the given
/// file.
diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h
index 95874077050a9..acf6c8146d70d 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -75,7 +75,7 @@ getDepthAndIndex(UnexpandedParameterPack UPP) {
if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
return std::make_pair(TTP->getDepth(), TTP->getIndex());
- return getDepthAndIndex(cast<NamedDecl *>(UPP.first));
+ return getDepthAndIndex(cast<const NamedDecl *>(UPP.first));
}
class TypoCorrectionConsumer : public VisibleDeclConsumer {
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index c8d61e2cf3f26..afa3db950284b 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1734,7 +1734,7 @@ PackIndexingExpr *PackIndexingExpr::Create(
NamedDecl *PackIndexingExpr::getPackDecl() const {
if (auto *D = dyn_cast<DeclRefExpr>(getPackIdExpression()); D) {
NamedDecl *ND = dyn_cast<NamedDecl>(D->getDecl());
- assert(ND && "exected a named decl");
+ assert(ND && "expected a named decl");
return ND;
}
assert(false && "invalid declaration kind in pack indexing expression");
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 96aac7871db1e..756937f44e948 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17589,7 +17589,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
for (UnexpandedParameterPack &U : Unexpanded) {
if (getDepthAndIndex(U).first >= FriendDeclDepth) {
- auto *ND = dyn_cast<NamedDecl *>(U.first);
+ auto *ND = dyn_cast<const NamedDecl *>(U.first);
if (!ND)
ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
Diag(U.second, diag::friend_template_decl_malformed_pack_expansion)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e6ec4a7178e81..6af842121ae79 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -874,8 +874,11 @@ class PackDeductionScope {
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
- unsigned Depth, Index;
- std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]);
+ UnexpandedParameterPack U = Unexpanded[I];
+ if (isa<const SubstTemplateTypeParmPackType *,
+ const SubstNonTypeTemplateParmPackExpr *>(U.first))
+ continue;
+ auto [Depth, Index] = getDepthAndIndex(U);
if (Depth == Info.getDeducedDepth())
AddPack(Index);
}
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d9256dbd07d7a..d6c71ecec13b6 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -38,7 +38,7 @@ class CollectUnexpandedParameterPacksVisitor
unsigned DepthLimit = (unsigned)-1;
#ifndef NDEBUG
- bool ContainsIntermediatePacks = false;
+ bool ContainsIntermediatePacks = false;
#endif
void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
@@ -97,6 +97,33 @@ class CollectUnexpandedParameterPacksVisitor
return true;
}
+ bool VisitSubstTemplateTypeParmPackTypeLoc(
+ SubstTemplateTypeParmPackTypeLoc TL) override {
+ Unexpanded.push_back({TL.getTypePtr(), TL.getNameLoc()});
+#ifdef NDEBUG
+ ContainsIntermediatePacks = true;
+#endif
+ return true;
+ }
+
+ bool VisitSubstTemplateTypeParmPackType(
+ SubstTemplateTypeParmPackType *T) override {
+ Unexpanded.push_back({T, SourceLocation()});
+#ifdef NDEBUG
+ ContainsIntermediatePacks = true;
+#endif
+ return true;
+ }
+
+ bool VisitSubstNonTypeTemplateParmPackExpr(
+ SubstNonTypeTemplateParmPackExpr *E) override {
+ Unexpanded.push_back({E, E->getParameterPackLocation()});
+#ifdef NDEBUG
+ ContainsIntermediatePacks = true;
+#endif
+ return true;
+ }
+
/// Record occurrences of function and non-type template
/// parameter packs in an expression.
bool VisitDeclRefExpr(DeclRefExpr *E) override {
@@ -315,24 +342,6 @@ class CollectUnexpandedParameterPacksVisitor
return true;
}
- bool TraverseSubstNonTypeTemplateParmPackExpr(
- SubstNonTypeTemplateParmPackExpr *) override {
- ContainsIntermediatePacks = true;
- return true;
- }
-
- bool VisitSubstTemplateTypeParmPackType(
- SubstTemplateTypeParmPackType *) override {
- ContainsIntermediatePacks = true;
- return true;
- }
-
- bool VisitSubstTemplateTypeParmPackTypeLoc(
- SubstTemplateTypeParmPackTypeLoc) override {
- ContainsIntermediatePacks = true;
- return true;
- }
-
bool containsIntermediatePacks() const { return ContainsIntermediatePacks; }
#endif
};
@@ -374,7 +383,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack);
return TTPD && TTPD->getTypeForDecl() == TTPT;
}
- return declaresSameEntity(cast<NamedDecl *>(Pack.first), LocalPack);
+ return declaresSameEntity(cast<const NamedDecl *>(Pack.first),
+ LocalPack);
};
if (llvm::any_of(CSI->LocalPacks, DeclaresThisPack))
ParamPackReferences.push_back(Pack);
@@ -425,8 +435,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
if (const TemplateTypeParmType *TTP
= Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>())
Name = TTP->getIdentifier();
- else if (NamedDecl *ND = Unexpanded[I].first.dyn_cast<NamedDecl *>())
- Name = ND->getIdentifier();
+ else
+ Name = cast<const NamedDecl *>(Unexpanded[I].first)->getIdentifier();
if (Name && NamesKnown.insert(Name).second)
Names.push_back(Name);
@@ -500,7 +510,7 @@ bool Sema::DiagnoseUnexpandedParameterPackInRequiresExpr(RequiresExpr *RE) {
llvm::SmallPtrSet<NamedDecl*, 8> ParmSet(Parms.begin(), Parms.end());
SmallVector<UnexpandedParameterPack, 2> UnexpandedParms;
for (auto Parm : Unexpanded)
- if (ParmSet.contains(Parm.first.dyn_cast<NamedDecl *>()))
+ if (ParmSet.contains(dyn_cast<const NamedDecl *>(Parm.first)))
UnexpandedParms.push_back(Parm);
if (UnexpandedParms.empty())
return false;
@@ -749,6 +759,19 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, NumExpansions);
}
+static bool IsUnexpandedPackExpansion(const TemplateArgument &TA) {
+ if (!TA.isPackExpansion())
+ return false;
+
+ if (TA.getKind() == TemplateArgument::Type)
+ return !TA.getAsType()->getAs<PackExpansionType>()->getNumExpansions();
+
+ if (TA.getKind() == TemplateArgument::Expression)
+ return !cast<PackExpansionExpr>(TA.getAsExpr())->getNumExpansions();
+
+ return !TA.getNumTemplateExpansions();
+}
+
bool Sema::CheckParameterPacksForExpansion(
SourceLocation EllipsisLoc, SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
@@ -756,125 +779,104 @@ bool Sema::CheckParameterPacksForExpansion(
bool &RetainExpansion, std::optional<unsigned> &NumExpansions) {
ShouldExpand = true;
RetainExpansion = false;
- std::pair<IdentifierInfo *, SourceLocation> FirstPack;
- bool HaveFirstPack = false;
- std::optional<unsigned> NumPartialExpansions;
- SourceLocation PartiallySubstitutedPackLoc;
- typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
+ std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
+ std::optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
+ std::optional<unsigned> CurNumExpansions, CurMaximumOfLeastExpansions;
- for (UnexpandedParameterPack ParmPack : Unexpanded) {
+ for (auto [P, Loc] : Unexpanded) {
// Compute the depth and index for this parameter pack.
- unsigned Depth = 0, Index = 0;
- IdentifierInfo *Name;
- bool IsVarDeclPack = false;
- FunctionParmPackExpr *BindingPack = nullptr;
-
- if (const TemplateTypeParmType *TTP =
- ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
- Depth = TTP->getDepth();
- Index = TTP->getIndex();
- Name = TTP->getIdentifier();
- } else {
- NamedDecl *ND = cast<NamedDecl *>(ParmPack.first);
- if (isa<VarDecl>(ND))
- IsVarDeclPack = true;
- else if (isa<BindingDecl>(ND)) {
+ std::optional<std::pair<unsigned, unsigned>> Pos;
+ unsigned NewPackSize, PendingPackExpansionSize = 0;
+ const auto *ND = dyn_cast_if_present<const NamedDecl *>(P);
+ if (ND) {
+ if (isa<VarDecl>(ND)) {
+ auto *DAP = dyn_cast<LocalInstantiationScope::DeclArgumentPack *>(
+ *CurrentInstantiationScope->findInstantiationOf(ND));
+ if (!DAP) {
+ // We can't expand this function parameter pack, so we can't expand
+ // the pack expansion.
+ ShouldExpand = false;
+ continue;
+ }
+ NewPackSize = DAP->size();
+ } else if (isa<BindingDecl>(ND)) {
// Find the instantiated BindingDecl and check it for a resolved pack.
- llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
- CurrentInstantiationScope->findInstantiationOf(ND);
+ llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *>
+ *Instantiation = CurrentInstantiationScope->findInstantiationOf(ND);
Decl *B = cast<Decl *>(*Instantiation);
Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
- BindingPack = cast_if_present<FunctionParmPackExpr>(BindingExpr);
+ auto *BindingPack = cast_if_present<FunctionParmPackExpr>(BindingExpr);
if (!BindingPack) {
ShouldExpand = false;
continue;
}
+ NewPackSize = BindingPack->getNumExpansions();
} else
- std::tie(Depth, Index) = getDepthAndIndex(ND);
-
- Name = ND->getIdentifier();
+ Pos = getDepthAndIndex(ND);
+ } else if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(P)) {
+ Pos = {TTP->getDepth(), TTP->getIndex()};
+ ND = TTP->getDecl();
+ // FIXME: We either should have some fallback for canonical TTP, or
+ // never have canonical TTP here.
+ } else if (const auto *STP =
+ dyn_cast<const SubstTemplateTypeParmPackType *>(P)) {
+ NewPackSize = STP->getNumArgs();
+ PendingPackExpansionSize = llvm::count_if(
+ STP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
+ ND = STP->getReplacedParameter();
+ } else {
+ const auto *SEP = cast<const SubstNonTypeTemplateParmPackExpr *>(P);
+ NewPackSize = SEP->getArgumentPack().pack_size();
+ PendingPackExpansionSize = llvm::count_if(
+ SEP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
+ ND = SEP->getParameterPack();
}
- // Determine the size of this argument pack.
- unsigned NewPackSize, PendingPackExpansionSize = 0;
- if (IsVarDeclPack) {
- // Figure out whether we're instantiating to an argument pack or not.
- llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
- CurrentInstantiationScope->findInstantiationOf(
- cast<NamedDecl *>(ParmPack.first));
- if (isa<DeclArgumentPack *>(*Instantiation)) {
- // We could expand this function parameter pack.
- NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
- } else {
- // We can't expand this function parameter pack, so we can't expand
- // the pack expansion.
- ShouldExpand = false;
- continue;
- }
- } else if (BindingPack) {
- NewPackSize = BindingPack->getNumExpansions();
- } else {
+ if (Pos) {
// If we don't have a template argument at this depth/index, then we
// cannot expand the pack expansion. Make a note of this, but we still
// want to check any parameter packs we *do* have arguments for.
- if (Depth >= TemplateArgs.getNumLevels() ||
- !TemplateArgs.hasTemplateArgument(Depth, Index)) {
+ if (Pos->first >= TemplateArgs.getNumLevels() ||
+ !TemplateArgs.hasTemplateArgument(Pos->first, Pos->second)) {
ShouldExpand = false;
continue;
}
-
// Determine the size of the argument pack.
ArrayRef<TemplateArgument> Pack =
- TemplateArgs(Depth, Index).getPackAsArray();
+ TemplateArgs(Pos->first, Pos->second).getPackAsArray();
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();
- });
- }
-
- // C++0x [temp.arg.explicit]p9:
- // Template argument deduction can extend the sequence of template
- // arguments corresponding to a template parameter pack, even when the
- // sequence contains explicitly specified template arguments.
- if (!IsVarDeclPack && CurrentInstantiationScope) {
- if (NamedDecl *PartialPack =
- CurrentInstantiationScope->getPartiallySubstitutedPack()) {
- unsigned PartialDepth, PartialIndex;
- std::tie(PartialDepth, PartialIndex) = getDepthAndIndex(PartialPack);
- if (PartialDepth == Depth && PartialIndex == Index) {
+ llvm::count_if(Pack, IsUnexpandedPackExpansion);
+ // C++0x [temp.arg.explicit]p9:
+ // Template argument deduction can extend the sequence of template
+ // arguments corresponding to a template parameter pack, even when the
+ // sequence contains explicitly specified template arguments.
+ if (CurrentInstantiationScope)
+ if (const NamedDecl *PartialPack =
+ CurrentInstantiationScope->getPartiallySubstitutedPack();
+ PartialPack && getDepthAndIndex(PartialPack) == *Pos) {
RetainExpansion = true;
// We don't actually know the new pack size yet.
- NumPartialExpansions = NewPackSize;
- PartiallySubstitutedPackLoc = ParmPack.second;
+ PartialExpansion = {NewPackSize, Loc};
continue;
}
- }
}
- if (!NumExpansions) {
+ unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
+ if (PendingPackExpansionSize)
+ CurMaximumOfLeastExpansions =
+ CurMaximumOfLeastExpansions
+ ? std::max(LeastNewPackSize, *CurMaximumOfLeastExpansions)
+ : LeastNewPackSize;
+
+ // FIXME: Workaround for Canonical TTP.
+ const IdentifierInfo *Name = ND ? ND->getIdentifier() : nullptr;
+ if (!CurNumExpansions) {
// This is the first pack we've seen for which we have an argument.
// Record it.
- NumExpansions = NewPackSize;
- FirstPack.first = Name;
- FirstPack.second = ParmPack.second;
- HaveFirstPack = true;
- continue;
- }
-
- if (NewPackSize != *NumExpansions) {
+ CurNumExpansions = NewPackSize;
+ FirstPack = {Name, Loc};
+ } else if (NewPackSize != *CurNumExpansions) {
// In some cases, we might be handling packs with unexpanded template
// arguments. For example, this can occur when substituting into a type
// alias declaration that uses its injected template parameters as
@@ -888,31 +890,40 @@ bool Sema::CheckParameterPacksForExpansion(
// Pack comes from another template parameter. 'S<int>' is first
// instantiated, expanding the outer pack 'Outer' to <int>. The alias
// declaration is accordingly substituted, leaving the template arguments
- // as unexpanded
- // '<Pack...>'.
+ // as unexpanded '<Pack...>'.
//
// Since we have no idea of the size of '<Pack...>' until its expansion,
// we shouldn't assume its pack size for validation. However if we are
// certain that there are extra arguments beyond unexpanded packs, in
// which case the pack size is already larger than the previous expansion,
// we can complain that before instantiation.
- unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
- if (PendingPackExpansionSize && LeastNewPackSize <= *NumExpansions) {
+ if (PendingPackExpansionSize && LeastNewPackSize <= *CurNumExpansions) {
ShouldExpand = false;
continue;
}
// C++0x [temp.variadic]p5:
// All of the parameter packs expanded by a pack expansion shall have
// the same number of arguments specified.
- if (HaveFirstPack)
- Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
- << FirstPack.first << Name << *NumExpansions
- << (LeastNewPackSize != NewPackSize) << LeastNewPackSize
- << SourceRange(FirstPack.second) << SourceRange(ParmPack.second);
- else
- Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
- << Name << *NumExpansions << (LeastNewPackSize != NewPackSize)
- << LeastNewPackSize << SourceRange(ParmPack.second);
+ Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
+ << FirstPack.first << Name << *CurNumExpansions
+ << (LeastNewPackSize != NewPackSize) << LeastNewPackSize
+ << SourceRange(FirstPack.second) << SourceRange(Loc);
+ return true;
+ }
+ }
+
+ if (NumExpansions && CurNumExpansions &&
+ *NumExpansions != *CurNumExpansions) {
+ if (CurMaximumOfLeastExpansions &&
+ *CurMaximumOfLeastExpansions <= *NumExpansions) {
+ ShouldExpand = false;
+ } else {
+ Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
+ << FirstPack.first << *NumExpansions
+ << bool(CurMaximumOfLeastExpansions)
+ << (CurMaximumOfLeastExpansions ? *CurMaximumOfLeastExpansions
+ : *CurNumExpansions)
+ << SourceRange(FirstPack.second);
return true;
}
}
@@ -926,17 +937,18 @@ bool Sema::CheckParameterPacksForExpansion(
//
// ... a call to 'A<int, int>().f<int>' should expand the pack once and
// retain an expansion.
- if (NumPartialExpansions) {
- if (NumExpansions && *NumExpansions < *NumPartialExpansions) {
+ if (PartialExpansion) {
+ if (CurNumExpansions && *CurNumExpansions < PartialExpansion->first) {
NamedDecl *PartialPack =
CurrentInstantiationScope->getPartiallySubstitutedPack();
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_partial)
- << PartialPack << *NumPartialExpansions << *NumExpansions
- << SourceRange(PartiallySubstitutedPackLoc);
+ << PartialPack << PartialExpansion->first << *CurNumExpansions
+ << SourceRange(PartialExpansion->second);
return true;
}
-
- NumExpansions = NumPartialExpansions;
+ NumExpansions = PartialExpansion->first;
+ } else {
+ NumExpansions = CurNumExpansions;
}
return false;
@@ -946,47 +958,47 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansionFromUnexpanded(
llvm::ArrayRef<UnexpandedParameterPack> Unexpanded,
const MultiLevelTemplateArgumentList &TemplateArgs) {
std::optional<unsigned> Result;
- for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
- // Compute the depth and index for this parameter pack.
- unsigned Depth;
- unsigned Index;
+ auto SetResultSize = [&Result](unsigned Size) {
+ assert((!Result || *Result == Size) && "inconsistent pack sizes");
+ Result = Size;
+ };
+ auto SetResultPos = [&](const std::pair<unsigned, unsigned> &Pos) -> bool {
+ unsigned Depth = Pos.first, Index = Pos.second;
+ 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 true;
+ // Determine the size of the argument pack.
+ SetResultSize(TemplateArgs(Depth, Index).pack_size());
+ return false;
+ };
- if (const TemplateTypeParmType *TTP =
- Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
- Depth = TTP->getDepth();
- Index = TTP->getIndex();
+ for (auto [I, _] : Unexpanded) {
+ if (const auto *TTP = I.dyn_cast<const TemplateTypeParmType *>()) {
+ if (SetResultPos({TTP->getDepth(), TTP->getIndex()}))
+ return std::nullopt;
+ } else if (const auto *STP =
+ I.dyn_cast<const SubstTemplateTypeParmPackType *>()) {
+ SetResultSize(STP->getNumArgs());
+ } else if (const auto *SEP =
+ I.dyn_cast<const SubstNonTypeTemplateParmPackExpr *>()) {
+ SetResultSize(SEP->getArgumentPack().pack_size());
} else {
- NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
+ const auto *ND = cast<const NamedDecl *>(I);
+ // Function parameter pack or init-capture pack.
if (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))
+ const auto *DAP = dyn_cast<LocalInstantiationScope::DeclArgumentPack *>(
+ *CurrentInstantiationScope->findInstantiationOf(ND));
+ if (!DAP)
// 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;
+ SetResultSize(DAP->size());
+ } else if (SetResultPos(getDepthAndIndex(ND))) {
+ return std::nullopt;
}
-
- 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.
- unsigned Size = TemplateArgs(Depth, Index).pack_size();
- assert((!Result || *Result == Size) && "inconsistent pack sizes");
- Result = Size;
}
return Result;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index a09f623ff8fbf..328a0e7e0b243 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -6275,6 +6275,7 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
= dyn_cast<PackExpansionType>(OldType)) {
// We have a function parameter pack that may need to be expanded.
QualType Pattern = Expansion->getPattern();
+ NumExpansions = Expansion->getNumExpansions();
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
index 16e668e971a21..dc254e0e3e11a 100644
--- a/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp
@@ -517,6 +517,56 @@ int fn() {
}
}
+namespace GH56094 {
+template <typename... T> struct D {
+ template <typename... U> using B = int(int (*...p)(T, U));
+ // expected-error at -1 {{pack expansion contains parameter packs 'T' and 'U' that have different lengths (1 vs. 2)}}
+ template <typename U1, typename U2> D(B<U1, U2> *);
+ // expected-note at -1 {{in instantiation of template type alias 'B' requested here}}
+};
+using t1 = D<float>::B<int>;
+// expected-note at -1 {{in instantiation of template class 'GH56094::D<float>' requested here}}
+
+template <bool...> struct F {};
+template <class...> struct G {};
+template <bool... I> struct E {
+ template <bool... U> using B = G<F<I, U>...>;
+ // expected-error at -1 {{pack expansion contains parameter packs 'I' and 'U' that have different lengths (1 vs. 2)}}
+ template <bool U1, bool U2> E(B<U1, U2> *);
+ // expected-note at -1 {{in instantiation of template type alias 'B' requested here}}
+};
+using t2 = E<true>::B<false>;
+// expected-note at -1 {{in instantiation of template class 'GH56094::E<true>' requested here}}
+} // namespace GH56094
+
+namespace GH56094 {
+#if __cplusplus >= 201402L
+template <class> struct A; // expected-note {{template is declared here}}
+template <class> using B = char;
+template <class ...Cs> int C{ A<B<Cs>>{}... }; // expected-error {{implicit instantiation of undefined template}}
+#endif
+} // namespace GH56094
+
+namespace GH58679 {
+#if __cplusplus >= 201402L
+template <class> constexpr int A = 1;
+
+template <int> struct B;
+template <> struct B<1> { using b1 = void; };
+
+template <class> using C = char;
+
+template <class... Ds> int D{ B<A<C<Ds>>>{}... };
+
+struct E {
+ template <class E1, class = typename B<A<E1>>::b1> E(E1);
+};
+
+template <typename... Es> int F{ E(C<Es>{})... };
+#endif
+} // namespace GH58679
+
+
namespace GH58452 {
template <typename... As> struct A {
template <typename... Bs> using B = void(As...(Bs));
diff --git a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
index 47a252eb335f6..e357a73e855fa 100644
--- a/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
+++ b/clang/test/SemaTemplate/cxx1z-fold-expressions.cpp
@@ -97,7 +97,7 @@ namespace PR41845 {
template <int I> struct Constant {};
template <int... Is> struct Sum {
- template <int... Js> using type = Constant<((Is + Js) + ... + 0)>; // expected-error {{pack expansion contains parameter pack 'Js' that has a different length (1 vs. 2) from outer parameter packs}}
+ template <int... Js> using type = Constant<((Is + Js) + ... + 0)>; // expected-error {{pack expansion contains parameter packs 'Is' and 'Js' that have different lengths (1 vs. 2)}}
};
Sum<1>::type<1, 2> x; // expected-note {{instantiation of}}
diff --git a/clang/test/SemaTemplate/pack-deduction.cpp b/clang/test/SemaTemplate/pack-deduction.cpp
index b3104609994a4..c96ea74c9aebc 100644
--- a/clang/test/SemaTemplate/pack-deduction.cpp
+++ b/clang/test/SemaTemplate/pack-deduction.cpp
@@ -134,14 +134,14 @@ namespace partial_full_mix {
template<typename ...T> struct tuple {};
template<typename ...T> struct A {
template<typename ...U> static pair<tuple<T...>, tuple<U...>> f(pair<T, U> ...p);
- // expected-note at -1 {{[with U = <char, double, long>]: pack expansion contains parameter pack 'U' that has a different length (2 vs. 3) from outer parameter packs}}
+ // expected-note at -1 {{[with U = <char, double, long>]: pack expansion contains parameter packs 'T' and 'U' that have different lengths (2 vs. 3)}}
// expected-note at -2 {{[with U = <char, double, void>]: pack expansion contains parameter pack 'U' that has a different length (at least 3 vs. 2) from outer parameter packs}}
template<typename ...U> static pair<tuple<T...>, tuple<U...>> g(pair<T, U> ...p, ...);
- // expected-note at -1 {{[with U = <char, double, long>]: pack expansion contains parameter pack 'U' that has a different length (2 vs. 3) from outer parameter packs}}
+ // expected-note at -1 {{[with U = <char, double, long>]: pack expansion contains parameter packs 'T' and 'U' that have different lengths (2 vs. 3)}}
template<typename ...U> static tuple<U...> h(tuple<pair<T, U>..., pair<int, int>>);
- // expected-note at -1 {{[with U = <int[2]>]: pack expansion contains parameter pack 'U' that has a different length (2 vs. 1) from outer parameter packs}}
+ // expected-note at -1 {{[with U = <int[2]>]: pack expansion contains parameter packs 'T' and 'U' that have different lengths (2 vs. 1)}}
};
pair<tuple<int, float>, tuple<char, double>> k1 = A<int, float>().f<char>(pair<int, char>(), pair<float, double>());
@@ -211,7 +211,7 @@ using any_pairs_list = X<int, int>::Y<T...>; // #any_pairs_list
template <class... T>
using any_pairs_list_2 = X<int, int>::Y<>;
-// expected-error@#GH17042_Y {{different length (2 vs. 0)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. 0)}} \
// expected-note at -1 {{requested here}}
template <class A, class B, class... P>
@@ -219,20 +219,20 @@ using any_pairs_list_3 = X<int, int>::Y<A, B, P...>; // #any_pairs_list_3
template <class A, class B, class C, class... P>
using any_pairs_list_4 = X<int, int>::Y<A, B, C, P...>;
-// expected-error@#GH17042_Y {{different length (2 vs. at least 3)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. at least 3)}} \
// expected-note at -1 {{requested here}}
static_assert(__is_same(any_pairs_list<char, char>, X<void(int, char), void(int, char)>), "");
static_assert(!__is_same(any_pairs_list<char, char, char>, X<void(int, char), void(int, char)>), "");
-// expected-error@#GH17042_Y {{different length (2 vs. 3)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. 3)}} \
// expected-note@#any_pairs_list {{requested here}} \
// expected-note at -1 {{requested here}}
static_assert(__is_same(any_pairs_list_3<char, char>, X<void(int, char), void(int, char)>), "");
static_assert(!__is_same(any_pairs_list_3<char, char, float>, X<void(int, char), void(int, char)>), "");
-// expected-error@#GH17042_Y {{different length (2 vs. 3)}} \
+// expected-error@#GH17042_Y {{different lengths (2 vs. 3)}} \
// expected-note@#any_pairs_list_3 {{requested here}} \
// expected-note at -1 {{requested here}}
@@ -257,4 +257,41 @@ template <int... Args1> struct Nttp {
template <int... Args> using Alias = Nttp<1, 2, 3>::B<Args...>;
}
+// This test case comes from https://bugs.llvm.org/show_bug.cgi?id=16668
+namespace PR16668 {
+ template<int, typename> struct P {};
+
+ template<int ...A> struct S {
+ template<int ...B> using sum = S<A + B ...>; // expected-error {{different lengths (3 vs. 4)}}
+
+ template<typename ...Ts> void f(P<A, Ts> ...);
+
+#if 0
+ // FIXME: Substituting into Fn resulted in rejects-valid.
+ template<typename ...Ts> using Fn = void(S::*)(P<A, Ts>...);
+ template<typename ...Ts> void g(Fn<Ts...>);
+#endif
+ };
+
+ template<int ...C> using X = S<1, 2, 3>::sum<4, C ...>; // expected-note {{here}}
+
+ using Y = X<5, 6>;
+ using Y = S<5, 7, 9>;
+
+ using Z = X<5, 6, 7>; // expected-note {{here}}
+
+ void g() {
+ P<0, int> p0;
+ P<1, char> p1;
+ P<2, double> p2;
+ using S = S<0, 1, 2>;
+ S s;
+ s.f(p0, p1, p2);
+#if 0
+ s.g(&S::f<int, char, double>);
+ s.g<int, char, double>(&S::f);
+#endif
+ }
+}
+
}
>From 01ab5f63944dcfd16806ee09f9d6f26c745babb1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 13 Mar 2025 13:31:44 +0800
Subject: [PATCH 2/9] Changelogs, which were taken from D128095 and GH56094
---
clang/docs/ReleaseNotes.rst | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e16cfc69590fa..1ea37cb8f1aa0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -247,6 +247,8 @@ Improvements to Clang's diagnostics
- The ``-Wsign-compare`` warning now treats expressions with bitwise not(~) and minus(-) as signed integers
except for the case where the operand is an unsigned integer
and throws warning if they are compared with unsigned integers (##18878).
+- When diagnosing multi-level pack expansions of mismatched lengths, Clang will
+ now, in most cases, be able to point to the relevant outer parameter.
- Improve the diagnostics for chained comparisons to report actual expressions and operators (#GH129069).
@@ -308,6 +310,7 @@ Bug Fixes to C++ Support
not in the last position.
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
+- Fixed multi-level pack expansion of undeclared function parameters. (#GH56094)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
>From 03ba8d4d3aa7cdcbec4904b4408b3e94d9dc04b8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 14 Mar 2025 13:58:25 +0800
Subject: [PATCH 3/9] Explanation, assertions
---
clang/include/clang/Sema/SemaInternal.h | 3 ++
clang/lib/Sema/SemaConcept.cpp | 3 ++
clang/lib/Sema/SemaDeclCXX.cpp | 4 ++
clang/lib/Sema/SemaTemplateVariadic.cpp | 49 ++++++++++++++++---------
4 files changed, 41 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h
index acf6c8146d70d..26c14646192cd 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -72,6 +72,9 @@ inline std::pair<unsigned, unsigned> getDepthAndIndex(const NamedDecl *ND) {
/// Retrieve the depth and index of an unexpanded parameter pack.
inline std::pair<unsigned, unsigned>
getDepthAndIndex(UnexpandedParameterPack UPP) {
+ assert(!(isa<SubstTemplateTypeParmPackType, SubstNonTypeTemplateParmPackExpr>(
+ UPP.first)) &&
+ "Couldn't find depths for Subst* pack expansions!");
if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
return std::make_pair(TTP->getDepth(), TTP->getIndex());
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 57dc4154a537f..854d3aa43b2a3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1716,6 +1716,9 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
for (const UnexpandedParameterPack &APack : APacks) {
+ assert(!(isa<const SubstTemplateTypeParmPackType *,
+ const SubstNonTypeTemplateParmPackExpr *>(APack.first)) &&
+ "The pattern of CXXFoldExpr contains a Subst* node?");
std::pair<unsigned, unsigned> DepthAndIndex = getDepthAndIndex(APack);
auto it = llvm::find_if(BPacks, [&](const UnexpandedParameterPack &BPack) {
return getDepthAndIndex(BPack) == DepthAndIndex;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 756937f44e948..74377b2b3d724 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17588,6 +17588,10 @@ DeclResult Sema::ActOnTemplatedFriendTag(
collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
for (UnexpandedParameterPack &U : Unexpanded) {
+ assert(
+ !(isa<SubstTemplateTypeParmPackType, SubstNonTypeTemplateParmPackExpr>(
+ U.first)) &&
+ "Shouldn't see Subst* nodes from the parser");
if (getDepthAndIndex(U).first >= FriendDeclDepth) {
auto *ND = dyn_cast<const NamedDecl *>(U.first);
if (!ND)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d6c71ecec13b6..d8a15fc6e0d03 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -759,19 +759,6 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, NumExpansions);
}
-static bool IsUnexpandedPackExpansion(const TemplateArgument &TA) {
- if (!TA.isPackExpansion())
- return false;
-
- if (TA.getKind() == TemplateArgument::Type)
- return !TA.getAsType()->getAs<PackExpansionType>()->getNumExpansions();
-
- if (TA.getKind() == TemplateArgument::Expression)
- return !cast<PackExpansionExpr>(TA.getAsExpr())->getNumExpansions();
-
- return !TA.getNumTemplateExpansions();
-}
-
bool Sema::CheckParameterPacksForExpansion(
SourceLocation EllipsisLoc, SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
@@ -782,6 +769,22 @@ bool Sema::CheckParameterPacksForExpansion(
std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
std::optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
std::optional<unsigned> CurNumExpansions, CurMaximumOfLeastExpansions;
+ typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
+
+ // Determine if a pack expansion type contains an unresolved pack expansion.
+ // (i.e. the length of the expanded size is unknown at this point.)
+ static auto IsUnexpandedPackExpansion = [](const TemplateArgument &TA) {
+ if (!TA.isPackExpansion())
+ return false;
+
+ if (TA.getKind() == TemplateArgument::Type)
+ return !TA.getAsType()->getAs<PackExpansionType>()->getNumExpansions();
+
+ if (TA.getKind() == TemplateArgument::Expression)
+ return !cast<PackExpansionExpr>(TA.getAsExpr())->getNumExpansions();
+
+ return !TA.getNumTemplateExpansions();
+ };
for (auto [P, Loc] : Unexpanded) {
// Compute the depth and index for this parameter pack.
@@ -790,7 +793,7 @@ bool Sema::CheckParameterPacksForExpansion(
const auto *ND = dyn_cast_if_present<const NamedDecl *>(P);
if (ND) {
if (isa<VarDecl>(ND)) {
- auto *DAP = dyn_cast<LocalInstantiationScope::DeclArgumentPack *>(
+ auto *DAP = dyn_cast<DeclArgumentPack *>(
*CurrentInstantiationScope->findInstantiationOf(ND));
if (!DAP) {
// We can't expand this function parameter pack, so we can't expand
@@ -801,8 +804,8 @@ bool Sema::CheckParameterPacksForExpansion(
NewPackSize = DAP->size();
} else if (isa<BindingDecl>(ND)) {
// Find the instantiated BindingDecl and check it for a resolved pack.
- llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *>
- *Instantiation = CurrentInstantiationScope->findInstantiationOf(ND);
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
+ CurrentInstantiationScope->findInstantiationOf(ND);
Decl *B = cast<Decl *>(*Instantiation);
Expr *BindingExpr = cast<BindingDecl>(B)->getBinding();
auto *BindingPack = cast_if_present<FunctionParmPackExpr>(BindingExpr);
@@ -863,6 +866,9 @@ bool Sema::CheckParameterPacksForExpansion(
}
unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
+ // This maintains the maximum length of the least pack expansion at the
+ // current level, where the least pack expansion contains only resolvable
+ // packs at this point.
if (PendingPackExpansionSize)
CurMaximumOfLeastExpansions =
CurMaximumOfLeastExpansions
@@ -872,8 +878,8 @@ bool Sema::CheckParameterPacksForExpansion(
// FIXME: Workaround for Canonical TTP.
const IdentifierInfo *Name = ND ? ND->getIdentifier() : nullptr;
if (!CurNumExpansions) {
- // This is the first pack we've seen for which we have an argument.
- // Record it.
+ // This is the first pack we've seen at this level for which it has a
+ // length. Record it.
CurNumExpansions = NewPackSize;
FirstPack = {Name, Loc};
} else if (NewPackSize != *CurNumExpansions) {
@@ -912,8 +918,15 @@ bool Sema::CheckParameterPacksForExpansion(
}
}
+ // We have tried our best in the for loop to find out which outer pack
+ // expansion has a different length than the current one (by checking those
+ // Subst* nodes). However, if we fail to determine that, we'll have to
+ // complain the difference in a vague manner.
if (NumExpansions && CurNumExpansions &&
*NumExpansions != *CurNumExpansions) {
+ // If the current pack expansion contains any unresolved packs, and since
+ // they might eventually expand to match the outer expansion, we don't
+ // complain it too early.
if (CurMaximumOfLeastExpansions &&
*CurMaximumOfLeastExpansions <= *NumExpansions) {
ShouldExpand = false;
>From b68abb2f66e4ed8d53e07b2ac00f89b30ae2b79f Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 14 Mar 2025 14:25:22 +0800
Subject: [PATCH 4/9] Fix build failures
---
clang/include/clang/Sema/SemaInternal.h | 4 ++--
clang/lib/Sema/SemaDeclCXX.cpp | 7 +++----
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h
index 26c14646192cd..36ac3717cc1e1 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -72,8 +72,8 @@ inline std::pair<unsigned, unsigned> getDepthAndIndex(const NamedDecl *ND) {
/// Retrieve the depth and index of an unexpanded parameter pack.
inline std::pair<unsigned, unsigned>
getDepthAndIndex(UnexpandedParameterPack UPP) {
- assert(!(isa<SubstTemplateTypeParmPackType, SubstNonTypeTemplateParmPackExpr>(
- UPP.first)) &&
+ assert(!(isa<const SubstTemplateTypeParmPackType *,
+ const SubstNonTypeTemplateParmPackExpr *>(UPP.first)) &&
"Couldn't find depths for Subst* pack expansions!");
if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
return std::make_pair(TTP->getDepth(), TTP->getIndex());
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 74377b2b3d724..d98ec9096cd3d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17588,10 +17588,9 @@ DeclResult Sema::ActOnTemplatedFriendTag(
collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
for (UnexpandedParameterPack &U : Unexpanded) {
- assert(
- !(isa<SubstTemplateTypeParmPackType, SubstNonTypeTemplateParmPackExpr>(
- U.first)) &&
- "Shouldn't see Subst* nodes from the parser");
+ assert(!(isa<const SubstTemplateTypeParmPackType *,
+ const SubstNonTypeTemplateParmPackExpr *>(U.first)) &&
+ "Shouldn't see Subst* nodes from the parser");
if (getDepthAndIndex(U).first >= FriendDeclDepth) {
auto *ND = dyn_cast<const NamedDecl *>(U.first);
if (!ND)
>From 117fa0f7458e939d9bd5377a8304b01f71a1627c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 27 Mar 2025 13:58:36 +0800
Subject: [PATCH 5/9] [Clang] Correct the DeclRefExpr's Type after the
initializer gets instantiated
The instantiation of a VarDecl's initializer might be deferred until
the variable is actually used. However, we were still building the
DeclRefExpr with a type that could later be changed by the
initializer's instantiation, which is incorrect when incomplete
arrays are involved.
---
clang/docs/ReleaseNotes.rst | 1 +
clang/lib/Sema/SemaExpr.cpp | 11 +++++---
.../cxx1y-variable-templates_top_level.cpp | 26 +++++++++++++++++++
3 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d813fdb02d7de..f4fae6dfd52a8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -358,6 +358,7 @@ Bug Fixes to C++ Support
- Correctly diagnoses if unresolved using declarations shadows template paramters (#GH129411)
- Clang was previously coalescing volatile writes to members of volatile base class subobjects.
The issue has been addressed by propagating qualifiers during derived-to-base conversions in the AST. (#GH127824)
+- Correctly propagates the instantiated array type to the ``DeclRefExpr`` that refers to it. (#GH79750), (#GH113936), (#GH133047)
- Fixed a Clang regression in C++20 mode where unresolved dependent call expressions were created inside non-dependent contexts (#GH122892)
- Clang now emits the ``-Wunused-variable`` warning when some structured bindings are unused
and the ``[[maybe_unused]]`` attribute is not applied. (#GH125810)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3af6d6c23438f..f837b047ddfb4 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19849,11 +19849,14 @@ static void DoMarkVarDeclReferenced(
SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var);
});
- // Re-set the member to trigger a recomputation of the dependence bits
- // for the expression.
- if (auto *DRE = dyn_cast_or_null<DeclRefExpr>(E))
+ if (auto *DRE = dyn_cast_or_null<DeclRefExpr>(E)) {
+ // Re-set the member to trigger a recomputation of the dependence bits
+ // for the expression.
DRE->setDecl(DRE->getDecl());
- else if (auto *ME = dyn_cast_or_null<MemberExpr>(E))
+ if (SemaRef.Context.getAsIncompleteArrayType(DRE->getType()) &&
+ !SemaRef.Context.getAsIncompleteArrayType(Var->getType()))
+ DRE->setType(Var->getType());
+ } else if (auto *ME = dyn_cast_or_null<MemberExpr>(E))
ME->setMemberDecl(ME->getMemberDecl());
} else if (FirstInstantiation) {
SemaRef.PendingInstantiations
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
index da1678ec68627..6fc2032ee7fb4 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
@@ -467,3 +467,29 @@ namespace VexingParse {
template <typename> int var; // expected-note {{declared here}}
int x(var); // expected-error {{use of variable template 'var' requires template arguments}}
}
+
+#ifndef PRECXX11
+
+namespace GH79750 {
+
+enum class Values { A };
+
+template<typename E>
+constexpr Values values[] = {E::A};
+
+constexpr auto r = values<Values>[0] == Values::A;
+
+}
+
+namespace GH113956 {
+
+template <class T, T... VALUES>
+struct C {
+ static constexpr T VALUEARRAY[] = {VALUES...};
+};
+
+static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[3] == 3, "");
+static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[0] == 0, "");
+
+}
+#endif
>From 5560ac1c538f3678358c0176aac8f6facb2fa845 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 31 Mar 2025 20:10:05 +0800
Subject: [PATCH 6/9] Address feedback
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 3 +
clang/lib/Sema/SemaTemplateVariadic.cpp | 80 ++++++++++++------------
2 files changed, 43 insertions(+), 40 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c015636fe890d..60adb579602bc 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -875,6 +875,9 @@ class PackDeductionScope {
S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
UnexpandedParameterPack U = Unexpanded[I];
+ // We're only interested in undeduced packs during argument deduction.
+ // However, collectUnexpandedParameterPacks() may collect Subst* nodes,
+ // so we ignore those.
if (isa<const SubstTemplateTypeParmPackType *,
const SubstNonTypeTemplateParmPackExpr *>(U.first))
continue;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d8a15fc6e0d03..53cafe35539e4 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -37,27 +37,25 @@ class CollectUnexpandedParameterPacksVisitor
bool InLambdaOrBlock = false;
unsigned DepthLimit = (unsigned)-1;
-#ifndef NDEBUG
bool ContainsIntermediatePacks = false;
-#endif
- void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
- if (auto *VD = dyn_cast<VarDecl>(ND)) {
- // For now, the only problematic case is a generic lambda's templated
- // call operator, so we don't need to look for all the other ways we
- // could have reached a dependent parameter pack.
- auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
- auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
- if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
- return;
- } else if (ND->isTemplateParameterPack() &&
- getDepthAndIndex(ND).first >= DepthLimit) {
+ void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
+ if (auto *VD = dyn_cast<VarDecl>(ND)) {
+ // For now, the only problematic case is a generic lambda's templated
+ // call operator, so we don't need to look for all the other ways we
+ // could have reached a dependent parameter pack.
+ auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
+ auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
+ if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
return;
- }
-
- Unexpanded.push_back({ND, Loc});
+ } else if (ND->isTemplateParameterPack() &&
+ getDepthAndIndex(ND).first >= DepthLimit) {
+ return;
}
+ Unexpanded.push_back({ND, Loc});
+ }
+
void addUnexpanded(const TemplateTypeParmType *T,
SourceLocation Loc = SourceLocation()) {
if (T->getDepth() < DepthLimit)
@@ -100,27 +98,21 @@ class CollectUnexpandedParameterPacksVisitor
bool VisitSubstTemplateTypeParmPackTypeLoc(
SubstTemplateTypeParmPackTypeLoc TL) override {
Unexpanded.push_back({TL.getTypePtr(), TL.getNameLoc()});
-#ifdef NDEBUG
ContainsIntermediatePacks = true;
-#endif
return true;
}
bool VisitSubstTemplateTypeParmPackType(
SubstTemplateTypeParmPackType *T) override {
Unexpanded.push_back({T, SourceLocation()});
-#ifdef NDEBUG
ContainsIntermediatePacks = true;
-#endif
return true;
}
bool VisitSubstNonTypeTemplateParmPackExpr(
SubstNonTypeTemplateParmPackExpr *E) override {
Unexpanded.push_back({E, E->getParameterPackLocation()});
-#ifdef NDEBUG
ContainsIntermediatePacks = true;
-#endif
return true;
}
@@ -141,10 +133,8 @@ class CollectUnexpandedParameterPacksVisitor
addUnexpanded(TTP);
}
-#ifndef NDEBUG
ContainsIntermediatePacks |=
(bool)Template.getAsSubstTemplateTemplateParmPack();
-#endif
return DynamicRecursiveASTVisitor::TraverseTemplateName(Template);
}
@@ -336,14 +326,12 @@ class CollectUnexpandedParameterPacksVisitor
return DynamicRecursiveASTVisitor::TraverseLambdaCapture(Lambda, C, Init);
}
-#ifndef NDEBUG
bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) override {
ContainsIntermediatePacks = true;
return true;
}
bool containsIntermediatePacks() const { return ContainsIntermediatePacks; }
-#endif
};
}
@@ -766,8 +754,13 @@ bool Sema::CheckParameterPacksForExpansion(
bool &RetainExpansion, std::optional<unsigned> &NumExpansions) {
ShouldExpand = true;
RetainExpansion = false;
+ // Track the first pack we encounter that has a known length, used for
+ // diagnostics.
std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
+ // Track the partially expanded packs at the current level, used for
+ // diagnostics.
std::optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
+ // The pack size and the resolvable pack size at the current level.
std::optional<unsigned> CurNumExpansions, CurMaximumOfLeastExpansions;
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
@@ -821,18 +814,25 @@ bool Sema::CheckParameterPacksForExpansion(
ND = TTP->getDecl();
// FIXME: We either should have some fallback for canonical TTP, or
// never have canonical TTP here.
- } else if (const auto *STP =
- dyn_cast<const SubstTemplateTypeParmPackType *>(P)) {
- NewPackSize = STP->getNumArgs();
- PendingPackExpansionSize = llvm::count_if(
- STP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
- ND = STP->getReplacedParameter();
} else {
- const auto *SEP = cast<const SubstNonTypeTemplateParmPackExpr *>(P);
- NewPackSize = SEP->getArgumentPack().pack_size();
- PendingPackExpansionSize = llvm::count_if(
- SEP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
- ND = SEP->getParameterPack();
+ // The Subst* nodes aren't used for pack expansion, so we don't set Pos.
+ // Instead, their presence indicates that we've encountered a pack that's
+ // been substituted but not yet expanded. In this case, we try to
+ // recover their identifiers to improve our diagnostics by highlighting
+ // which parameters conflict with the current expansion.
+ if (const auto *STP =
+ dyn_cast<const SubstTemplateTypeParmPackType *>(P)) {
+ NewPackSize = STP->getNumArgs();
+ PendingPackExpansionSize = llvm::count_if(
+ STP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
+ ND = STP->getReplacedParameter();
+ } else {
+ const auto *SEP = cast<const SubstNonTypeTemplateParmPackExpr *>(P);
+ NewPackSize = SEP->getArgumentPack().pack_size();
+ PendingPackExpansionSize = llvm::count_if(
+ SEP->getArgumentPack().getPackAsArray(), IsUnexpandedPackExpansion);
+ ND = SEP->getParameterPack();
+ }
}
if (Pos) {
@@ -920,13 +920,13 @@ bool Sema::CheckParameterPacksForExpansion(
// We have tried our best in the for loop to find out which outer pack
// expansion has a different length than the current one (by checking those
- // Subst* nodes). However, if we fail to determine that, we'll have to
- // complain the difference in a vague manner.
+ // Subst* nodes). Here we display a more generic diagnostic if we could not
+ // find the outer pack.
if (NumExpansions && CurNumExpansions &&
*NumExpansions != *CurNumExpansions) {
// If the current pack expansion contains any unresolved packs, and since
// they might eventually expand to match the outer expansion, we don't
- // complain it too early.
+ // diagnose it now.
if (CurMaximumOfLeastExpansions &&
*CurMaximumOfLeastExpansions <= *NumExpansions) {
ShouldExpand = false;
>From 1ae0d0c7edb123a1c02064d62ae3544bc1b9ce4b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 31 Mar 2025 20:13:26 +0800
Subject: [PATCH 7/9] Don't format addUnexpanded()
---
clang/lib/Sema/SemaTemplateVariadic.cpp | 28 ++++++++++++-------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 53cafe35539e4..2c99b0e00c842 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -39,22 +39,22 @@ class CollectUnexpandedParameterPacksVisitor
bool ContainsIntermediatePacks = false;
- void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
- if (auto *VD = dyn_cast<VarDecl>(ND)) {
- // For now, the only problematic case is a generic lambda's templated
- // call operator, so we don't need to look for all the other ways we
- // could have reached a dependent parameter pack.
- auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
- auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
- if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
+ void addUnexpanded(NamedDecl *ND, SourceLocation Loc = SourceLocation()) {
+ if (auto *VD = dyn_cast<VarDecl>(ND)) {
+ // For now, the only problematic case is a generic lambda's templated
+ // call operator, so we don't need to look for all the other ways we
+ // could have reached a dependent parameter pack.
+ auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext());
+ auto *FTD = FD ? FD->getDescribedFunctionTemplate() : nullptr;
+ if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
+ return;
+ } else if (ND->isTemplateParameterPack() &&
+ getDepthAndIndex(ND).first >= DepthLimit) {
return;
- } else if (ND->isTemplateParameterPack() &&
- getDepthAndIndex(ND).first >= DepthLimit) {
- return;
- }
+ }
- Unexpanded.push_back({ND, Loc});
- }
+ Unexpanded.push_back({ND, Loc});
+ }
void addUnexpanded(const TemplateTypeParmType *T,
SourceLocation Loc = SourceLocation()) {
>From 16719f52886669923316b154083ee9a6a62c34c8 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 31 Mar 2025 20:15:08 +0800
Subject: [PATCH 8/9] Again, revert unrelated format changes
---
clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 2c99b0e00c842..9e9a3ffdae806 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -49,7 +49,7 @@ class CollectUnexpandedParameterPacksVisitor
if (FTD && FTD->getTemplateParameters()->getDepth() >= DepthLimit)
return;
} else if (ND->isTemplateParameterPack() &&
- getDepthAndIndex(ND).first >= DepthLimit) {
+ getDepthAndIndex(ND).first >= DepthLimit) {
return;
}
>From 2fb48389743361e162388e508cd640607cf1ac9c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 31 Mar 2025 20:17:27 +0800
Subject: [PATCH 9/9] Address one more comment
---
clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 9e9a3ffdae806..14f65d76c0166 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -865,10 +865,10 @@ bool Sema::CheckParameterPacksForExpansion(
}
}
- unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
// This maintains the maximum length of the least pack expansion at the
// current level, where the least pack expansion contains only resolvable
// packs at this point.
+ unsigned LeastNewPackSize = NewPackSize - PendingPackExpansionSize;
if (PendingPackExpansionSize)
CurMaximumOfLeastExpansions =
CurMaximumOfLeastExpansions
More information about the cfe-commits
mailing list