Skip to content

Commit 1d1a569

Browse files
committed
Clang: fix AST representation of expanded template arguments.
Extend clang's SubstTemplateTypeParm to represent the pack substitution index. Fixes PR56099. Signed-off-by: Matheus Izvekov <[email protected]> Differential Revision: https://reviews.llvm.org/D128113
1 parent cf75ef4 commit 1d1a569

15 files changed

+113
-29
lines changed

clang/include/clang/AST/ASTContext.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
16141614
QualType Wrapped);
16151615

16161616
QualType getSubstTemplateTypeParmType(const TemplateTypeParmType *Replaced,
1617-
QualType Replacement) const;
1617+
QualType Replacement,
1618+
Optional<unsigned> PackIndex) const;
16181619
QualType getSubstTemplateTypeParmPackType(
16191620
const TemplateTypeParmType *Replaced,
16201621
const TemplateArgument &ArgPack);

clang/include/clang/AST/JSONNodeDumper.h

+1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ class JSONNodeDumper
220220
void VisitUnaryTransformType(const UnaryTransformType *UTT);
221221
void VisitTagType(const TagType *TT);
222222
void VisitTemplateTypeParmType(const TemplateTypeParmType *TTPT);
223+
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *STTPT);
223224
void VisitAutoType(const AutoType *AT);
224225
void VisitTemplateSpecializationType(const TemplateSpecializationType *TST);
225226
void VisitInjectedClassNameType(const InjectedClassNameType *ICNT);

clang/include/clang/AST/TextNodeDumper.h

+1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ class TextNodeDumper
317317
void VisitUnaryTransformType(const UnaryTransformType *T);
318318
void VisitTagType(const TagType *T);
319319
void VisitTemplateTypeParmType(const TemplateTypeParmType *T);
320+
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T);
320321
void VisitAutoType(const AutoType *T);
321322
void VisitDeducedTemplateSpecializationType(
322323
const DeducedTemplateSpecializationType *T);

clang/include/clang/AST/Type.h

+27-4
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,18 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
17901790
unsigned NumArgs;
17911791
};
17921792

