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 @@ -1618,8 +1618,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 @@ -1819,7 +1819,14 @@ 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. This index starts at the end of the pack and + /// increments towards the beginning. + /// Positive non-zero number represents the index + 1. + /// Zero means this is not substituted from an expansion. + unsigned PackIndex : 16; }; class SubstTemplateTypeParmPackTypeBitfields { @@ -5033,7 +5040,7 @@ Decl *ReplacedDecl; SubstTemplateTypeParmType(QualType Replacement, Decl *ReplacedDecl, - unsigned Index); + unsigned Index, Optional PackIndex); public: /// Gets the type that was substituted for the template @@ -5051,18 +5058,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 @@ -733,11 +733,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 @@ -4775,11 +4775,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); @@ -4788,8 +4790,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); } @@ -12870,11 +12872,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 @@ -1533,7 +1533,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 @@ -1069,6 +1069,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 @@ -694,6 +694,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 @@ -1578,6 +1578,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++ @@ -3721,9 +3722,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) { @@ -3733,6 +3734,7 @@ *getTrailingObjects() = Replacement; SubstTemplateTypeParmTypeBits.Index = Index; + SubstTemplateTypeParmTypeBits.PackIndex = PackIndex ? *PackIndex + 1 : 0; assert(ReplacedDecl != nullptr); assert(getReplacedParameter() != nullptr); } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3506,9 +3506,10 @@ TemplateParameterList *TPL = BTD->getTemplateParameters(); // Wrap the type in substitution sugar. - auto getSubstType = [&](unsigned IndexReplaced, QualType Replacement) { - return SemaRef.Context.getSubstTemplateTypeParmType(Replacement, BTD, - IndexReplaced); + auto getSubstType = [&](QualType Replacement, unsigned IndexReplaced, + Optional PackIndexReplaced) { + return SemaRef.Context.getSubstTemplateTypeParmType( + Replacement, BTD, IndexReplaced, PackIndexReplaced); }; switch (BTD->getBuiltinTemplateKind()) { @@ -3533,7 +3534,7 @@ TemplateArgumentListInfo SyntheticTemplateArgs; // The type argument, wrapped in substitution sugar, gets reused as the // first template argument in the synthetic template argument list. - QualType SyntheticType = getSubstType(1, OrigType); + QualType SyntheticType = getSubstType(OrigType, 1, None); SyntheticTemplateArgs.addArgument( TemplateArgumentLoc(TemplateArgument(SyntheticType), SemaRef.Context.getTrivialTypeSourceInfo( @@ -3588,8 +3589,9 @@ } // We simply return the type at index `Index`. - auto Nth = std::next(Ts.pack_begin(), Index.getExtValue()); - return getSubstType(1, Nth->getAsType()); + int64_t N = Index.getExtValue(); + return getSubstType(Ts.getPackAsArray()[N].getAsType(), 1, + Ts.pack_size() - 1 - N); } llvm_unreachable("unexpected BuiltinTemplateDecl!"); } 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 @@ -1823,6 +1823,7 @@ Decl *ReplacedDecl = TemplateArgs.getReplacedDecl(T->getDepth()); + Optional PackIndex; if (T->isParameterPack()) { assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack"); @@ -1839,6 +1840,8 @@ return Result; } + // PackIndex starts from last element. + PackIndex = Arg.pack_size() - 1 - getSema().ArgumentPackSubstitutionIndex; Arg = getPackSubstitutedTemplateArgument(getSema(), Arg); } @@ -1849,7 +1852,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()); @@ -1890,11 +1893,14 @@ return Result; } - TemplateArgument Arg = - getPackSubstitutedTemplateArgument(getSema(), T->getArgumentPack()); + TemplateArgument Pack = T->getArgumentPack(); + TemplateArgument Arg = getPackSubstitutedTemplateArgument(getSema(), Pack); + // PackIndex starts from last element. QualType Result = getSema().Context.getSubstTemplateTypeParmType( - Arg.getAsType(), NewReplaced, T->getIndex()); + Arg.getAsType(), NewReplaced, T->getIndex(), + Pack.pack_size() - 1 - 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 3 // 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 2 // 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 1 // 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 0 // 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 1 // 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 1 // 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 0 // 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 0 // CHECK-NEXT: TypeAliasTemplate 0x{{[^ ]*}} 'B' } // namespace PR56099 diff --git a/clang/test/SemaTemplate/type_pack_element.cpp b/clang/test/SemaTemplate/type_pack_element.cpp --- a/clang/test/SemaTemplate/type_pack_element.cpp +++ b/clang/test/SemaTemplate/type_pack_element.cpp @@ -11,7 +11,7 @@ // CHECK-NEXT: | `-IntegerLiteral 0x{{[0-9A-Fa-f]+}} 'int' 0 // CHECK-NEXT: |-TemplateArgument type 'int' // CHECK-NEXT: | `-BuiltinType 0x{{[0-9A-Fa-f]+}} 'int' -// CHECK-NEXT: `-SubstTemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'int' sugar typename depth 0 index 1 ... +// CHECK-NEXT: `-SubstTemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'int' sugar typename depth 0 index 1 ... pack_index 0 // CHECK-NEXT: |-BuiltinTemplate 0x{{[0-9A-Fa-f]+}} '__type_pack_element' // CHECK-NEXT: `-BuiltinType 0x{{[0-9A-Fa-f]+}} 'int' 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", 3); + testType(Ctx, "float", 2); + testType(Ctx, "int", 1); + testType(Ctx, "short", 0); + }; + + tests(FromTU->getASTContext()); + + ClassTemplateSpecializationDecl *ToClass = Import(FromClass, Lang_CXX11); + tests(ToClass->getASTContext()); +} + const AstTypeMatcher substTemplateTypeParmPackType;