diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1615,8 +1615,8 @@ QualType Wrapped); QualType getSubstTemplateTypeParmType(QualType Replacement, - Decl *ReplacedDecl, - unsigned Index) const; + Decl *ReplacedDecl, unsigned Index, + Optional PackIndex) const; QualType getSubstTemplateTypeParmPackType(Decl *ReplacedDecl, unsigned Index, const TemplateArgument &ArgPack); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1801,7 +1801,13 @@ unsigned HasNonCanonicalUnderlyingType : 1; // The index of the template parameter this substitution represents. - unsigned Index : 16; + unsigned Index : 15; + + /// Represents the index within a pack if this represents a substitution + /// from a pack expansion. + /// Positive non-zero number represents the index + 1. + /// Zero means this is not substituted from an expansion. + unsigned PackIndex : 16; }; class SubstTemplateTypeParmPackTypeBitfields { @@ -4993,7 +4999,7 @@ Decl *ReplacedDecl; SubstTemplateTypeParmType(QualType Replacement, Decl *ReplacedDecl, - unsigned Index); + unsigned Index, Optional PackIndex); public: /// Gets the type that was substituted for the template @@ -5011,18 +5017,27 @@ unsigned getIndex() const { return SubstTemplateTypeParmTypeBits.Index; } + Optional getPackIndex() const { + if (SubstTemplateTypeParmTypeBits.PackIndex == 0) + return None; + return SubstTemplateTypeParmTypeBits.PackIndex - 1; + } + bool isSugared() const { return true; } QualType desugar() const { return getReplacementType(); } void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getReplacementType(), getReplacedDecl(), getIndex()); + Profile(ID, getReplacementType(), getReplacedDecl(), getIndex(), + getPackIndex()); } static void Profile(llvm::FoldingSetNodeID &ID, QualType Replacement, - const Decl *ReplacedDecl, unsigned Index) { + const Decl *ReplacedDecl, unsigned Index, + Optional PackIndex) { ID.AddPointer(ReplacedDecl); Replacement.Profile(ID); ID.AddInteger(Index); + ID.AddInteger(PackIndex ? *PackIndex - 1 : 0); } static bool classof(const Type *T) { diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -737,11 +737,14 @@ def : Property<"Index", UInt32> { let Read = [{ node->getIndex() }]; } + def : Property<"PackIndex", Optional> { + let Read = [{ node->getPackIndex() }]; + } // The call to getCanonicalType here existed in ASTReader.cpp, too. def : Creator<[{ return ctx.getSubstTemplateTypeParmType( - replacementType, replacedDecl, Index); + replacementType, replacedDecl, Index, PackIndex); }]>; } diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4749,11 +4749,13 @@ } /// Retrieve a substitution-result type. -QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement, - Decl *ReplacedDecl, - unsigned Index) const { +QualType +ASTContext::getSubstTemplateTypeParmType(QualType Replacement, + Decl *ReplacedDecl, unsigned Index, + Optional PackIndex) const { llvm::FoldingSetNodeID ID; - SubstTemplateTypeParmType::Profile(ID, Replacement, ReplacedDecl, Index); + SubstTemplateTypeParmType::Profile(ID, Replacement, ReplacedDecl, Index, + PackIndex); void *InsertPos = nullptr; SubstTemplateTypeParmType *SubstParm = SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos); @@ -4762,8 +4764,8 @@ void *Mem = Allocate(SubstTemplateTypeParmType::totalSizeToAlloc( !Replacement.isCanonical()), TypeAlignment); - SubstParm = - new (Mem) SubstTemplateTypeParmType(Replacement, ReplacedDecl, Index); + SubstParm = new (Mem) + SubstTemplateTypeParmType(Replacement, ReplacedDecl, Index, PackIndex); Types.push_back(SubstParm); SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos); } @@ -12810,11 +12812,14 @@ unsigned Index = SX->getIndex(); if (Index != SY->getIndex()) return QualType(); + auto PackIndex = SX->getPackIndex(); + if (Index != SY->getPackIndex()) + return QualType(); Decl *CD = ::getCommonDecl(SX->getReplacedDecl(), SY->getReplacedDecl()); if (!CD) return QualType(); return Ctx.getSubstTemplateTypeParmType(Ctx.getQualifiedType(Underlying), - CD, Index); + CD, Index, PackIndex); } case Type::ObjCTypeParam: // FIXME: Try to merge these. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1529,7 +1529,8 @@ return ToReplacementTypeOrErr.takeError(); return Importer.getToContext().getSubstTemplateTypeParmType( - *ToReplacementTypeOrErr, *ReplacedOrErr, T->getIndex()); + *ToReplacementTypeOrErr, *ReplacedOrErr, T->getIndex(), + T->getPackIndex()); } ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmPackType( diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1063,6 +1063,8 @@ return false; if (Subst1->getIndex() != Subst2->getIndex()) return false; + if (Subst1->getPackIndex() != Subst2->getPackIndex()) + return false; break; } diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -686,6 +686,8 @@ void JSONNodeDumper::VisitSubstTemplateTypeParmType( const SubstTemplateTypeParmType *STTPT) { JOS.attribute("index", STTPT->getIndex()); + if (auto PackIndex = STTPT->getPackIndex()) + JOS.attribute("pack_index", *PackIndex); } void JSONNodeDumper::VisitSubstTemplateTypeParmPackType( diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1574,6 +1574,8 @@ const SubstTemplateTypeParmType *T) { dumpDeclRef(T->getReplacedDecl()); VisitTemplateTypeParmDecl(T->getReplacedParameter()); + if (auto PackIndex = T->getPackIndex()) + OS << " pack_index " << *PackIndex; } void TextNodeDumper::VisitSubstTemplateTypeParmPackType( diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1166,8 +1166,9 @@ == T->getReplacementType().getAsOpaquePtr()) return QualType(T, 0); - return Ctx.getSubstTemplateTypeParmType( - replacementType, T->getReplacedDecl(), T->getIndex()); + return Ctx.getSubstTemplateTypeParmType(replacementType, + T->getReplacedDecl(), T->getIndex(), + T->getPackIndex()); } // FIXME: Non-trivial to implement, but important for C++ @@ -3712,9 +3713,9 @@ getReplacedTemplateParameterList(D)->getParam(Index)); } -SubstTemplateTypeParmType::SubstTemplateTypeParmType(QualType Replacement, - Decl *ReplacedDecl, - unsigned Index) +SubstTemplateTypeParmType::SubstTemplateTypeParmType( + QualType Replacement, Decl *ReplacedDecl, unsigned Index, + Optional PackIndex) : Type(SubstTemplateTypeParm, Replacement.getCanonicalType(), Replacement->getDependence()), ReplacedDecl(ReplacedDecl) { @@ -3724,6 +3725,7 @@ *getTrailingObjects() = Replacement; SubstTemplateTypeParmTypeBits.Index = Index; + SubstTemplateTypeParmTypeBits.PackIndex = PackIndex ? *PackIndex + 1 : 0; assert(ReplacedDecl != nullptr); assert(getReplacedParameter() != nullptr); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1821,6 +1821,7 @@ Decl *ReplacedDecl = TemplateArgs.getReplacedDecl(T->getDepth()); + Optional PackIndex; if (T->isParameterPack()) { assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack"); @@ -1838,6 +1839,7 @@ } Arg = getPackSubstitutedTemplateArgument(getSema(), Arg); + PackIndex = getSema().ArgumentPackSubstitutionIndex; } assert(Arg.getKind() == TemplateArgument::Type && @@ -1847,7 +1849,7 @@ // TODO: only do this uniquing once, at the start of instantiation. QualType Result = getSema().Context.getSubstTemplateTypeParmType( - Replacement, ReplacedDecl, T->getIndex()); + Replacement, ReplacedDecl, T->getIndex(), PackIndex); SubstTemplateTypeParmTypeLoc NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); @@ -1892,7 +1894,8 @@ getPackSubstitutedTemplateArgument(getSema(), T->getArgumentPack()); QualType Result = getSema().Context.getSubstTemplateTypeParmType( - Arg.getAsType(), NewReplaced, T->getIndex()); + Arg.getAsType(), NewReplaced, T->getIndex(), + getSema().ArgumentPackSubstitutionIndex); SubstTemplateTypeParmTypeLoc NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4853,7 +4853,7 @@ Replacement.getUnqualifiedType(), Qs); T = SemaRef.Context.getSubstTemplateTypeParmType( Replacement, SubstTypeParam->getReplacedDecl(), - SubstTypeParam->getIndex()); + SubstTypeParam->getIndex(), SubstTypeParam->getPackIndex()); } else if ((AutoTy = dyn_cast(T)) && AutoTy->isDeduced()) { // 'auto' types behave the same way as template parameters. QualType Deduced = AutoTy->getDeducedType(); @@ -6414,7 +6414,7 @@ // Always canonicalize the replacement type. Replacement = SemaRef.Context.getCanonicalType(Replacement); QualType Result = SemaRef.Context.getSubstTemplateTypeParmType( - Replacement, NewReplaced, T->getIndex()); + Replacement, NewReplaced, T->getIndex(), T->getPackIndex()); // Propagate type-source information. SubstTemplateTypeParmTypeLoc NewTL diff --git a/clang/test/AST/ast-dump-template-decls.cpp b/clang/test/AST/ast-dump-template-decls.cpp --- a/clang/test/AST/ast-dump-template-decls.cpp +++ b/clang/test/AST/ast-dump-template-decls.cpp @@ -136,13 +136,13 @@ }; using t1 = foo::bind; // CHECK: TemplateSpecializationType 0x{{[^ ]*}} 'Y' sugar Y -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar typename depth 0 index 0 ... Bs +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar typename depth 0 index 0 ... Bs pack_index 0 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'Z' -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar typename depth 0 index 0 ... Bs +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar typename depth 0 index 0 ... Bs pack_index 1 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'Z' -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar typename depth 0 index 0 ... Bs +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar typename depth 0 index 0 ... Bs pack_index 2 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'Z' -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar typename depth 0 index 0 ... Bs +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar typename depth 0 index 0 ... Bs pack_index 3 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'Z' template struct D { @@ -152,13 +152,13 @@ // CHECK: TemplateSpecializationType 0x{{[^ ]*}} 'B' sugar alias B // CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (int (*)(float, int), int (*)(char, short))' cdecl // CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (float, int)' cdecl -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar typename depth 0 index 0 ... T +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar typename depth 0 index 0 ... T pack_index 0 // CHECK-NEXT: ClassTemplateSpecialization 0x{{[^ ]*}} 'D' -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar typename depth 0 index 0 ... U +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar typename depth 0 index 0 ... U pack_index 0 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'B' // CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (char, short)' cdecl -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar typename depth 0 index 0 ... T +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar typename depth 0 index 0 ... T pack_index 1 // CHECK-NEXT: ClassTemplateSpecialization 0x{{[^ ]*}} 'D' -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar typename depth 0 index 0 ... U +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar typename depth 0 index 0 ... U pack_index 1 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'B' } // namespace PR56099 diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4793,6 +4793,44 @@ ToD2->getDeclContext(), ToD2->getTemplateParameters()->getParam(0))); } +TEST_P(ASTImporterOptionSpecificTestBase, ImportSubstTemplateTypeParmType) { + constexpr auto Code = R"( + template struct A { + using B = A1(A2...); + }; + template struct A; + )"; + Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cpp"); + auto *FromClass = FirstDeclMatcher().match( + FromTU, classTemplateSpecializationDecl()); + + auto testType = [&](ASTContext &Ctx, const char *Name, + llvm::Optional PackIndex) { + const auto *Subst = selectFirst( + "sttp", match(substTemplateTypeParmType( + hasReplacementType(hasCanonicalType(asString(Name)))) + .bind("sttp"), + Ctx)); + const char *ExpectedTemplateParamName = PackIndex ? "A2" : "A1"; + ASSERT_TRUE(Subst); + ASSERT_EQ(Subst->getReplacedParameter()->getIdentifier()->getName(), + ExpectedTemplateParamName); + ASSERT_EQ(Subst->getPackIndex(), PackIndex); + }; + auto tests = [&](ASTContext &Ctx) { + testType(Ctx, "void", None); + testType(Ctx, "char", 0); + testType(Ctx, "float", 1); + testType(Ctx, "int", 2); + testType(Ctx, "short", 3); + }; + + tests(FromTU->getASTContext()); + + ClassTemplateSpecializationDecl *ToClass = Import(FromClass, Lang_CXX11); + tests(ToClass->getASTContext()); +} + const AstTypeMatcher substTemplateTypeParmPackType;