Skip to content

Commit 3a0309c

Browse files
committed
[clang] Improve diagnostics for expansion length mismatch
When checking parameter packs for expansion, instead of basing the diagnostic for length mismatch for outer parameters only on the known number of expansions, we should also analyze SubstTemplateTypeParmPackType and SubstNonTypeTemplateParmPackExpr for unexpanded packs, so we can emit a diagnostic pointing to a concrete outer parameter. Signed-off-by: Matheus Izvekov <[email protected]> Differential Revision: https://reviews.llvm.org/D128095
1 parent 0f6a2cd commit 3a0309c

File tree

8 files changed

+140
-126
lines changed

8 files changed

+140
-126
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ Improvements to Clang's diagnostics
118118
- Correctly diagnose a future keyword if it exist as a keyword in the higher
119119
language version and specifies in which version it will be a keyword. This
120120
supports both c and c++ language.
121+
- When diagnosing multi-level pack expansions of mismatched lengths, Clang will
122+
now, in most cases, be able to point to the relevant outer parameter.
121123

122124
Non-comprehensive list of changes in this release
123125
-------------------------------------------------

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,11 @@ namespace threadSafety {
238238

239239
// FIXME: No way to easily map from TemplateTypeParmTypes to
240240
// TemplateTypeParmDecls, so we have this horrible PointerUnion.
241-
typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType*, NamedDecl*>,
242-
SourceLocation> UnexpandedParameterPack;
241+
using UnexpandedParameterPack = std::pair<
242+
llvm::PointerUnion<
243+
const TemplateTypeParmType *, const SubstTemplateTypeParmPackType *,
244+
const SubstNonTypeTemplateParmPackExpr *, const NamedDecl *>,
245+
SourceLocation>;
243246

244247
/// Describes whether we've seen any nullability information for the given
245248
/// file.

clang/include/clang/Sema/SemaInternal.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ inline InheritableAttr *getDLLAttr(Decl *D) {
6262
}
6363

6464
/// Retrieve the depth and index of a template parameter.
65-
inline std::pair<unsigned, unsigned> getDepthAndIndex(NamedDecl *ND) {
65+
inline std::pair<unsigned, unsigned> getDepthAndIndex(const NamedDecl *ND) {
6666
if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(ND))
6767
return std::make_pair(TTP->getDepth(), TTP->getIndex());
6868

@@ -79,7 +79,7 @@ getDepthAndIndex(UnexpandedParameterPack UPP) {
7979
if (const auto *TTP = UPP.first.dyn_cast<const TemplateTypeParmType *>())
8080
return std::make_pair(TTP->getDepth(), TTP->getIndex());
8181

82-
return getDepthAndIndex(UPP.first.get<NamedDecl *>());
82+
return getDepthAndIndex(UPP.first.get<const NamedDecl *>());
8383
}
8484

8585
class TypoCorrectionConsumer : public VisibleDeclConsumer {

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,8 +738,11 @@ class PackDeductionScope {
738738
SmallVector<UnexpandedParameterPack, 2> Unexpanded;
739739
S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
740740
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
741-
unsigned Depth, Index;
742-
std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]);
741+
UnexpandedParameterPack U = Unexpanded[I];
742+
if (U.first.is<const SubstTemplateTypeParmPackType *>() ||
743+
U.first.is<const SubstNonTypeTemplateParmPackExpr *>())
744+
continue;
745+
auto [Depth, Index] = getDepthAndIndex(U);
743746
if (Depth == Info.getDeducedDepth())
744747
AddPack(Index);
745748
}

clang/lib/Sema/SemaTemplateVariadic.cpp

Lines changed: 120 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,23 @@ namespace {
8888
return true;
8989
}
9090

91+
bool
92+
VisitSubstTemplateTypeParmPackTypeLoc(SubstTemplateTypeParmPackTypeLoc TL) {
93+
Unexpanded.push_back({TL.getTypePtr(), TL.getNameLoc()});
94+
return true;
95+
}
96+
97+
bool VisitSubstTemplateTypeParmPackType(SubstTemplateTypeParmPackType *T) {
98+
Unexpanded.push_back({T, SourceLocation()});
99+
return true;
100+
}
101+
102+
bool
103+
VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E) {
104+
Unexpanded.push_back({E, E->getParameterPackLocation()});
105+
return true;
106+
}
107+
91108
/// Record occurrences of function and non-type template
92109
/// parameter packs in an expression.
93110
bool VisitDeclRefExpr(DeclRefExpr *E) {
@@ -306,7 +323,8 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
306323
auto *TTPD = dyn_cast<TemplateTypeParmDecl>(LocalPack);
307324
return TTPD && TTPD->getTypeForDecl() == TTPT;
308325
}
309-
return declaresSameEntity(Pack.first.get<NamedDecl *>(), LocalPack);
326+
return declaresSameEntity(Pack.first.get<const NamedDecl *>(),
327+
LocalPack);
310328
};
311329
if (llvm::any_of(LSI->LocalPacks, DeclaresThisPack))
312330
LambdaParamPackReferences.push_back(Pack);
@@ -358,7 +376,7 @@ Sema::DiagnoseUnexpandedParameterPacks(SourceLocation Loc,
358376
= Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>())
359377
Name = TTP->getIdentifier();
360378
else
361-
Name = Unexpanded[I].first.get<NamedDecl *>()->getIdentifier();
379+
Name = Unexpanded[I].first.get<const NamedDecl *>()->getIdentifier();
362380