1793+
class SubstTemplateTypeParmTypeBitfields {
1794+
friend class SubstTemplateTypeParmType;
1795+
1796+
unsigned : NumTypeBits;
1797+
1798+
/// Represents the index within a pack if this represents a substitution
1799+
/// from a pack expansion.
1800+
/// Positive non-zero number represents the index + 1.
1801+
/// Zero means this is not substituted from an expansion.
1802+
unsigned PackIndex;
1803+
};
1804+
17931805
class SubstTemplateTypeParmPackTypeBitfields {
17941806
friend class SubstTemplateTypeParmPackType;
17951807

@@ -1872,6 +1884,7 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
18721884
ElaboratedTypeBitfields ElaboratedTypeBits;
18731885
VectorTypeBitfields VectorTypeBits;
18741886
SubstTemplateTypeParmPackTypeBitfields SubstTemplateTypeParmPackTypeBits;
1887+
SubstTemplateTypeParmTypeBitfields SubstTemplateTypeParmTypeBits;
18751888
TemplateSpecializationTypeBitfields TemplateSpecializationTypeBits;
18761889
DependentTemplateSpecializationTypeBitfields
18771890
DependentTemplateSpecializationTypeBits;
@@ -4974,9 +4987,12 @@ class SubstTemplateTypeParmType : public Type, public llvm::FoldingSetNode {
49744987
// The original type parameter.
49754988
const TemplateTypeParmType *Replaced;
49764989

4977-
SubstTemplateTypeParmType(const TemplateTypeParmType *Param, QualType Canon)
4990+
SubstTemplateTypeParmType(const TemplateTypeParmType *Param, QualType Canon,
4991+
Optional<unsigned> PackIndex)
49784992
: Type(SubstTemplateTypeParm, Canon, Canon->getDependence()),
4979-
Replaced(Param) {}
4993+
Replaced(Param) {
4994+
SubstTemplateTypeParmTypeBits.PackIndex = PackIndex ? *PackIndex + 1 : 0;
4995+
}
49804996

49814997
public:
49824998
/// Gets the template parameter that was substituted for.
@@ -4990,18 +5006,25 @@ class SubstTemplateTypeParmType : public Type, public llvm::FoldingSetNode {
49905006
return getCanonicalTypeInternal();
49915007
}
49925008

5009+
Optional<unsigned> getPackIndex() const {
5010+
if (SubstTemplateTypeParmTypeBits.PackIndex == 0)
5011+
return None;
5012+
return SubstTemplateTypeParmTypeBits.PackIndex - 1;
5013+
}
5014+
49935015
bool isSugared() const { return true; }
49945016
QualType desugar() const { return getReplacementType(); }
49955017

49965018
void Profile(llvm::FoldingSetNodeID &ID) {
4997-
Profile(ID, getReplacedParameter(), getReplacementType());
5019+
Profile(ID, getReplacedParameter(), getReplacementType(), getPackIndex());
49985020
}
49995021

50005022
static void Profile(llvm::FoldingSetNodeID &ID,
50015023
const TemplateTypeParmType *Replaced,
5002-
QualType Replacement) {
5024+
QualType Replacement, Optional<unsigned> PackIndex) {
50035025
ID.AddPointer(Replaced);
50045026
ID.AddPointer(Replacement.getAsOpaquePtr());
5027+
ID.AddInteger(PackIndex ? *PackIndex - 1 : 0);
50055028
}
50065029

50075030
static bool classof(const Type *T) {

clang/include/clang/AST/TypeProperties.td

+4-1
Original file line numberDiff line numberDiff line change
@@ -734,12 +734,15 @@ let Class = SubstTemplateTypeParmType in {
734734
def : Property<"replacementType", QualType> {
735735
let Read = [{ node->getReplacementType() }];
736736
}
737+
def : Property<"PackIndex", Optional<UInt32>> {
738+
let Read = [{ node->getPackIndex() }];
739+
}
737740

738741
def : Creator<[{
739742
// The call to getCanonicalType here existed in ASTReader.cpp, too.
740743
return ctx.getSubstTemplateTypeParmType(
741744
cast<TemplateTypeParmType>(replacedParameter),
742-
ctx.getCanonicalType(replacementType));
745+
ctx.getCanonicalType(replacementType), PackIndex);
743746
}]>;
744747
}
745748

clang/lib/AST/ASTContext.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -4744,19 +4744,20 @@ QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
47444744
/// Retrieve a substitution-result type.
47454745
QualType
47464746
ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm,
4747-
QualType Replacement) const {
4747+
QualType Replacement,
4748+
Optional<unsigned> PackIndex) const {
47484749
assert(Replacement.isCanonical()
47494750
&& "replacement types must always be canonical");
47504751

47514752
llvm::FoldingSetNodeID ID;
4752-
SubstTemplateTypeParmType::Profile(ID, Parm, Replacement);
4753+
SubstTemplateTypeParmType::Profile(ID, Parm, Replacement, PackIndex);
47534754
void *InsertPos = nullptr;
47544755
SubstTemplateTypeParmType *SubstParm
47554756
= SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos);
47564757

47574758
if (!SubstParm) {
47584759
SubstParm = new (*this, TypeAlignment)
4759-
SubstTemplateTypeParmType(Parm, Replacement);
4760+
SubstTemplateTypeParmType(Parm, Replacement, PackIndex);
47604761
Types.push_back(SubstParm);
47614762
SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos);
47624763
}

clang/lib/AST/ASTImporter.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,8 @@ ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmType(
15301530
return ToReplacementTypeOrErr.takeError();
15311531

15321532
return Importer.getToContext().getSubstTemplateTypeParmType(
1533-
*ReplacedOrErr, ToReplacementTypeOrErr->getCanonicalType());
1533+
*ReplacedOrErr, ToReplacementTypeOrErr->getCanonicalType(),
1534+
T->getPackIndex());
15341535
}
15351536

15361537
ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmPackType(

clang/lib/AST/ASTStructuralEquivalence.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
10621062
if (!IsStructurallyEquivalent(Context, Subst1->getReplacementType(),
10631063
Subst2->getReplacementType()))
10641064
return false;
1065+
if (Subst1->getPackIndex() != Subst2->getPackIndex())
1066+
return false;
10651067
break;
10661068
}
10671069

clang/lib/AST/JSONNodeDumper.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,12 @@ void JSONNodeDumper::VisitTemplateTypeParmType(
680680
JOS.attribute("decl", createBareDeclRef(TTPT->getDecl()));
681681
}
682682

