From: Douglas Gregor Date: Fri, 14 Jan 2011 02:55:32 +0000 (+0000) Subject: Start implementing support for substitution into pack expansions that X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c3069d618f4661d923cb1b5c4525b082fce73b04;p=clang Start implementing support for substitution into pack expansions that involve template parameter packs at multiple template levels that occur within the signatures members of class templates (and partial specializations thereof). This is a work-in-progress that is deficient in several ways, notably: - It only works for template type parameter packs, but we need to also support non-type template parameter packs and template template parameter packs. - It doesn't keep track of the lengths of the substituted argument packs in the expansion, so it can't properly diagnose length mismatches. However, this is a concrete step in the right direction. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123425 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index a0544ef8bd..15bc301bd9 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -102,6 +102,8 @@ class ASTContext { mutable llvm::FoldingSet TemplateTypeParmTypes; mutable llvm::FoldingSet SubstTemplateTypeParmTypes; + mutable llvm::FoldingSet + SubstTemplateTypeParmPackTypes; mutable llvm::ContextualFoldingSet TemplateSpecializationTypes; mutable llvm::FoldingSet ParenTypes; @@ -664,6 +666,9 @@ public: QualType getSubstTemplateTypeParmType(const TemplateTypeParmType *Replaced, QualType Replacement) const; + QualType getSubstTemplateTypeParmPackType( + const TemplateTypeParmType *Replaced, + const TemplateArgument &ArgPack); QualType getTemplateTypeParmType(unsigned Depth, unsigned Index, bool ParameterPack, diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 31bb50dd9a..3d1279b252 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -718,6 +718,7 @@ DEF_TRAVERSE_TYPE(RecordType, { }) DEF_TRAVERSE_TYPE(EnumType, { }) DEF_TRAVERSE_TYPE(TemplateTypeParmType, { }) DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, { }) +DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, { }) DEF_TRAVERSE_TYPE(TemplateSpecializationType, { TRY_TO(TraverseTemplateName(T->getTemplateName())); @@ -925,6 +926,7 @@ DEF_TRAVERSE_TYPELOC(RecordType, { }) DEF_TRAVERSE_TYPELOC(EnumType, { }) DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, { }) DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, { }) +DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, { }) // FIXME: use the loc for the template name? DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, { diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 19a16a3437..c081a161de 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2860,6 +2860,59 @@ public: static bool classof(const SubstTemplateTypeParmType *T) { return true; } }; +/// \brief Represents the result of substituting a set of types for a template +/// type parameter pack. +/// +/// When a pack expansion in the source code contains multiple parameter packs +/// and those parameter packs correspond to different levels of template +/// parameter lists, this type node is used to represent a template type +/// parameter pack from an outer level, which has already had its argument pack +/// substituted but that still lives within a pack expansion that itself +/// could not be instantiated. When actually performing a substitution into +/// that pack expansion (e.g., when all template parameters have corresponding +/// arguments), this type will be replaced with the \c SubstTemplateTypeParmType +/// at the current pack substitution index. +class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode { + /// \brief The original type parameter. + const TemplateTypeParmType *Replaced; + + /// \brief A pointer to the set of template arguments that this + /// parameter pack is instantiated with. + const TemplateArgument *Arguments; + + /// \brief The number of template arguments in \c Arguments. + unsigned NumArguments; + + SubstTemplateTypeParmPackType(const TemplateTypeParmType *Param, + QualType Canon, + const TemplateArgument &ArgPack); + + friend class ASTContext; + +public: + IdentifierInfo *getName() const { return Replaced->getName(); } + + /// Gets the template parameter that was substituted for. + const TemplateTypeParmType *getReplacedParameter() const { + return Replaced; + } + + bool isSugared() const { return false; } + QualType desugar() const { return QualType(this, 0); } + + TemplateArgument getArgumentPack() const; + + void Profile(llvm::FoldingSetNodeID &ID); + static void Profile(llvm::FoldingSetNodeID &ID, + const TemplateTypeParmType *Replaced, + const TemplateArgument &ArgPack); + + static bool classof(const Type *T) { + return T->getTypeClass() == SubstTemplateTypeParmPack; + } + static bool classof(const SubstTemplateTypeParmPackType *T) { return true; } +}; + /// \brief Represents the type of a template specialization as written /// in the source code. /// diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 16a8cb40c8..0b8ec0412a 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -592,6 +592,13 @@ class SubstTemplateTypeParmTypeLoc : SubstTemplateTypeParmType> { }; + /// \brief Wrapper for substituted template type parameters. +class SubstTemplateTypeParmPackTypeLoc : + public InheritingConcreteTypeLoc { +}; + struct AttributedLocInfo { union { Expr *ExprOperand; diff --git a/include/clang/AST/TypeNodes.def b/include/clang/AST/TypeNodes.def index 52b8f831ac..3587767f8f 100644 --- a/include/clang/AST/TypeNodes.def +++ b/include/clang/AST/TypeNodes.def @@ -91,6 +91,7 @@ NON_CANONICAL_TYPE(Elaborated, Type) NON_CANONICAL_TYPE(Attributed, Type) DEPENDENT_TYPE(TemplateTypeParm, Type) NON_CANONICAL_TYPE(SubstTemplateTypeParm, Type) +DEPENDENT_TYPE(SubstTemplateTypeParmPack, Type) NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TemplateSpecialization, Type) DEPENDENT_TYPE(InjectedClassName, Type) DEPENDENT_TYPE(DependentName, Type) diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index ea93d2e1d6..0ea9132915 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -555,7 +555,9 @@ namespace clang { /// \brief A PackExpansionType record. TYPE_PACK_EXPANSION = 35, /// \brief An AttributedType record. - TYPE_ATTRIBUTED = 36 + TYPE_ATTRIBUTED = 36, + /// \brief A SubstTemplateTypeParmPackType record. + TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK = 37 }; /// \brief The type IDs for special types constructed by semantic diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 3abfe22caa..35d4b7a097 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1932,6 +1932,42 @@ ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm, return QualType(SubstParm, 0); } +/// \brief Retrieve a +QualType ASTContext::getSubstTemplateTypeParmPackType( + const TemplateTypeParmType *Parm, + const TemplateArgument &ArgPack) { +#ifndef NDEBUG + for (TemplateArgument::pack_iterator P = ArgPack.pack_begin(), + PEnd = ArgPack.pack_end(); + P != PEnd; ++P) { + assert(P->getKind() == TemplateArgument::Type &&"Pack contains a non-type"); + assert(P->getAsType().isCanonical() && "Pack contains non-canonical type"); + } +#endif + + llvm::FoldingSetNodeID ID; + SubstTemplateTypeParmPackType::Profile(ID, Parm, ArgPack); + void *InsertPos = 0; + if (SubstTemplateTypeParmPackType *SubstParm + = SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(SubstParm, 0); + + QualType Canon; + if (!Parm->isCanonicalUnqualified()) { + Canon = getCanonicalType(QualType(Parm, 0)); + Canon = getSubstTemplateTypeParmPackType(cast(Canon), + ArgPack); + SubstTemplateTypeParmPackTypes.FindNodeOrInsertPos(ID, InsertPos); + } + + SubstTemplateTypeParmPackType *SubstParm + = new (*this, TypeAlignment) SubstTemplateTypeParmPackType(Parm, Canon, + ArgPack); + Types.push_back(SubstParm); + SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos); + return QualType(SubstParm, 0); +} + /// \brief Retrieve the template type parameter type for a template /// parameter or parameter pack with the given depth, index, and (optionally) /// name. diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 8daba88ea8..0ebd7da679 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -1383,6 +1383,11 @@ void CXXNameMangler::mangleType(const TemplateTypeParmType *T) { mangleTemplateParameter(T->getIndex()); } +// ::= +void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) { + mangleTemplateParameter(T->getReplacedParameter()->getIndex()); +} + // ::= P # pointer-to void CXXNameMangler::mangleType(const PointerType *T) { Out << 'P'; diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index ef50899e43..ce53fc2174 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -1011,6 +1011,12 @@ void MicrosoftCXXNameMangler::mangleType(const TemplateTypeParmType *T) { assert(false && "Don't know how to mangle TemplateTypeParmTypes yet!"); } +void MicrosoftCXXNameMangler::mangleType( + const SubstTemplateTypeParmPackType *T) { + assert(false && + "Don't know how to mangle SubstTemplateTypeParmPackTypes yet!"); +} + // ::= // ::= void MicrosoftCXXNameMangler::mangleType(const PointerType *T) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d96993e7bd..3882f4960a 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1244,6 +1244,34 @@ bool EnumType::classof(const TagType *TT) { return isa(TT->getDecl()); } +SubstTemplateTypeParmPackType:: +SubstTemplateTypeParmPackType(const TemplateTypeParmType *Param, + QualType Canon, + const TemplateArgument &ArgPack) + : Type(SubstTemplateTypeParmPack, Canon, true, false, true), Replaced(Param), + Arguments(ArgPack.pack_begin()), NumArguments(ArgPack.pack_size()) +{ +} + +TemplateArgument SubstTemplateTypeParmPackType::getArgumentPack() const { + return TemplateArgument(Arguments, NumArguments); +} + +void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getReplacedParameter(), getArgumentPack()); +} + +void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID, + const TemplateTypeParmType *Replaced, + const TemplateArgument &ArgPack) { + ID.AddPointer(Replaced); + ID.AddInteger(ArgPack.pack_size()); + for (TemplateArgument::pack_iterator P = ArgPack.pack_begin(), + PEnd = ArgPack.pack_end(); + P != PEnd; ++P) + ID.AddPointer(P->getAsType().getAsOpaquePtr()); +} + bool TemplateSpecializationType:: anyDependentTemplateArguments(const TemplateArgumentListInfo &Args) { return anyDependentTemplateArguments(Args.getArgumentArray(), Args.size()); diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index ef9704ec85..8a740d4ad8 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -568,6 +568,12 @@ void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T, print(T->getReplacementType(), S); } +void TypePrinter::printSubstTemplateTypeParmPack( + const SubstTemplateTypeParmPackType *T, + std::string &S) { + printTemplateTypeParm(T->getReplacedParameter(), S); +} + void TypePrinter::printTemplateSpecialization( const TemplateSpecializationType *T, std::string &S) { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 868458c2a9..6a37482d66 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2713,6 +2713,11 @@ bool UnnamedLocalNoLinkageFinder::VisitTemplateTypeParmType( return false; } +bool UnnamedLocalNoLinkageFinder::VisitSubstTemplateTypeParmPackType( + const SubstTemplateTypeParmPackType *) { + return false; +} + bool UnnamedLocalNoLinkageFinder::VisitTemplateSpecializationType( const TemplateSpecializationType*) { return false; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 5f181fb5e1..629dc58958 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -712,6 +712,12 @@ namespace { QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, TemplateTypeParmTypeLoc TL); + /// \brief Transforms an already-substituted template type parameter pack + /// into either itself (if we aren't substituting into its pack expansion) + /// or the appropriate substituted argument. + QualType TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB, + SubstTemplateTypeParmPackTypeLoc TL); + ExprResult TransformCallExpr(CallExpr *CE) { getSema().CallsUndergoingInstantiation.push_back(CE); ExprResult Result = @@ -1024,10 +1030,15 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, "Missing argument pack"); if (getSema().ArgumentPackSubstitutionIndex == -1) { - // FIXME: Variadic templates fun case. - getSema().Diag(TL.getSourceRange().getBegin(), - diag::err_pack_expansion_mismatch_unsupported); - return QualType(); + // We have the template argument pack, but we're not expanding the + // enclosing pack expansion yet. Just save the template argument + // pack for later substitution. + QualType Result + = getSema().Context.getSubstTemplateTypeParmPackType(T, Arg); + SubstTemplateTypeParmPackTypeLoc NewTL + = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; } assert(getSema().ArgumentPackSubstitutionIndex < (int)Arg.pack_size()); @@ -1063,6 +1074,32 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, return Result; } +QualType +TemplateInstantiator::TransformSubstTemplateTypeParmPackType( + TypeLocBuilder &TLB, + SubstTemplateTypeParmPackTypeLoc TL) { + if (getSema().ArgumentPackSubstitutionIndex == -1) { + // We aren't expanding the parameter pack, so just return ourselves. + SubstTemplateTypeParmPackTypeLoc NewTL + = TLB.push(TL.getType()); + NewTL.setNameLoc(TL.getNameLoc()); + return TL.getType(); + } + + const TemplateArgument &ArgPack = TL.getTypePtr()->getArgumentPack(); + unsigned Index = (unsigned)getSema().ArgumentPackSubstitutionIndex; + assert(Index < ArgPack.pack_size() && "Substitution index out-of-range"); + + QualType Result = ArgPack.pack_begin()[Index].getAsType(); + Result = getSema().Context.getSubstTemplateTypeParmType( + TL.getTypePtr()->getReplacedParameter(), + Result); + SubstTemplateTypeParmTypeLoc NewTL + = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; +} + /// \brief Perform substitution on the type T with a given set of template /// arguments. /// diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 473dcb7d1f..dc5013c25d 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -456,6 +456,10 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, std::pair FirstPack; bool HaveFirstPack = false; + // FIXME: Variadic templates. Even if we don't expand, we'd still like to + // return the number of expansions back to the caller, perhaps as an + // llvm::Optional, so that it can be embedded in the pack expansion. This + // is important for the multi-level substitution case. for (unsigned I = 0; I != NumUnexpanded; ++I) { // Compute the depth and index for this parameter pack. unsigned Depth; diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 43d89e2875..4127d5045a 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -3926,6 +3926,13 @@ QualType TreeTransform::TransformSubstTemplateTypeParmType( return TransformTypeSpecType(TLB, TL); } +template +QualType TreeTransform::TransformSubstTemplateTypeParmPackType( + TypeLocBuilder &TLB, + SubstTemplateTypeParmPackTypeLoc TL) { + return TransformTypeSpecType(TLB, TL); +} + template QualType TreeTransform::TransformTemplateSpecializationType( TypeLocBuilder &TLB, diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 487e23f19c..edb79ee808 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2997,6 +2997,15 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { Replacement); } + case TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK: { + unsigned Idx = 0; + QualType Parm = GetType(Record[Idx++]); + TemplateArgument ArgPack = ReadTemplateArgument(*Loc.F, Record, Idx); + return Context->getSubstTemplateTypeParmPackType( + cast(Parm), + ArgPack); + } + case TYPE_INJECTED_CLASS_NAME: { CXXRecordDecl *D = cast(GetDecl(Record[0])); QualType TST = GetType(Record[1]); // probably derivable @@ -3233,6 +3242,10 @@ void TypeLocReader::VisitSubstTemplateTypeParmTypeLoc( SubstTemplateTypeParmTypeLoc TL) { TL.setNameLoc(ReadSourceLocation(Record, Idx)); } +void TypeLocReader::VisitSubstTemplateTypeParmPackTypeLoc( + SubstTemplateTypeParmPackTypeLoc TL) { + TL.setNameLoc(ReadSourceLocation(Record, Idx)); +} void TypeLocReader::VisitTemplateSpecializationTypeLoc( TemplateSpecializationTypeLoc TL) { TL.setTemplateNameLoc(ReadSourceLocation(Record, Idx)); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index d3b103f56c..52cf3829dc 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -243,6 +243,14 @@ ASTTypeWriter::VisitSubstTemplateTypeParmType( Code = TYPE_SUBST_TEMPLATE_TYPE_PARM; } +void +ASTTypeWriter::VisitSubstTemplateTypeParmPackType( + const SubstTemplateTypeParmPackType *T) { + Writer.AddTypeRef(QualType(T->getReplacedParameter(), 0), Record); + Writer.AddTemplateArgument(T->getArgumentPack(), Record); + Code = TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK; +} + void ASTTypeWriter::VisitTemplateSpecializationType( const TemplateSpecializationType *T) { @@ -491,6 +499,10 @@ void TypeLocWriter::VisitSubstTemplateTypeParmTypeLoc( SubstTemplateTypeParmTypeLoc TL) { Writer.AddSourceLocation(TL.getNameLoc(), Record); } +void TypeLocWriter::VisitSubstTemplateTypeParmPackTypeLoc( + SubstTemplateTypeParmPackTypeLoc TL) { + Writer.AddSourceLocation(TL.getNameLoc(), Record); +} void TypeLocWriter::VisitTemplateSpecializationTypeLoc( TemplateSpecializationTypeLoc TL) { Writer.AddSourceLocation(TL.getTemplateNameLoc(), Record); diff --git a/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp b/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp index 649c9b8ef2..ff2134dc33 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/multi-level-substitution.cpp @@ -11,3 +11,23 @@ struct X0 { void test_X0() { X0().f<1, 2, 3, 4, 5>(); } + +namespace PacksAtDifferentLevels { + template struct tuple { }; + template struct pair { }; + + template + struct X { + template struct Inner; + + template + struct Inner...> > { + static const unsigned zero = sizeof...(Types) - sizeof...(YTypes); + }; + }; + + int check0[X::Inner, + pair, + pair> + >::zero == 0? 1 : -1]; +}