363381
if (Name && NamesKnown.insert(Name).second)
364382
Names.push_back(Name);
@@ -421,7 +439,7 @@ bool Sema::DiagnoseUnexpandedParameterPackInRequiresExpr(RequiresExpr *RE) {
421439
llvm::SmallPtrSet<NamedDecl*, 8> ParmSet(Parms.begin(), Parms.end());
422440
SmallVector<UnexpandedParameterPack, 2> UnexpandedParms;
423441
for (auto Parm : Unexpanded)
424-
if (ParmSet.contains(Parm.first.dyn_cast<NamedDecl*>()))
442+
if (ParmSet.contains(Parm.first.dyn_cast<const NamedDecl *>()))
425443
UnexpandedParms.push_back(Parm);
426444
if (UnexpandedParms.empty())
427445
return false;
@@ -672,109 +690,95 @@ bool Sema::CheckParameterPacksForExpansion(
672690
bool &RetainExpansion, Optional<unsigned> &NumExpansions) {
673691
ShouldExpand = true;
674692
RetainExpansion = false;
675-
std::pair<IdentifierInfo *, SourceLocation> FirstPack;
676-
bool HaveFirstPack = false;
677-
Optional<unsigned> NumPartialExpansions;
678-
SourceLocation PartiallySubstitutedPackLoc;
693+
std::pair<const IdentifierInfo *, SourceLocation> FirstPack;
694+
Optional<std::pair<unsigned, SourceLocation>> PartialExpansion;
695+
Optional<unsigned> CurNumExpansions;
679696

680-
for (UnexpandedParameterPack ParmPack : Unexpanded) {
697+
for (auto [P, Loc] : Unexpanded) {
681698
// Compute the depth and index for this parameter pack.
682-
unsigned Depth = 0, Index = 0;
683-
IdentifierInfo *Name;
684-
bool IsVarDeclPack = false;
685-
686-
if (const TemplateTypeParmType *TTP =
687-
ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
688-
Depth = TTP->getDepth();
689-
Index = TTP->getIndex();
690-
Name = TTP->getIdentifier();
691-
} else {
692-
NamedDecl *ND = ParmPack.first.get<NamedDecl *>();
693-
if (isa<VarDecl>(ND))
694-
IsVarDeclPack = true;
695-
else
696-
std::tie(Depth, Index) = getDepthAndIndex(ND);
697-
698-
Name = ND->getIdentifier();
699-
}
700-
701-
// Determine the size of this argument pack.
699+
Optional<std::pair<unsigned, unsigned>> Pos;
702700
unsigned NewPackSize;
703-
if (IsVarDeclPack) {
704-
// Figure out whether we're instantiating to an argument pack or not.
705-
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
706-
707-
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
708-
CurrentInstantiationScope->findInstantiationOf(
709-
ParmPack.first.get<NamedDecl *>());
710-
if (Instantiation->is<DeclArgumentPack *>()) {
711-
// We could expand this function parameter pack.
712-
NewPackSize = Instantiation->get<DeclArgumentPack *>()->size();
713-
} else {
701+
const auto *ND = P.dyn_cast<const NamedDecl *>();
702+
if (ND && isa<VarDecl>(ND)) {
703+
const auto *DAP =
704+
CurrentInstantiationScope->findInstantiationOf(ND)
705+
->dyn_cast<LocalInstantiationScope::DeclArgumentPack *>();
706+
if (!DAP) {
714707
// We can't expand this function parameter pack, so we can't expand
715708
// the pack expansion.
716709
ShouldExpand = false;
717710
continue;
718711
}
712+
NewPackSize = DAP->size();
713+
} else if (ND) {
714+
Pos = getDepthAndIndex(ND);
715+
} else if (const auto *TTP = P.dyn_cast<const TemplateTypeParmType *>()) {
716+
Pos = {TTP->getDepth(), TTP->getIndex()};
717+
ND = TTP->getDecl();
718+
// FIXME: We either should have some fallback for canonical TTP, or
719+
// never have canonical TTP here.
720+
} else if (const auto *STP =
721+
P.dyn_cast<const SubstTemplateTypeParmPackType *>()) {
722+
NewPackSize = STP->getNumArgs();
723+
ND = STP->getReplacedParameter()->getDecl();
719724
} else {
725+
const auto *SEP = P.get<const SubstNonTypeTemplateParmPackExpr *>();
726+
NewPackSize = SEP->getArgumentPack().pack_size();
727+
ND = SEP->getParameterPack();
728+
}
729+
730+
if (Pos) {
720731
// If we don't have a template argument at this depth/index, then we
721732
// cannot expand the pack expansion. Make a note of this, but we still
722733
// want to check any parameter packs we *do* have arguments for.
723-
if (Depth >= TemplateArgs.getNumLevels() ||
724-
!TemplateArgs.hasTemplateArgument(Depth, Index)) {
734+
if (Pos->first >= TemplateArgs.getNumLevels() ||
735+
!TemplateArgs.hasTemplateArgument(Pos->first, Pos->second)) {
725736
ShouldExpand = false;
726737
continue;
727738
}
728-
729739
// Determine the size of the argument pack.
730-
NewPackSize = TemplateArgs(Depth, Index).pack_size();
731-
}
732-
733-
// C++0x [temp.arg.explicit]p9:
734-
// Template argument deduction can extend the sequence of template
735-
// arguments corresponding to a template parameter pack, even when the
736-
// sequence contains explicitly specified template arguments.
737-
if (!IsVarDeclPack && CurrentInstantiationScope) {
738-
if (NamedDecl *PartialPack
739-
= CurrentInstantiationScope->getPartiallySubstitutedPack()){
740-
unsigned PartialDepth, PartialIndex;
741-
std::tie(PartialDepth, PartialIndex) = getDepthAndIndex(PartialPack);
742-
if (PartialDepth == Depth && PartialIndex == Index) {
740+
NewPackSize = TemplateArgs(Pos->first, Pos->second).pack_size();
741+
// C++0x [temp.arg.explicit]p9:
742+
// Template argument deduction can extend the sequence of template
743+
// arguments corresponding to a template parameter pack, even when the
744+
// sequence contains explicitly specified template arguments.
745+
if (CurrentInstantiationScope)
746+
if (const NamedDecl *PartialPack =
747+
CurrentInstantiationScope->getPartiallySubstitutedPack();
748+
PartialPack && getDepthAndIndex(PartialPack) == *Pos) {
743749
RetainExpansion = true;
744750
// We don't actually know the new pack size yet.
745-
NumPartialExpansions = NewPackSize;
746-
PartiallySubstitutedPackLoc = ParmPack.second;
751+
PartialExpansion = {NewPackSize, Loc};
747752
continue;
748753
}
749-
}
750754
}
751755

752-
if (!NumExpansions) {
756+
// FIXME: Workaround for Canonical TTP.
757+
const IdentifierInfo *Name = ND ? ND->getIdentifier() : nullptr;
758+
if (!CurNumExpansions) {
753759
// The is the first pack we've seen for which we have an argument.
754760
// Record it.
755-
NumExpansions = NewPackSize;
756-
FirstPack.first = Name;
757-
FirstPack.second = ParmPack.second;
758-
HaveFirstPack = true;
759-
continue;
760-
}
761-
762-
if (NewPackSize != *NumExpansions) {
761+
CurNumExpansions = NewPackSize;
762+
FirstPack = {Name, Loc};
763+
} else if (NewPackSize != *CurNumExpansions) {
763764
// C++0x [temp.variadic]p5:
764765
// All of the parameter packs expanded by a pack expansion shall have
765766
// the same number of arguments specified.
766-
if (HaveFirstPack)
767-
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
768-
<< FirstPack.first << Name << *NumExpansions << NewPackSize
769-
<< SourceRange(FirstPack.second) << SourceRange(ParmPack.second);
770-
else
771-
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
772-
<< Name << *NumExpansions << NewPackSize
773-
<< SourceRange(ParmPack.second);
767+
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict)
768+
<< FirstPack.first << Name << *CurNumExpansions << NewPackSize
769+
<< SourceRange(FirstPack.second) << SourceRange(Loc);
774770
return true;
775771
}
776772
}
777773

774+
if (NumExpansions && CurNumExpansions &&
775+
*NumExpansions != *CurNumExpansions) {
776+
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_multilevel)
777+
<< FirstPack.first << *CurNumExpansions << *NumExpansions
778+
<< SourceRange(FirstPack.second);
779+
return true;
780+
}
781+
778782
// If we're performing a partial expansion but we also have a full expansion,
779783
// expand to the number of common arguments. For example, given:
780784
//
@@ -784,17 +788,18 @@ bool Sema::CheckParameterPacksForExpansion(
784788
//
785789
// ... a call to 'A<int, int>().f<int>' should expand the pack once and
786790
// retain an expansion.
787-
if (NumPartialExpansions) {
788-
if (NumExpansions && *NumExpansions < *NumPartialExpansions) {
791+
if (PartialExpansion) {
792+
if (CurNumExpansions && *CurNumExpansions < PartialExpansion->first) {
789793
NamedDecl *PartialPack =
790794
CurrentInstantiationScope->getPartiallySubstitutedPack();
791795
Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_partial)
792-
<< PartialPack << *NumPartialExpansions << *NumExpansions
793-
<< SourceRange(PartiallySubstitutedPackLoc);
796+
<< PartialPack << PartialExpansion->first << *CurNumExpansions
797+
<< SourceRange(PartialExpansion->second);
794798
return true;
795799
}
796-
797-
NumExpansions = NumPartialExpansions;
800+
NumExpansions = PartialExpansion->first;
801+
} else {
802+
NumExpansions = CurNumExpansions;
798803
}
799804

800805
return false;
@@ -807,47 +812,48 @@ Optional<unsigned> Sema::getNumArgumentsInExpansion(QualType T,
807812
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(Pattern);
808813

809814
Optional<unsigned> Result;
810-
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
811-
// Compute the depth and index for this parameter pack.
812-
unsigned Depth;
813-
unsigned Index;
815+
auto setResultSz = [&Result](unsigned Size) {
816+
assert((!Result || *Result == Size) && "inconsistent pack sizes");
817+
Result = Size;
818+
};
819+
auto setResultPos = [&](const std::pair<unsigned, unsigned> &Pos) -> bool {
820+
unsigned Depth = Pos.first, Index = Pos.second;
821+
if (Depth >= TemplateArgs.getNumLevels() ||
822+
!TemplateArgs.hasTemplateArgument(Depth, Index))
823+
// The pattern refers to an unknown template argument. We're not ready to
824+
// expand this pack yet.
825+
return true;
826+
// Determine the size of the argument pack.
827+
setResultSz(TemplateArgs(Depth, Index).pack_size());
828+
return false;
829+
};
814830

815-
if (const TemplateTypeParmType *TTP
816-
= Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
817-
Depth = TTP->getDepth();
818-
Index = TTP->getIndex();
831+
for (auto [I, _] : Unexpanded) {
832+
if (const auto *TTP = I.dyn_cast<const TemplateTypeParmType *>()) {
833+
if (setResultPos({TTP->getDepth(), TTP->getIndex()}))
834+
return None;
835+
} else if (const auto *STP =
836+
I.dyn_cast<const SubstTemplateTypeParmPackType *>()) {
837+
setResultSz(STP->getNumArgs());
838+
} else if (const auto *SEP =
839+
I.dyn_cast<const SubstNonTypeTemplateParmPackExpr *>()) {
840+
setResultSz(SEP->getArgumentPack().pack_size());
819841
} else {
820-
NamedDecl *ND = Unexpanded[I].first.get<NamedDecl *>();
842+
const auto *ND = I.get<const NamedDecl *>();
843+
// Function parameter pack or init-capture pack.
821844
if (isa<VarDecl>(ND)) {
822-
// Function parameter pack or init-capture pack.
823-
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
824-
825-
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation
826-
= CurrentInstantiationScope->findInstantiationOf(
827-
Unexpanded[I].first.get<NamedDecl *>());
828-
if (Instantiation->is<Decl*>())
845+
const auto *DAP =
846+
CurrentInstantiationScope->findInstantiationOf(ND)
847+
->dyn_cast<LocalInstantiationScope::DeclArgumentPack *>();
848+
if (!DAP)
829849
// The pattern refers to an unexpanded pack. We're not ready to expand
830850
// this pack yet.
831851
return None;
832-
833-
unsigned Size = Instantiation->get<DeclArgumentPack *>()->size();
834-
assert((!Result || *Result == Size) && "inconsistent pack sizes");
835-
Result = Size;
836-
continue;
852+
setResultSz(DAP->size());
853+
} else if (setResultPos(getDepthAndIndex(ND))) {
854+
return None;
837855
}
838-
839-
std::tie(Depth, Index) = getDepthAndIndex(ND);
840856
}
841-
if (Depth >= TemplateArgs.getNumLevels() ||
842-
!TemplateArgs.hasTemplateArgument(Depth, Index))
843-
// The pattern refers to an unknown template argument. We're not ready to
844-
// expand this pack yet.
845-
return None;
846-
847-
// Determine the size of the argument pack.
848-
unsigned Size = TemplateArgs(Depth, Index).pack_size();
849-
assert((!Result || *Result == Size) && "inconsistent pack sizes");
850-
Result = Size;
851857
}
852858

853859
return Result;

clang/test/CXX/temp/temp.decls/temp.variadic/p5.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ int fn() {
473473
namespace pr56094 {
474474
template <typename... T> struct D {
475475
template <typename... U> using B = int(int (*...p)(T, U));
476-
// expected-error@-1 {{pack expansion contains parameter pack 'U' that has a different length (1 vs. 2) from outer parameter packs}}
476+
// expected-error@-1 {{pack expansion contains parameter packs 'T' and 'U' that have different lengths (1 vs. 2)}}
477477
template <typename U1, typename U2> D(B<U1, U2> *);
478478
// expected-note@-1 {{in instantiation of template type alias 'B' requested here}}
479479
};
@@ -484,7 +484,7 @@ template <bool...> struct F {};
484484
template <class...> struct G {};
485485
template <bool... I> struct E {
486486
template <bool... U> using B = G<F<I, U>...>;
487-
// expected-error@-1 {{pack expansion contains parameter pack 'U' that has a different length (1 vs. 2) from outer parameter packs}}
487+
// expected-error@-1 {{pack expansion contains parameter packs 'I' and 'U' that have different lengths (1 vs. 2)}}
488488
template <bool U1, bool U2> E(B<U1, U2> *);
489489
// expected-note@-1 {{in instantiation of template type alias 'B' requested here}}
490490
};

0 commit comments

Comments
 (0)