683+
void JSONNodeDumper::VisitSubstTemplateTypeParmType(
684+
const SubstTemplateTypeParmType *STTPT) {
685+
if (auto PackIndex = STTPT->getPackIndex())
686+
JOS.attribute("pack_index", *PackIndex);
687+
}
688+
683689
void JSONNodeDumper::VisitAutoType(const AutoType *AT) {
684690
JOS.attribute("undeduced", !AT->isDeduced());
685691
switch (AT->getKeyword()) {

clang/lib/AST/TextNodeDumper.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,12 @@ void TextNodeDumper::VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
15681568
dumpDeclRef(T->getDecl());
15691569
}
15701570

1571+
void TextNodeDumper::VisitSubstTemplateTypeParmType(
1572+
const SubstTemplateTypeParmType *T) {
1573+
if (auto PackIndex = T->getPackIndex())
1574+
OS << " pack_index " << *PackIndex;
1575+
}
1576+
15711577
void TextNodeDumper::VisitAutoType(const AutoType *T) {
15721578
if (T->isDecltypeAuto())
15731579
OS << " decltype(auto)";

clang/lib/AST/Type.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,7 @@ struct SimpleTransformVisitor : public TypeVisitor<Derived, QualType> {
11671167
return QualType(T, 0);
11681168

11691169
return Ctx.getSubstTemplateTypeParmType(T->getReplacedParameter(),
1170-
replacementType);
1170+
replacementType, T->getPackIndex());
11711171
}
11721172

11731173
// FIXME: Non-trivial to implement, but important for C++

clang/lib/Sema/SemaTemplateInstantiate.cpp

+7-6
Original file line numberDiff line numberDiff line change
@@ -1813,6 +1813,7 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
18131813
return NewT;
18141814
}
18151815

1816+
Optional<unsigned> PackIndex;
18161817
if (T->isParameterPack()) {
18171818
assert(Arg.getKind() == TemplateArgument::Pack &&
18181819
"Missing argument pack");
@@ -1830,6 +1831,7 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
18301831
}
18311832

18321833
Arg = getPackSubstitutedTemplateArgument(getSema(), Arg);
1834+
PackIndex = getSema().ArgumentPackSubstitutionIndex;
18331835
}
18341836

