From: Douglas Gregor Date: Wed, 19 Jan 2011 20:10:05 +0000 (+0000) Subject: Implement support for non-type template parameter packs whose type is X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6952f1e4256c5b43aee5e98cea4e9b663bd1d413;p=clang Implement support for non-type template parameter packs whose type is a pack expansion, e.g., the parameter pack Values in: template struct Outer { template struct Inner; }; This new implementation approach introduces the notion of an "expanded" non-type template parameter pack, for which we have already expanded the types of the parameter pack (to, say, "int*, float*", for Outer) but have not yet expanded the values. Aside from creating these expanded non-type template parameter packs, this patch updates template argument checking and non-type template parameter pack instantiation to make use of the appropriate types in the parameter pack. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123845 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 3815eb5866..a2247e6f66 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1411,7 +1411,8 @@ public: /// initialized to a given location, which defaults to the empty /// location. TypeSourceInfo * - getTrivialTypeSourceInfo(QualType T, SourceLocation Loc = SourceLocation()); + getTrivialTypeSourceInfo(QualType T, + SourceLocation Loc = SourceLocation()) const; TypeSourceInfo *getNullTypeSourceInfo() { return &NullTypeSourceInfo; } diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 0fa4c705ba..069088124d 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -996,14 +996,30 @@ class NonTypeTemplateParmDecl /// \brief Whether this non-type template parameter is a parameter pack. bool ParameterPack; + /// \brief Whether this non-type template parameter is an "expanded" + /// parameter pack, meaning that its type is a pack expansion and we + /// already know the set of types that expansion expands to. + bool ExpandedParameterPack; + + /// \brief The number of types in an expanded parameter pack. + unsigned NumExpandedTypes; + NonTypeTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, TypeSourceInfo *TInfo) : VarDecl(NonTypeTemplateParm, DC, L, Id, T, TInfo, SC_None, SC_None), TemplateParmPosition(D, P), DefaultArgumentAndInherited(0, false), - ParameterPack(ParameterPack) + ParameterPack(ParameterPack), ExpandedParameterPack(false), + NumExpandedTypes(0) { } + NonTypeTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D, + unsigned P, IdentifierInfo *Id, QualType T, + TypeSourceInfo *TInfo, + const QualType *ExpandedTypes, + unsigned NumExpandedTypes, + TypeSourceInfo **ExpandedTInfos); + friend class ASTDeclReader; public: @@ -1012,6 +1028,12 @@ public: unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, TypeSourceInfo *TInfo); + static NonTypeTemplateParmDecl * + Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D, + unsigned P, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, + const QualType *ExpandedTypes, unsigned NumExpandedTypes, + TypeSourceInfo **ExpandedTInfos); + using TemplateParmPosition::getDepth; using TemplateParmPosition::setDepth; using TemplateParmPosition::getPosition; @@ -1063,6 +1085,54 @@ public: /// \endcode bool isParameterPack() const { return ParameterPack; } + /// \brief Whether this parameter is a non-type template parameter pack + /// that has different types at different positions. + /// + /// A parameter pack is an expanded parameter pack when the original + /// parameter pack's type was itself a pack expansion, and that expansion + /// has already been expanded. For example, given: + /// + /// \code + /// template + /// struct X { + /// template + /// struct Y { /* ... */ }; + /// }; + /// \endcode + /// + /// The parameter pack \c Values has a \c PackExpansionType as its type, + /// which expands \c Types. When \c Types is supplied with template arguments + /// by instantiating \c X, the instantiation of \c Values becomes an + /// expanded parameter pack. For example, instantiating + /// \c X results in \c Values being an expanded parameter + /// pack with expansion types \c int and \c unsigned int. + /// + /// The \c getExpansionType() and \c getExpansionTypeSourceInfo() functions + /// return the expansion types. + bool isExpandedParameterPack() const { return ExpandedParameterPack; } + + /// \brief Retrieves the number of expansion types in an expanded parameter pack. + unsigned getNumExpansionTypes() const { + assert(ExpandedParameterPack && "Not an expansion parameter pack"); + return NumExpandedTypes; + } + + /// \brief Retrieve a particular expansion type within an expanded parameter + /// pack. + QualType getExpansionType(unsigned I) const { + assert(I < NumExpandedTypes && "Out-of-range expansion type index"); + void * const *TypesAndInfos = reinterpret_cast(this + 1); + return QualType::getFromOpaquePtr(TypesAndInfos[2*I]); + } + + /// \brief Retrieve a particular expansion type source info within an + /// expanded parameter pack. + TypeSourceInfo *getExpansionTypeSourceInfo(unsigned I) const { + assert(I < NumExpandedTypes && "Out-of-range expansion type index"); + void * const *TypesAndInfos = reinterpret_cast(this + 1); + return static_cast(TypesAndInfos[2*I+1]); + } + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const NonTypeTemplateParmDecl *D) { return true; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a130df6f7a..2d48dd8cbd 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3028,6 +3028,7 @@ public: NamedDecl *Template, SourceLocation TemplateLoc, SourceLocation RAngleLoc, + unsigned ArgumentPackIndex, llvm::SmallVectorImpl &Converted, CheckTemplateArgumentKind CTAK = CTAK_Specified); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 7a9019f384..bb316f0713 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -728,7 +728,10 @@ namespace clang { /// \brief A record containing CXXBaseSpecifiers. DECL_CXX_BASE_SPECIFIERS, /// \brief A IndirectFieldDecl record. - DECL_INDIRECTFIELD + DECL_INDIRECTFIELD, + /// \brief A NonTypeTemplateParmDecl record that stores an expanded + /// non-type template parameter pack. + DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK }; /// \brief Record codes for each kind of statement or expression. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b1ecb2bc28..d1ac00871a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -69,6 +69,13 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, ID.AddInteger(1); ID.AddBoolean(NTTP->isParameterPack()); ID.AddPointer(NTTP->getType().getAsOpaquePtr()); + if (NTTP->isExpandedParameterPack()) { + ID.AddBoolean(true); + ID.AddInteger(NTTP->getNumExpansionTypes()); + for (unsigned I = 0, N = NTTP->getNumExpansionTypes(); I != N; ++I) + ID.AddPointer(NTTP->getExpansionType(I).getAsOpaquePtr()); + } else + ID.AddBoolean(false); continue; } @@ -104,15 +111,40 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( TTP->getIndex(), 0, false, TTP->isParameterPack())); else if (NonTypeTemplateParmDecl *NTTP - = dyn_cast(*P)) - CanonParams.push_back( - NonTypeTemplateParmDecl::Create(*this, getTranslationUnitDecl(), - SourceLocation(), NTTP->getDepth(), - NTTP->getPosition(), 0, - getCanonicalType(NTTP->getType()), - NTTP->isParameterPack(), - 0)); - else + = dyn_cast(*P)) { + QualType T = getCanonicalType(NTTP->getType()); + TypeSourceInfo *TInfo = getTrivialTypeSourceInfo(T); + NonTypeTemplateParmDecl *Param; + if (NTTP->isExpandedParameterPack()) { + llvm::SmallVector ExpandedTypes; + llvm::SmallVector ExpandedTInfos; + for (unsigned I = 0, N = NTTP->getNumExpansionTypes(); I != N; ++I) { + ExpandedTypes.push_back(getCanonicalType(NTTP->getExpansionType(I))); + ExpandedTInfos.push_back( + getTrivialTypeSourceInfo(ExpandedTypes.back())); + } + + Param = NonTypeTemplateParmDecl::Create(*this, getTranslationUnitDecl(), + SourceLocation(), + NTTP->getDepth(), + NTTP->getPosition(), 0, + T, + TInfo, + ExpandedTypes.data(), + ExpandedTypes.size(), + ExpandedTInfos.data()); + } else { + Param = NonTypeTemplateParmDecl::Create(*this, getTranslationUnitDecl(), + SourceLocation(), + NTTP->getDepth(), + NTTP->getPosition(), 0, + T, + NTTP->isParameterPack(), + TInfo); + } + CanonParams.push_back(Param); + + } else CanonParams.push_back(getCanonicalTemplateTemplateParmDecl( cast(*P))); } @@ -1071,7 +1103,7 @@ TypeSourceInfo *ASTContext::CreateTypeSourceInfo(QualType T, } TypeSourceInfo *ASTContext::getTrivialTypeSourceInfo(QualType T, - SourceLocation L) { + SourceLocation L) const { TypeSourceInfo *DI = CreateTypeSourceInfo(T); DI->getTypeLoc().initialize(L); return DI; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 156b8496c4..9e2e8763f8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -133,11 +133,21 @@ getLVForTemplateParameterList(const TemplateParameterList *Params) { for (TemplateParameterList::const_iterator P = Params->begin(), PEnd = Params->end(); P != PEnd; ++P) { - if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*P)) + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*P)) { + if (NTTP->isExpandedParameterPack()) { + for (unsigned I = 0, N = NTTP->getNumExpansionTypes(); I != N; ++I) { + QualType T = NTTP->getExpansionType(I); + if (!T->isDependentType()) + LV = merge(LV, T->getLinkageAndVisibility()); + } + continue; + } + if (!NTTP->getType()->isDependentType()) { LV = merge(LV, NTTP->getType()->getLinkageAndVisibility()); continue; } + } if (TemplateTemplateParmDecl *TTP = dyn_cast(*P)) { diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index b6716b34bd..e3820172fe 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -50,24 +50,33 @@ TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc, } unsigned TemplateParameterList::getMinRequiredArguments() const { - unsigned NumRequiredArgs = size(); - iterator Param = const_cast(this)->end(), - ParamBegin = const_cast(this)->begin(); - while (Param != ParamBegin) { - --Param; - - if (!(*Param)->isTemplateParameterPack() && - !(isa(*Param) && - cast(*Param)->hasDefaultArgument()) && - !(isa(*Param) && - cast(*Param)->hasDefaultArgument()) && - !(isa(*Param) && - cast(*Param)->hasDefaultArgument())) + unsigned NumRequiredArgs = 0; + for (iterator P = const_cast(this)->begin(), + PEnd = const_cast(this)->end(); + P != PEnd; ++P) { + if ((*P)->isTemplateParameterPack()) { + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*P)) + if (NTTP->isExpandedParameterPack()) { + NumRequiredArgs += NTTP->getNumExpansionTypes(); + continue; + } + break; - - --NumRequiredArgs; + } + + if (TemplateTypeParmDecl *TTP = dyn_cast(*P)) { + if (TTP->hasDefaultArgument()) + break; + } else if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(*P)) { + if (NTTP->hasDefaultArgument()) + break; + } else if (cast(*P)->hasDefaultArgument()) + break; + + ++NumRequiredArgs; } - + return NumRequiredArgs; } @@ -391,6 +400,28 @@ unsigned TemplateTypeParmDecl::getIndex() const { // NonTypeTemplateParmDecl Method Implementations //===----------------------------------------------------------------------===// +NonTypeTemplateParmDecl::NonTypeTemplateParmDecl(DeclContext *DC, + SourceLocation L, unsigned D, + unsigned P, IdentifierInfo *Id, + QualType T, + TypeSourceInfo *TInfo, + const QualType *ExpandedTypes, + unsigned NumExpandedTypes, + TypeSourceInfo **ExpandedTInfos) + : VarDecl(NonTypeTemplateParm, DC, L, Id, T, TInfo, SC_None, SC_None), + TemplateParmPosition(D, P), DefaultArgumentAndInherited(0, false), + ParameterPack(true), ExpandedParameterPack(true), + NumExpandedTypes(NumExpandedTypes) +{ + if (ExpandedTypes && ExpandedTInfos) { + void **TypesAndInfos = reinterpret_cast(this + 1); + for (unsigned I = 0; I != NumExpandedTypes; ++I) { + TypesAndInfos[2*I] = ExpandedTypes[I].getAsOpaquePtr(); + TypesAndInfos[2*I + 1] = ExpandedTInfos[I]; + } + } +} + NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D, unsigned P, @@ -400,6 +431,22 @@ NonTypeTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, TInfo); } +NonTypeTemplateParmDecl * +NonTypeTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, + SourceLocation L, unsigned D, unsigned P, + IdentifierInfo *Id, QualType T, + TypeSourceInfo *TInfo, + const QualType *ExpandedTypes, + unsigned NumExpandedTypes, + TypeSourceInfo **ExpandedTInfos) { + unsigned Size = sizeof(NonTypeTemplateParmDecl) + + NumExpandedTypes * 2 * sizeof(void*); + void *Mem = C.Allocate(Size); + return new (Mem) NonTypeTemplateParmDecl(DC, L, D, P, Id, T, TInfo, + ExpandedTypes, NumExpandedTypes, + ExpandedTInfos); +} + SourceLocation NonTypeTemplateParmDecl::getDefaultArgumentLoc() const { return hasDefaultArgument() ? getDefaultArgument()->getSourceRange().getBegin() diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 8b12650a47..5a16729d80 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -911,8 +911,11 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { // type-name case tok::annot_typename: { - DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, - getTypeAnnotation(Tok)); + if (getTypeAnnotation(Tok)) + DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, + getTypeAnnotation(Tok)); + else + DS.SetTypeSpecError(); DS.SetRangeEnd(Tok.getAnnotationEndLoc()); ConsumeToken(); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index b2749bf6ce..a164e5c7b8 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5967,8 +5967,6 @@ bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) { cast(Params->getParam(0)); // The template parameter must be a char parameter pack. - // FIXME: This test will always fail because non-type parameter packs - // have not been implemented. if (PmDecl && PmDecl->isTemplateParameterPack() && Context.hasSameType(PmDecl->getType(), Context.CharTy)) Valid = true; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index f97c650d96..ad08a11f20 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2197,11 +2197,36 @@ Sema::SubstDefaultTemplateArgumentIfAvailable(TemplateDecl *Template, /// \brief Check that the given template argument corresponds to the given /// template parameter. +/// +/// \param Param The template parameter against which the argument will be +/// checked. +/// +/// \param Arg The template argument. +/// +/// \param Template The template in which the template argument resides. +/// +/// \param TemplateLoc The location of the template name for the template +/// whose argument list we're matching. +/// +/// \param RAngleLoc The location of the right angle bracket ('>') that closes +/// the template argument list. +/// +/// \param ArgumentPackIndex The index into the argument pack where this +/// argument will be placed. Only valid if the parameter is a parameter pack. +/// +/// \param Converted The checked, converted argument will be added to the +/// end of this small vector. +/// +/// \param CTAK Describes how we arrived at this particular template argument: +/// explicitly written, deduced, etc. +/// +/// \returns true on error, false otherwise. bool Sema::CheckTemplateArgument(NamedDecl *Param, const TemplateArgumentLoc &Arg, NamedDecl *Template, SourceLocation TemplateLoc, SourceLocation RAngleLoc, + unsigned ArgumentPackIndex, llvm::SmallVectorImpl &Converted, CheckTemplateArgumentKind CTAK) { // Check template type parameters. @@ -2214,6 +2239,9 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, // with the template arguments we've seen thus far. But if the // template has a dependent context then we cannot substitute yet. QualType NTTPType = NTTP->getType(); + if (NTTP->isParameterPack() && NTTP->isExpandedParameterPack()) + NTTPType = NTTP->getExpansionType(ArgumentPackIndex); + if (NTTPType->isDependentType() && !isa(Template) && !Template->getDeclContext()->isDependentContext()) { @@ -2447,9 +2475,28 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, break; if (ArgIdx < NumArgs) { + // If we have an expanded parameter pack, make sure we don't have too + // many arguments. + if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(*Param)) { + if (NTTP->isExpandedParameterPack() && + ArgumentPack.size() >= NTTP->getNumExpansionTypes()) { + Diag(TemplateLoc, diag::err_template_arg_list_different_arity) + << true + << (isa(Template)? 0 : + isa(Template)? 1 : + isa(Template)? 2 : 3) + << Template; + Diag(Template->getLocation(), diag::note_template_decl_here) + << Params->getSourceRange(); + return true; + } + } + // Check the template argument we were given. if (CheckTemplateArgument(*Param, TemplateArgs[ArgIdx], Template, - TemplateLoc, RAngleLoc, Converted)) + TemplateLoc, RAngleLoc, + ArgumentPack.size(), Converted)) return true; if ((*Param)->isTemplateParameterPack()) { @@ -2544,7 +2591,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // Check the default template argument. if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, - RAngleLoc, Converted)) + RAngleLoc, 0, Converted)) return true; // Move to the next template parameter and argument. diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 812c633c58..fc480391ea 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1705,6 +1705,7 @@ static bool ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, DeducedTemplateArgument Arg, NamedDecl *Template, QualType NTTPType, + unsigned ArgumentPackIndex, TemplateDeductionInfo &Info, bool InFunctionTemplate, llvm::SmallVectorImpl &Output) { @@ -1721,8 +1722,8 @@ static bool ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, DeducedTemplateArgument InnerArg(*PA); InnerArg.setDeducedFromArrayBound(Arg.wasDeducedFromArrayBound()); if (ConvertDeducedTemplateArgument(S, Param, InnerArg, Template, - NTTPType, Info, - InFunctionTemplate, Output)) + NTTPType, PackedArgsBuilder.size(), + Info, InFunctionTemplate, Output)) return true; // Move the converted template argument into our argument pack. @@ -1748,6 +1749,7 @@ static bool ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, Template, Template->getLocation(), Template->getSourceRange().getEnd(), + ArgumentPackIndex, Output, InFunctionTemplate ? (Arg.wasDeducedFromArrayBound() @@ -1810,7 +1812,7 @@ FinishTemplateArgumentDeduction(Sema &S, } if (ConvertDeducedTemplateArgument(S, Param, Deduced[I], - Partial, NTTPType, Info, false, + Partial, NTTPType, 0, Info, false, Builder)) { Info.Param = makeTemplateParameter(Param); // FIXME: These template arguments are temporary. Free them! @@ -2160,7 +2162,7 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, } if (ConvertDeducedTemplateArgument(*this, Param, Deduced[I], - FunctionTemplate, NTTPType, Info, + FunctionTemplate, NTTPType, 0, Info, true, Builder)) { Info.Param = makeTemplateParameter(Param); // FIXME: These template arguments are temporary. Free them! @@ -2212,7 +2214,7 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, FunctionTemplate, FunctionTemplate->getLocation(), FunctionTemplate->getSourceRange().getEnd(), - Builder, + 0, Builder, CTAK_Deduced)) { Info.Param = makeTemplateParameter( const_cast(TemplateParams->getParam(I))); diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 0ae8804481..45fde296e3 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1016,9 +1016,19 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, // Derive the type we want the substituted decl to have. This had // better be non-dependent, or these checks will have serious problems. - QualType TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs, - E->getLocation(), - DeclarationName()); + QualType TargetType; + if (NTTP->isExpandedParameterPack()) + TargetType = NTTP->getExpansionType( + getSema().ArgumentPackSubstitutionIndex); + else if (NTTP->isParameterPack() && + isa(NTTP->getType())) { + TargetType = SemaRef.SubstType( + cast(NTTP->getType())->getPattern(), + TemplateArgs, E->getLocation(), + NTTP->getDeclName()); + } else + TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs, + E->getLocation(), NTTP->getDeclName()); assert(!TargetType.isNull() && "type substitution failed for param type"); assert(!TargetType->isDependentType() && "param type still dependent"); return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg, @@ -1038,7 +1048,6 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr( return getSema().Owned(E); } - // FIXME: Variadic templates select Nth from type? const TemplateArgument &ArgPack = E->getArgumentPack(); unsigned Index = (unsigned)getSema().ArgumentPackSubstitutionIndex; assert(Index < ArgPack.pack_size() && "Substitution index out-of-range"); @@ -1058,8 +1067,17 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr( if (!VD) return ExprError(); - return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg, - E->getType(), + QualType T; + NonTypeTemplateParmDecl *NTTP = E->getParameterPack(); + if (NTTP->isExpandedParameterPack()) + T = NTTP->getExpansionType(getSema().ArgumentPackSubstitutionIndex); + else if (const PackExpansionType *Expansion + = dyn_cast(NTTP->getType())) + T = SemaRef.SubstType(Expansion->getPattern(), TemplateArgs, + E->getParameterPackLocation(), NTTP->getDeclName()); + else + T = E->getType(); + return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg, T, E->getParameterPackLocation()); } @@ -1778,6 +1796,11 @@ Sema::InstantiateClassTemplateSpecialization( } } + // If we're dealing with a member template where the template parameters + // have been instantiated, this provides the original template parameters + // from which the member template's parameters were instantiated. + llvm::SmallVector InstantiatedTemplateParameters; + if (Matched.size() >= 1) { llvm::SmallVector::iterator Best = Matched.begin(); if (Matched.size() == 1) { diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index b4ccdd572d..fd7325cc01 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1455,28 +1455,140 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl( Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( NonTypeTemplateParmDecl *D) { // Substitute into the type of the non-type template parameter. + TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc(); + llvm::SmallVector ExpandedParameterPackTypesAsWritten; + llvm::SmallVector ExpandedParameterPackTypes; + bool IsExpandedParameterPack = false; + TypeSourceInfo *DI; QualType T; - TypeSourceInfo *DI = SemaRef.SubstType(D->getTypeSourceInfo(), TemplateArgs, D->getLocation(), - D->getDeclName()); - if (!DI) - return 0; - - T = DI->getType(); - - // Check that this type is acceptable for a non-type template parameter. bool Invalid = false; - T = SemaRef.CheckNonTypeTemplateParameterType(T, D->getLocation()); - if (T.isNull()) { - T = SemaRef.Context.IntTy; - Invalid = true; + + if (D->isExpandedParameterPack()) { + // The non-type template parameter pack is an already-expanded pack + // expansion of types. Substitute into each of the expanded types. + ExpandedParameterPackTypes.reserve(D->getNumExpansionTypes()); + ExpandedParameterPackTypesAsWritten.reserve(D->getNumExpansionTypes()); + for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { + TypeSourceInfo *NewDI =SemaRef.SubstType(D->getExpansionTypeSourceInfo(I), + TemplateArgs, + D->getLocation(), + D->getDeclName()); + if (!NewDI) + return 0; + + ExpandedParameterPackTypesAsWritten.push_back(NewDI); + QualType NewT =SemaRef.CheckNonTypeTemplateParameterType(NewDI->getType(), + D->getLocation()); + if (NewT.isNull()) + return 0; + ExpandedParameterPackTypes.push_back(NewT); + } + + IsExpandedParameterPack = true; + DI = D->getTypeSourceInfo(); + T = DI->getType(); + } else if (isa(TL)) { + // The non-type template parameter pack's type is a pack expansion of types. + // Determine whether we need to expand this parameter pack into separate + // types. + PackExpansionTypeLoc Expansion = cast(TL); + TypeLoc Pattern = Expansion.getPatternLoc(); + llvm::SmallVector Unexpanded; + SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); + + // Determine whether the set of unexpanded parameter packs can and should + // be expanded. + bool Expand = true; + bool RetainExpansion = false; + llvm::Optional OrigNumExpansions + = Expansion.getTypePtr()->getNumExpansions(); + llvm::Optional NumExpansions = OrigNumExpansions; + if (SemaRef.CheckParameterPacksForExpansion(Expansion.getEllipsisLoc(), + Pattern.getSourceRange(), + Unexpanded.data(), + Unexpanded.size(), + TemplateArgs, + Expand, RetainExpansion, + NumExpansions)) + return 0; + + if (Expand) { + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I); + TypeSourceInfo *NewDI = SemaRef.SubstType(Pattern, TemplateArgs, + D->getLocation(), + D->getDeclName()); + if (!NewDI) + return 0; + + ExpandedParameterPackTypesAsWritten.push_back(NewDI); + QualType NewT = SemaRef.CheckNonTypeTemplateParameterType( + NewDI->getType(), + D->getLocation()); + if (NewT.isNull()) + return 0; + ExpandedParameterPackTypes.push_back(NewT); + } + + // Note that we have an expanded parameter pack. The "type" of this + // expanded parameter pack is the original expansion type, but callers + // will end up using the expanded parameter pack types for type-checking. + IsExpandedParameterPack = true; + DI = D->getTypeSourceInfo(); + T = DI->getType(); + } else { + // We cannot fully expand the pack expansion now, so substitute into the + // pattern and create a new pack expansion type. + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, -1); + TypeSourceInfo *NewPattern = SemaRef.SubstType(Pattern, TemplateArgs, + D->getLocation(), + D->getDeclName()); + if (!NewPattern) + return 0; + + DI = SemaRef.CheckPackExpansion(NewPattern, Expansion.getEllipsisLoc(), + NumExpansions); + if (!DI) + return 0; + + T = DI->getType(); + } + } else { + // Simple case: substitution into a parameter that is not a parameter pack. + DI = SemaRef.SubstType(D->getTypeSourceInfo(), TemplateArgs, + D->getLocation(), D->getDeclName()); + if (!DI) + return 0; + + // Check that this type is acceptable for a non-type template parameter. + bool Invalid = false; + T = SemaRef.CheckNonTypeTemplateParameterType(DI->getType(), + D->getLocation()); + if (T.isNull()) { + T = SemaRef.Context.IntTy; + Invalid = true; + } } - // FIXME: Variadic templates. - NonTypeTemplateParmDecl *Param - = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner, D->getLocation(), + NonTypeTemplateParmDecl *Param; + if (IsExpandedParameterPack) + Param = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner, + D->getLocation(), D->getDepth() - TemplateArgs.getNumLevels(), - D->getPosition(), D->getIdentifier(), T, - D->isParameterPack(), DI); + D->getPosition(), + D->getIdentifier(), T, + DI, + ExpandedParameterPackTypes.data(), + ExpandedParameterPackTypes.size(), + ExpandedParameterPackTypesAsWritten.data()); + else + Param = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner, + D->getLocation(), + D->getDepth() - TemplateArgs.getNumLevels(), + D->getPosition(), + D->getIdentifier(), T, + D->isParameterPack(), DI); + if (Invalid) Param->setInvalidDecl(); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 5c66a9680f..757433228f 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1155,13 +1155,21 @@ void ASTDeclReader::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // TemplateParmPosition. D->setDepth(Record[Idx++]); D->setPosition(Record[Idx++]); - // Rest of NonTypeTemplateParmDecl. - D->ParameterPack = Record[Idx++]; - if (Record[Idx++]) { - Expr *DefArg = Reader.ReadExpr(F); - bool Inherited = Record[Idx++]; - D->setDefaultArgument(DefArg, Inherited); - } + if (D->isExpandedParameterPack()) { + void **Data = reinterpret_cast(D + 1); + for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { + Data[2*I] = Reader.GetType(Record[Idx++]).getAsOpaquePtr(); + Data[2*I + 1] = GetTypeSourceInfo(Record, Idx); + } + } else { + // Rest of NonTypeTemplateParmDecl. + D->ParameterPack = Record[Idx++]; + if (Record[Idx++]) { + Expr *DefArg = Reader.ReadExpr(F); + bool Inherited = Record[Idx++]; + D->setDefaultArgument(DefArg, Inherited); + } + } } void ASTDeclReader::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { @@ -1433,6 +1441,11 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) { D = NonTypeTemplateParmDecl::Create(*Context, 0, SourceLocation(), 0,0,0, QualType(), false, 0); break; + case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: + D = NonTypeTemplateParmDecl::Create(*Context, 0, SourceLocation(), 0, 0, + 0, QualType(), 0, 0, Record[Idx++], + 0); + break; case DECL_TEMPLATE_TEMPLATE_PARM: D = TemplateTemplateParmDecl::Create(*Context, 0, SourceLocation(), 0, 0, false, 0, 0); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index c329711ecf..507c66931c 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -988,18 +988,33 @@ void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { } void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + // For an expanded parameter pack, record the number of expansion types here + // so that it's easier for + if (D->isExpandedParameterPack()) + Record.push_back(D->getNumExpansionTypes()); + VisitVarDecl(D); // TemplateParmPosition. Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); - // Rest of NonTypeTemplateParmDecl. - Record.push_back(D->isParameterPack()); - Record.push_back(D->getDefaultArgument() != 0); - if (D->getDefaultArgument()) { - Writer.AddStmt(D->getDefaultArgument()); - Record.push_back(D->defaultArgumentWasInherited()); + + if (D->isExpandedParameterPack()) { + for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { + Writer.AddTypeRef(D->getExpansionType(I), Record); + Writer.AddTypeSourceInfo(D->getExpansionTypeSourceInfo(I), Record); + } + + Code = serialization::DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK; + } else { + // Rest of NonTypeTemplateParmDecl. + Record.push_back(D->isParameterPack()); + Record.push_back(D->getDefaultArgument() != 0); + if (D->getDefaultArgument()) { + Writer.AddStmt(D->getDefaultArgument()); + Record.push_back(D->defaultArgumentWasInherited()); + } + Code = serialization::DECL_NON_TYPE_TEMPLATE_PARM; } - Code = serialization::DECL_NON_TYPE_TEMPLATE_PARM; } void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { 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 8a53f54456..2df6d33a89 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 @@ -1,6 +1,20 @@ // RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s template struct value_tuple {}; +template struct tuple { }; +template struct pair { }; + +template struct value_c; + +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; template struct X0 { @@ -13,8 +27,6 @@ void test_X0() { } namespace PacksAtDifferentLevels { - template struct tuple { }; - template struct pair { }; template struct X { @@ -176,3 +188,31 @@ namespace PacksAtDifferentLevels { add_const>>::value == 0? 1 : -1]; } + +namespace ExpandingNonTypeTemplateParameters { + template + struct tuple_of_values { + template // expected-error{{a non-type template parameter cannot have type 'float'}} \ + // expected-note{{template parameter is declared here}} + struct apply { // expected-note 2{{template is declared here}} + typedef tuple...> type; + }; + }; + + int i; + float f; + int check_tuple_of_values_1[ + is_same::apply + ::type, + tuple, value_c, value_c, + value_c> + >::value? 1 : -1]; + + tuple_of_values tv1; // expected-note{{in instantiation of template class 'ExpandingNonTypeTemplateParameters::tuple_of_values' requested here}} + + tuple_of_values::apply::type tv2; // expected-error{{non-type template parameter of reference type 'float &' cannot bind to template argument of type 'int'}} + + tuple_of_values::apply::type tv3; // expected-error{{too few template arguments for class template 'apply'}} + + tuple_of_values::apply::type tv4; // expected-error{{too many template arguments for class template 'apply'}} +}