diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -125,6 +125,9 @@ - Fixes to builtin template emulation of regular templates. `Issue 42102 `_ `Issue 51928 `_ +- A SubstTemplateTypeParmType can now represent the pack index for a + substitution from an expanded pack. + `Issue 56099 `_ Improvements to Clang's diagnostics 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 @@ -1617,10 +1617,11 @@ QualType Wrapped); QualType getSubstTemplateTypeParmType(const TemplateTypeParmType *Replaced, - QualType Replacement) const; - QualType getSubstTemplateTypeParmPackType( - const TemplateTypeParmType *Replaced, - const TemplateArgument &ArgPack); + QualType Replacement, + Optional PackIndex) const; + QualType + getSubstTemplateTypeParmPackType(const TemplateTypeParmType *Replaced, + const TemplateArgument &ArgPack); QualType getTemplateTypeParmType(unsigned Depth, unsigned Index, diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -220,6 +220,7 @@ void VisitUnaryTransformType(const UnaryTransformType *UTT); void VisitTagType(const TagType *TT); void VisitTemplateTypeParmType(const TemplateTypeParmType *TTPT); + void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *STTPT); void VisitAutoType(const AutoType *AT); void VisitTemplateSpecializationType(const TemplateSpecializationType *TST); void VisitInjectedClassNameType(const InjectedClassNameType *ICNT); diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -317,6 +317,7 @@ void VisitUnaryTransformType(const UnaryTransformType *T); void VisitTagType(const TagType *T); void VisitTemplateTypeParmType(const TemplateTypeParmType *T); + void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T); void VisitAutoType(const AutoType *T); void VisitDeducedTemplateSpecializationType( const DeducedTemplateSpecializationType *T); 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 @@ -1793,6 +1793,19 @@ unsigned NumArgs; }; + class SubstTemplateTypeParmTypeBitfields { + friend class SubstTemplateTypeParmType; + + unsigned : NumTypeBits; + + /// 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 { friend class SubstTemplateTypeParmPackType; @@ -1874,6 +1887,7 @@ TypeWithKeywordBitfields TypeWithKeywordBits; ElaboratedTypeBitfields ElaboratedTypeBits; VectorTypeBitfields VectorTypeBits; + SubstTemplateTypeParmTypeBitfields SubstTemplateTypeParmTypeBits; SubstTemplateTypeParmPackTypeBitfields SubstTemplateTypeParmPackTypeBits; TemplateSpecializationTypeBitfields TemplateSpecializationTypeBits; DependentTemplateSpecializationTypeBitfields @@ -4977,9 +4991,8 @@ // The original type parameter. const TemplateTypeParmType *Replaced; - SubstTemplateTypeParmType(const TemplateTypeParmType *Param, QualType Canon) - : Type(SubstTemplateTypeParm, Canon, Canon->getDependence()), - Replaced(Param) {} + SubstTemplateTypeParmType(const TemplateTypeParmType *Param, QualType Canon, + Optional PackIndex); public: /// Gets the template parameter that was substituted for. @@ -4993,18 +5006,25 @@ return getCanonicalTypeInternal(); } + 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, getReplacedParameter(), getReplacementType()); + Profile(ID, getReplacedParameter(), getReplacementType(), getPackIndex()); } static void Profile(llvm::FoldingSetNodeID &ID, const TemplateTypeParmType *Replaced, - QualType Replacement) { + QualType Replacement, Optional PackIndex) { ID.AddPointer(Replaced); ID.AddPointer(Replacement.getAsOpaquePtr()); + 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 @@ -734,12 +734,15 @@ def : Property<"replacementType", QualType> { let Read = [{ node->getReplacementType() }]; } + def : Property<"PackIndex", Optional> { + let Read = [{ node->getPackIndex() }]; + } def : Creator<[{ // The call to getCanonicalType here existed in ASTReader.cpp, too. return ctx.getSubstTemplateTypeParmType( cast(replacedParameter), - ctx.getCanonicalType(replacementType)); + ctx.getCanonicalType(replacementType), 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 @@ -4754,19 +4754,20 @@ /// Retrieve a substitution-result type. QualType ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm, - QualType Replacement) const { + QualType Replacement, + Optional PackIndex) const { assert(Replacement.isCanonical() && "replacement types must always be canonical"); llvm::FoldingSetNodeID ID; - SubstTemplateTypeParmType::Profile(ID, Parm, Replacement); + SubstTemplateTypeParmType::Profile(ID, Parm, Replacement, PackIndex); void *InsertPos = nullptr; SubstTemplateTypeParmType *SubstParm = SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos); if (!SubstParm) { SubstParm = new (*this, TypeAlignment) - SubstTemplateTypeParmType(Parm, Replacement); + SubstTemplateTypeParmType(Parm, Replacement, PackIndex); Types.push_back(SubstParm); SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos); } @@ -12866,9 +12867,11 @@ const TemplateTypeParmType *PX = SX->getReplacedParameter(); if (PX != SY->getReplacedParameter()) return QualType(); - - return Ctx.getSubstTemplateTypeParmType(PX, - Ctx.getQualifiedType(Underlying)); + auto PackIndex = SX->getPackIndex(); + if (PackIndex != SY->getPackIndex()) + return QualType(); + return Ctx.getSubstTemplateTypeParmType( + PX, Ctx.getQualifiedType(Underlying), 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 @@ -1530,7 +1530,8 @@ return ToReplacementTypeOrErr.takeError(); return Importer.getToContext().getSubstTemplateTypeParmType( - *ReplacedOrErr, ToReplacementTypeOrErr->getCanonicalType()); + *ReplacedOrErr, ToReplacementTypeOrErr->getCanonicalType(), + 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 @@ -1062,6 +1062,8 @@ if (!IsStructurallyEquivalent(Context, Subst1->getReplacementType(), Subst2->getReplacementType())) 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 @@ -683,6 +683,12 @@ JOS.attribute("decl", createBareDeclRef(TTPT->getDecl())); } +void JSONNodeDumper::VisitSubstTemplateTypeParmType( + const SubstTemplateTypeParmType *STTPT) { + if (auto PackIndex = STTPT->getPackIndex()) + JOS.attribute("pack_index", *PackIndex); +} + void JSONNodeDumper::VisitAutoType(const AutoType *AT) { JOS.attribute("undeduced", !AT->isDeduced()); switch (AT->getKeyword()) { 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 @@ -1570,6 +1570,12 @@ dumpDeclRef(T->getDecl()); } +void TextNodeDumper::VisitSubstTemplateTypeParmType( + const SubstTemplateTypeParmType *T) { + if (auto PackIndex = T->getPackIndex()) + OS << " pack_index " << *PackIndex; +} + void TextNodeDumper::VisitAutoType(const AutoType *T) { if (T->isDecltypeAuto()) OS << " decltype(auto)"; 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 @@ -1167,7 +1167,7 @@ return QualType(T, 0); return Ctx.getSubstTemplateTypeParmType(T->getReplacedParameter(), - replacementType); + replacementType, T->getPackIndex()); } // FIXME: Non-trivial to implement, but important for C++ @@ -3649,6 +3649,14 @@ return isCanonicalUnqualified() ? nullptr : getDecl()->getIdentifier(); } +SubstTemplateTypeParmType::SubstTemplateTypeParmType( + const TemplateTypeParmType *Param, QualType Canon, + Optional PackIndex) + : Type(SubstTemplateTypeParm, Canon, Canon->getDependence()), + Replaced(Param) { + SubstTemplateTypeParmTypeBits.PackIndex = PackIndex ? *PackIndex + 1 : 0; +} + SubstTemplateTypeParmPackType::SubstTemplateTypeParmPackType( const TemplateTypeParmType *Param, QualType Canon, const TemplateArgument &ArgPack) 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 @@ -3505,12 +3505,14 @@ TemplateParameterList *TPL = BTD->getTemplateParameters(); // Wrap the type in substitution sugar. - auto getSubstType = [&](unsigned IndexReplaced, QualType Replacement) { + auto getSubstType = [&](QualType Replacement, unsigned IndexReplaced, + Optional PackIndexReplaced) { QualType TTP = SemaRef.Context.getTemplateTypeParmType( 0, IndexReplaced, false, cast(TPL->getParam(IndexReplaced))); return SemaRef.Context.getSubstTemplateTypeParmType( - cast(TTP), Replacement.getCanonicalType()); + cast(TTP), Replacement.getCanonicalType(), + PackIndexReplaced); }; switch (BTD->getBuiltinTemplateKind()) { @@ -3535,7 +3537,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( @@ -3590,8 +3592,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 @@ -1813,6 +1813,7 @@ return NewT; } + Optional PackIndex; if (T->isParameterPack()) { assert(Arg.getKind() == TemplateArgument::Pack && "Missing argument pack"); @@ -1829,6 +1830,8 @@ return Result; } + // PackIndex starts from last element. + PackIndex = Arg.pack_size() - 1 - getSema().ArgumentPackSubstitutionIndex; Arg = getPackSubstitutedTemplateArgument(getSema(), Arg); } @@ -1838,8 +1841,8 @@ QualType Replacement = Arg.getAsType(); // TODO: only do this uniquing once, at the start of instantiation. - QualType Result - = getSema().Context.getSubstTemplateTypeParmType(T, Replacement); + QualType Result = getSema().Context.getSubstTemplateTypeParmType( + T, Replacement, PackIndex); SubstTemplateTypeParmTypeLoc NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); @@ -1875,13 +1878,13 @@ return TL.getType(); } - TemplateArgument Arg = TL.getTypePtr()->getArgumentPack(); - Arg = getPackSubstitutedTemplateArgument(getSema(), Arg); - QualType Result = Arg.getAsType(); - - Result = getSema().Context.getSubstTemplateTypeParmType( - TL.getTypePtr()->getReplacedParameter(), - Result); + const SubstTemplateTypeParmPackType *T = TL.getTypePtr(); + TemplateArgument Pack = T->getArgumentPack(); + TemplateArgument Arg = getPackSubstitutedTemplateArgument(getSema(), Pack); + // PackIndex starts from last element. + QualType Result = getSema().Context.getSubstTemplateTypeParmType( + T->getReplacedParameter(), Arg.getAsType(), + 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 @@ -4852,7 +4852,8 @@ Replacement = SemaRef.Context.getQualifiedType( Replacement.getUnqualifiedType(), Qs); T = SemaRef.Context.getSubstTemplateTypeParmType( - SubstTypeParam->getReplacedParameter(), Replacement); + SubstTypeParam->getReplacedParameter(), Replacement, + SubstTypeParam->getPackIndex()); } else if ((AutoTy = dyn_cast(T)) && AutoTy->isDeduced()) { // 'auto' types behave the same way as template parameters. QualType Deduced = AutoTy->getDeducedType(); @@ -6409,9 +6410,8 @@ // Always canonicalize the replacement type. Replacement = SemaRef.Context.getCanonicalType(Replacement); - QualType Result - = SemaRef.Context.getSubstTemplateTypeParmType(T->getReplacedParameter(), - Replacement); + QualType Result = SemaRef.Context.getSubstTemplateTypeParmType( + T->getReplacedParameter(), Replacement, 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 +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar pack_index 3 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar pack_index 2 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar pack_index 1 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar pack_index 0 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'Bs' dependent contains_unexpanded_pack depth 0 index 0 pack 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 +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'float' sugar pack_index 1 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'int' sugar pack_index 1 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'U' dependent contains_unexpanded_pack depth 0 index 0 pack // CHECK: FunctionProtoType 0x{{[^ ]*}} 'int (char, short)' cdecl -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'char' sugar pack_index 0 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'T' dependent contains_unexpanded_pack depth 0 index 0 pack -// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar +// CHECK: SubstTemplateTypeParmType 0x{{[^ ]*}} 'short' sugar pack_index 0 // CHECK-NEXT: TemplateTypeParmType 0x{{[^ ]*}} 'U' dependent contains_unexpanded_pack depth 0 index 0 pack } // 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 +// CHECK-NEXT: `-SubstTemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'int' sugar pack_index 0 // CHECK-NEXT: |-TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'auto' dependent depth 0 index 1 // CHECK-NEXT: | `-TemplateTypeParm 0x{{[0-9A-Fa-f]+}} '' // 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;