18351837
assert(Arg.getKind() == TemplateArgument::Type &&
@@ -1838,8 +1840,8 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
18381840
QualType Replacement = Arg.getAsType();
18391841

18401842
// TODO: only do this uniquing once, at the start of instantiation.
1841-
QualType Result
1842-
= getSema().Context.getSubstTemplateTypeParmType(T, Replacement);
1843+
QualType Result = getSema().Context.getSubstTemplateTypeParmType(
1844+
T, Replacement, PackIndex);
18431845
SubstTemplateTypeParmTypeLoc NewTL
18441846
= TLB.push<SubstTemplateTypeParmTypeLoc>(Result);
18451847
NewTL.setNameLoc(TL.getNameLoc());
@@ -1877,11 +1879,10 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
18771879

18781880
TemplateArgument Arg = TL.getTypePtr()->getArgumentPack();
18791881
Arg = getPackSubstitutedTemplateArgument(getSema(), Arg);
1880-
QualType Result = Arg.getAsType();
18811882

1882-
Result = getSema().Context.getSubstTemplateTypeParmType(
1883-
TL.getTypePtr()->getReplacedParameter(),
1884-
Result);
1883+
QualType Result = getSema().Context.getSubstTemplateTypeParmType(
1884+
TL.getTypePtr()->getReplacedParameter(), Arg.getAsType(),
1885+
getSema().ArgumentPackSubstitutionIndex);
18851886
SubstTemplateTypeParmTypeLoc NewTL
18861887
= TLB.push<SubstTemplateTypeParmTypeLoc>(Result);
18871888
NewTL.setNameLoc(TL.getNameLoc());

clang/lib/Sema/TreeTransform.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -4854,7 +4854,8 @@ QualType TreeTransform<Derived>::RebuildQualifiedType(QualType T,
48544854
Replacement = SemaRef.Context.getQualifiedType(
48554855
Replacement.getUnqualifiedType(), Qs);
48564856
T = SemaRef.Context.getSubstTemplateTypeParmType(
4857-
SubstTypeParam->getReplacedParameter(), Replacement);
4857+
SubstTypeParam->getReplacedParameter(), Replacement,
4858+
SubstTypeParam->getPackIndex());
48584859
} else if ((AutoTy = dyn_cast<AutoType>(T)) && AutoTy->isDeduced()) {
48594860
// 'auto' types behave the same way as template parameters.
48604861
QualType Deduced = AutoTy->getDeducedType();
@@ -6410,9 +6411,8 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType(
64106411

64116412
// Always canonicalize the replacement type.
64126413
Replacement = SemaRef.Context.getCanonicalType(Replacement);
6413-
QualType Result
6414-
= SemaRef.Context.getSubstTemplateTypeParmType(T->getReplacedParameter(),
6415-
Replacement);
6414+
QualType Result = SemaRef.Context.getSubstTemplateTypeParmType(
6415+
T->getReplacedParameter(), Replacement, T->getPackIndex());
64166416

64176417
// Propagate type-source information.
64186418
SubstTemplateTypeParmTypeLoc NewTL

clang/test/AST/ast-dump-template-decls.cpp

+8-8
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ template <typename... Cs> struct foo {
136136
};
137137
using t1 = foo<int, short>::bind<char, float>;
138138
// CHECK: TemplateSpecializationType 0x{{[^ ]*}} 'Y<char, float, int, short>' sugar Y
139-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar
139+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar pack_index 0
140140
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack
141-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar
141+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar pack_index 1
142142
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack
143-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar
143+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar pack_index 2
144144
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack
145-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar
145+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar pack_index 3
146146
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack
147147

148148
template <typename... T> struct D {
@@ -152,13 +152,13 @@ using t2 = D<float, char>::B<int, short>;
152152
// CHECK: TemplateSpecializationType 0x{{[^ ]*}} 'B<int, short>' sugar alias B
153153
// CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (int (*)(float, int), int (*)(char, short))' cdecl
154154
// CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (float, int)' cdecl
155-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar
155+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar pack_index 0
156156
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack
157-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar
157+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar pack_index 0
158158
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'U' dependent contains_unexpanded_pack depth 0 index 0 pack
159159
// CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (char, short)' cdecl
160-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar
160+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar pack_index 1
161161
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack
162-
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar
162+
// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar pack_index 1
163163
// CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'U' dependent contains_unexpanded_pack depth 0 index 0 pack
164164
} // namespace PR56099

clang/unittests/AST/ASTImporterTest.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -4793,6 +4793,44 @@ TEST_P(ASTImporterOptionSpecificTestBase,
47934793
ToD2->getDeclContext(), ToD2->getTemplateParameters()->getParam(0)));
47944794
}
47954795

4796+
TEST_P(ASTImporterOptionSpecificTestBase, ImportSubstTemplateTypeParmType) {
4797+
constexpr auto Code = R"(
4798+
template <class A1, class... A2> struct A {
4799+
using B = A1(A2...);
4800+
};
4801+
template struct A<void, char, float, int, short>;
4802+
)";
4803+
Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cpp");
4804+
auto *FromClass = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
4805+
FromTU, classTemplateSpecializationDecl());
4806+
4807+
auto testType = [&](ASTContext &Ctx, const char *Name,
4808+
llvm::Optional<unsigned> PackIndex) {
4809+
const auto *Subst = selectFirst<SubstTemplateTypeParmType>(
4810+
"sttp", match(substTemplateTypeParmType(
4811+
hasReplacementType(hasCanonicalType(asString(Name))))
4812+
.bind("sttp"),
4813+
Ctx));
4814+
const char *ExpectedTemplateParamName = PackIndex ? "A2" : "A1";
4815+
ASSERT_TRUE(Subst);
4816+
ASSERT_EQ(Subst->getReplacedParameter()->getIdentifier()->getName(),
4817+
ExpectedTemplateParamName);
4818+
ASSERT_EQ(Subst->getPackIndex(), PackIndex);
4819+
};
4820+
auto tests = [&](ASTContext &Ctx) {
4821+
testType(Ctx, "void", None);
4822+
testType(Ctx, "char", 0);
4823+
testType(Ctx, "float", 1);
4824+
testType(Ctx, "int", 2);
4825+
testType(Ctx, "short", 3);
4826+
};
4827+
4828+
tests(FromTU->getASTContext());
4829+
4830+
ClassTemplateSpecializationDecl *ToClass = Import(FromClass, Lang_CXX11);
4831+
tests(ToClass->getASTContext());
4832+
}
4833+
47964834
const AstTypeMatcher<SubstTemplateTypeParmPackType>
47974835
substTemplateTypeParmPackType;
47984836

0 commit comments

Comments
 (0)