From 6964b3f80ce1ba489e7e25e7cd58062699af9b0c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 7 Sep 2012 02:06:42 +0000 Subject: [PATCH] PR9023: A template template parameter whose template parameter list contains an unexpanded parameter pack is a pack expansion. Thus, as with a non-type template parameter which is a pack expansion, it needs to be expanded early into a fixed list of template parameters. Since the expanded list of template parameters is not itself a parameter pack, it is permitted to appear before the end of the template parameter list, so also remove that restriction (for both template template parameter pack expansions and non-type template parameter pack expansions). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163369 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclTemplate.h | 94 +++++++- include/clang/Sema/Sema.h | 3 +- include/clang/Serialization/ASTBitCodes.h | 3 + lib/AST/DeclTemplate.cpp | 58 ++++- lib/Sema/SemaTemplate.cpp | 281 ++++++++++++---------- lib/Sema/SemaTemplateDeduction.cpp | 2 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 108 ++++++++- lib/Serialization/ASTReaderDecl.cpp | 21 +- lib/Serialization/ASTWriterDecl.cpp | 28 ++- test/CXX/temp/temp.param/p15-cxx0x.cpp | 150 ++++++++++++ test/PCH/cxx-variadic-templates.cpp | 4 + test/PCH/cxx-variadic-templates.h | 7 + 12 files changed, 607 insertions(+), 152 deletions(-) diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 7affc7e15f..3cff5b07c4 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -50,7 +50,11 @@ class TemplateParameterList { /// The number of template parameters in this template /// parameter list. - unsigned NumParams; + unsigned NumParams : 31; + + /// Whether this template parameter list contains an unexpanded parameter + /// pack. + unsigned ContainsUnexpandedParameterPack : 1; protected: TemplateParameterList(SourceLocation TemplateLoc, SourceLocation LAngleLoc, @@ -104,6 +108,12 @@ public: /// the second template parameter list will have depth 1, etc. unsigned getDepth() const; + /// \brief Determine whether this template parameter list contains an + /// unexpanded parameter pack. + bool containsUnexpandedParameterPack() const { + return ContainsUnexpandedParameterPack; + } + SourceLocation getTemplateLoc() const { return TemplateLoc; } SourceLocation getLAngleLoc() const { return LAngleLoc; } SourceLocation getRAngleLoc() const { return RAngleLoc; } @@ -1090,8 +1100,17 @@ public: /// \endcode bool isParameterPack() const { return ParameterPack; } + /// \brief Whether this parameter pack is a pack expansion. + /// + /// A non-type template parameter pack is a pack expansion if its type + /// contains an unexpanded parameter pack. In this case, we will have + /// built a PackExpansionType wrapping the type. + bool isPackExpansion() const { + return ParameterPack && getType()->getAs(); + } + /// \brief Whether this parameter is a non-type template parameter pack - /// that has different types at different positions. + /// that has a known list of 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 @@ -1165,23 +1184,47 @@ class TemplateTemplateParmDecl : public TemplateDecl, /// \brief Whether this parameter is a parameter pack. bool ParameterPack; + /// \brief Whether this template template parameter is an "expanded" + /// parameter pack, meaning that it is a pack expansion and we + /// already know the set of template parameters that expansion expands to. + bool ExpandedParameterPack; + + /// \brief The number of parameters in an expanded parameter pack. + unsigned NumExpandedParams; + TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D, unsigned P, bool ParameterPack, IdentifierInfo *Id, TemplateParameterList *Params) : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params), TemplateParmPosition(D, P), DefaultArgument(), - DefaultArgumentWasInherited(false), ParameterPack(ParameterPack) + DefaultArgumentWasInherited(false), ParameterPack(ParameterPack), + ExpandedParameterPack(false), NumExpandedParams(0) { } + TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, + unsigned D, unsigned P, + IdentifierInfo *Id, TemplateParameterList *Params, + unsigned NumExpansions, + TemplateParameterList * const *Expansions); + public: static TemplateTemplateParmDecl *Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D, unsigned P, bool ParameterPack, IdentifierInfo *Id, TemplateParameterList *Params); + static TemplateTemplateParmDecl *Create(const ASTContext &C, DeclContext *DC, + SourceLocation L, unsigned D, + unsigned P, + IdentifierInfo *Id, + TemplateParameterList *Params, + llvm::ArrayRef Expansions); - static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C, + static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C, unsigned ID); + static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C, + unsigned ID, + unsigned NumExpansions); using TemplateParmPosition::getDepth; using TemplateParmPosition::getPosition; @@ -1195,6 +1238,49 @@ public: /// \endcode bool isParameterPack() const { return ParameterPack; } + /// \brief Whether this parameter pack is a pack expansion. + /// + /// A template template parameter pack is a pack expansion if its template + /// parameter list contains an unexpanded parameter pack. + bool isPackExpansion() const { + return ParameterPack && + getTemplateParameters()->containsUnexpandedParameterPack(); + } + + /// \brief Whether this parameter is a template template parameter pack that + /// has a known list of different template parameter lists at different + /// positions. + /// + /// A parameter pack is an expanded parameter pack when the original parameter + /// pack's template parameter list was itself a pack expansion, and that + /// expansion has already been expanded. For exampe, given: + /// + /// \code + /// template struct Outer { + /// template class...Templates> struct Inner; + /// }; + /// \endcode + /// + /// The parameter pack \c Templates is a pack expansion, which expands the + /// pack \c Types. When \c Types is supplied with template arguments by + /// instantiating \c Outer, the instantiation of \c Templates is an expanded + /// parameter pack. + bool isExpandedParameterPack() const { return ExpandedParameterPack; } + + /// \brief Retrieves the number of expansion template parameters in + /// an expanded parameter pack. + unsigned getNumExpansionTemplateParameters() const { + assert(ExpandedParameterPack && "Not an expansion parameter pack"); + return NumExpandedParams; + } + + /// \brief Retrieve a particular expansion type within an expanded parameter + /// pack. + TemplateParameterList *getExpansionTemplateParameters(unsigned I) const { + assert(I < NumExpandedParams && "Out-of-range expansion type index"); + return reinterpret_cast(this + 1)[I]; + } + /// \brief Determine whether this template parameter has a default /// argument. bool hasDefaultArgument() const { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 919bd6883e..97733d37e3 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4832,7 +4832,8 @@ public: TemplateArgument &Converted, CheckTemplateArgumentKind CTAK = CTAK_Specified); bool CheckTemplateArgument(TemplateTemplateParmDecl *Param, - const TemplateArgumentLoc &Arg); + const TemplateArgumentLoc &Arg, + unsigned ArgumentPackIndex); ExprResult BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index f2134134ae..e714da771b 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -945,6 +945,9 @@ namespace clang { /// \brief A NonTypeTemplateParmDecl record that stores an expanded /// non-type template parameter pack. DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK, + /// \brief A TemplateTemplateParmDecl record that stores an expanded + /// template template parameter pack. + DECL_EXPANDED_TEMPLATE_TEMPLATE_PARM_PACK, /// \brief A ClassScopeFunctionSpecializationDecl record a class scope /// function specialization. (Microsoft extension). DECL_CLASS_SCOPE_FUNCTION_SPECIALIZATION, diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index a7e89994af..fc31d2f55b 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -32,9 +32,25 @@ TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc, NamedDecl **Params, unsigned NumParams, SourceLocation RAngleLoc) : TemplateLoc(TemplateLoc), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), - NumParams(NumParams) { - for (unsigned Idx = 0; Idx < NumParams; ++Idx) - begin()[Idx] = Params[Idx]; + NumParams(NumParams), ContainsUnexpandedParameterPack(false) { + assert(this->NumParams == NumParams && "Too many template parameters"); + for (unsigned Idx = 0; Idx < NumParams; ++Idx) { + NamedDecl *P = Params[Idx]; + begin()[Idx] = P; + + if (!P->isTemplateParameterPack()) { + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(P)) + if (NTTP->getType()->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + + if (TemplateTemplateParmDecl *TTP = dyn_cast(P)) + if (TTP->getTemplateParameters()->containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + + // FIXME: If a default argument contains an unexpanded parameter pack, the + // template parameter list does too. + } + } } TemplateParameterList * @@ -577,6 +593,19 @@ SourceLocation NonTypeTemplateParmDecl::getDefaultArgumentLoc() const { void TemplateTemplateParmDecl::anchor() { } +TemplateTemplateParmDecl::TemplateTemplateParmDecl( + DeclContext *DC, SourceLocation L, unsigned D, unsigned P, + IdentifierInfo *Id, TemplateParameterList *Params, + unsigned NumExpansions, TemplateParameterList * const *Expansions) + : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params), + TemplateParmPosition(D, P), DefaultArgument(), + DefaultArgumentWasInherited(false), ParameterPack(true), + ExpandedParameterPack(true), NumExpandedParams(NumExpansions) { + if (Expansions) + std::memcpy(reinterpret_cast(this + 1), Expansions, + sizeof(TemplateParameterList*) * NumExpandedParams); +} + TemplateTemplateParmDecl * TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D, unsigned P, @@ -586,6 +615,19 @@ TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, Params); } +TemplateTemplateParmDecl * +TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, + SourceLocation L, unsigned D, unsigned P, + IdentifierInfo *Id, + TemplateParameterList *Params, + llvm::ArrayRef Expansions) { + void *Mem = C.Allocate(sizeof(TemplateTemplateParmDecl) + + sizeof(TemplateParameterList*) * Expansions.size()); + return new (Mem) TemplateTemplateParmDecl(DC, L, D, P, Id, Params, + Expansions.size(), + Expansions.data()); +} + TemplateTemplateParmDecl * TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void *Mem = AllocateDeserializedDecl(C, ID, sizeof(TemplateTemplateParmDecl)); @@ -593,6 +635,16 @@ TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID) { 0, 0); } +TemplateTemplateParmDecl * +TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, + unsigned NumExpansions) { + unsigned Size = sizeof(TemplateTemplateParmDecl) + + sizeof(TemplateParameterList*) * NumExpansions; + void *Mem = AllocateDeserializedDecl(C, ID, Size); + return new (Mem) TemplateTemplateParmDecl(0, SourceLocation(), 0, 0, 0, 0, + NumExpansions, 0); +} + //===----------------------------------------------------------------------===// // TemplateArgumentList Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index d6ad8ebf53..1e81f0d299 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1205,11 +1205,17 @@ static bool DiagnoseDefaultTemplateArgument(Sema &S, /// of a template template parameter, recursively. static bool DiagnoseUnexpandedParameterPacks(Sema &S, TemplateTemplateParmDecl *TTP) { + // A template template parameter which is a parameter pack is also a pack + // expansion. + if (TTP->isParameterPack()) + return false; + TemplateParameterList *Params = TTP->getTemplateParameters(); for (unsigned I = 0, N = Params->size(); I != N; ++I) { NamedDecl *P = Params->getParam(I); if (NonTypeTemplateParmDecl *NTTP = dyn_cast(P)) { - if (S.DiagnoseUnexpandedParameterPack(NTTP->getLocation(), + if (!NTTP->isParameterPack() && + S.DiagnoseUnexpandedParameterPack(NTTP->getLocation(), NTTP->getTypeSourceInfo(), Sema::UPPC_NonTypeTemplateParameterType)) return true; @@ -1322,7 +1328,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, } else if (NonTypeTemplateParmDecl *NewNonTypeParm = dyn_cast(*NewParam)) { // Check for unexpanded parameter packs. - if (DiagnoseUnexpandedParameterPack(NewNonTypeParm->getLocation(), + if (!NewNonTypeParm->isParameterPack() && + DiagnoseUnexpandedParameterPack(NewNonTypeParm->getLocation(), NewNonTypeParm->getTypeSourceInfo(), UPPC_NonTypeTemplateParameterType)) { Invalid = true; @@ -1343,7 +1350,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, if (NewNonTypeParm->isParameterPack()) { assert(!NewNonTypeParm->hasDefaultArgument() && "Parameter packs can't have a default argument!"); - SawParameterPack = true; + if (!NewNonTypeParm->isPackExpansion()) + SawParameterPack = true; } else if (OldNonTypeParm && OldNonTypeParm->hasDefaultArgument() && NewNonTypeParm->hasDefaultArgument()) { OldDefaultLoc = OldNonTypeParm->getDefaultArgumentLoc(); @@ -1390,7 +1398,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, if (NewTemplateParm->isParameterPack()) { assert(!NewTemplateParm->hasDefaultArgument() && "Parameter packs can't have a default argument!"); - SawParameterPack = true; + if (!NewTemplateParm->isPackExpansion()) + SawParameterPack = true; } else if (OldTemplateParm && OldTemplateParm->hasDefaultArgument() && NewTemplateParm->hasDefaultArgument()) { OldDefaultLoc = OldTemplateParm->getDefaultArgument().getLocation(); @@ -1417,10 +1426,10 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams, MissingDefaultArg = true; } - // C++0x [temp.param]p11: + // C++11 [temp.param]p11: // If a template parameter of a primary class template or alias template // is a template parameter pack, it shall be the last template parameter. - if (SawParameterPack && (NewParam + 1) != NewParamEnd && + if (SawParameterPack && (NewParam + 1) != NewParamEnd && (TPC == TPC_ClassTemplate || TPC == TPC_TypeAliasTemplate)) { Diag((*NewParam)->getLocation(), diag::err_template_param_pack_must_be_last_template_parameter); @@ -2950,7 +2959,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: - if (CheckTemplateArgument(TempParm, Arg)) + if (CheckTemplateArgument(TempParm, Arg, ArgumentPackIndex)) return true; Converted.push_back(Arg.getArgument()); @@ -2999,6 +3008,33 @@ static bool diagnoseArityMismatch(Sema &S, TemplateDecl *Template, return true; } +/// \brief Check whether the template parameter is a pack expansion, and if so, +/// determine the number of parameters produced by that expansion. For instance: +/// +/// \code +/// template struct A { +/// template class ...TTs, typename ...Us> struct B; +/// }; +/// \endcode +/// +/// In \c A::B, \c NTs and \c TTs have expanded pack size 2, and \c Us +/// is not a pack expansion, so returns an empty Optional. +static llvm::Optional getExpandedPackSize(NamedDecl *Param) { + if (NonTypeTemplateParmDecl *NTTP + = dyn_cast(Param)) { + if (NTTP->isExpandedParameterPack()) + return NTTP->getNumExpansionTypes(); + } + + if (TemplateTemplateParmDecl *TTP + = dyn_cast(Param)) { + if (TTP->isExpandedParameterPack()) + return TTP->getNumExpansionTemplateParameters(); + } + + return llvm::Optional(); +} + /// \brief Check that the given template argument list is well-formed /// for specializing the given template. bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, @@ -3011,15 +3047,9 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, *ExpansionIntoFixedList = false; TemplateParameterList *Params = Template->getTemplateParameters(); - unsigned NumParams = Params->size(); - unsigned NumArgs = TemplateArgs.size(); - bool Invalid = false; SourceLocation RAngleLoc = TemplateArgs.getRAngleLoc(); - bool HasParameterPack = - NumParams > 0 && Params->getParam(NumParams - 1)->isTemplateParameterPack(); - // C++ [temp.arg]p1: // [...] The type and form of each template-argument specified in // a template-id shall match the type and form specified for the @@ -3027,38 +3057,53 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // template-parameter-list. bool isTemplateTemplateParameter = isa(Template); SmallVector ArgumentPack; - TemplateParameterList::iterator Param = Params->begin(), - ParamEnd = Params->end(); - unsigned ArgIdx = 0; + unsigned ArgIdx = 0, NumArgs = TemplateArgs.size(); LocalInstantiationScope InstScope(*this, true); - bool SawPackExpansion = false; - while (Param != ParamEnd) { - if (ArgIdx < NumArgs) { - // If we have an expanded parameter pack, make sure we don't have too - // many arguments. - // FIXME: This really should fall out from the normal arity checking. - 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; + for (TemplateParameterList::iterator Param = Params->begin(), + ParamEnd = Params->end(); + Param != ParamEnd; /* increment in loop */) { + // If we have an expanded parameter pack, make sure we don't have too + // many arguments. + if (llvm::Optional Expansions = getExpandedPackSize(*Param)) { + if (*Expansions == ArgumentPack.size()) { + // We're done with this parameter pack. Pack up its arguments and add + // them to the list. + if (ArgumentPack.empty()) + Converted.push_back(TemplateArgument(0, 0)); + else { + Converted.push_back( + TemplateArgument::CreatePackCopy(Context, + ArgumentPack.data(), + ArgumentPack.size())); + ArgumentPack.clear(); } + // This argument is assigned to the next parameter. + ++Param; + continue; + } else if (ArgIdx == NumArgs && !PartialTemplateArgs) { + // Not enough arguments for this parameter pack. + Diag(TemplateLoc, diag::err_template_arg_list_different_arity) + << false + << (isa(Template)? 0 : + isa(Template)? 1 : + isa(Template)? 2 : 3) + << Template; + Diag(Template->getLocation(), diag::note_template_decl_here) + << Params->getSourceRange(); + return true; } + } + if (ArgIdx < NumArgs) { // Check the template argument we were given. if (CheckTemplateArgument(*Param, TemplateArgs[ArgIdx], Template, TemplateLoc, RAngleLoc, ArgumentPack.size(), Converted)) return true; + // We're now done with this argument. + ++ArgIdx; + if ((*Param)->isTemplateParameterPack()) { // The template parameter was a template parameter pack, so take the // deduced argument and place it on the argument pack. Note that we @@ -3070,16 +3115,51 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // Move to the next template parameter. ++Param; } - - // If this template argument is a pack expansion, record that fact - // and break out; we can't actually check any more. - if (TemplateArgs[ArgIdx].getArgument().isPackExpansion()) { - SawPackExpansion = true; - ++ArgIdx; - break; + + // If we just saw a pack expansion, then directly convert the remaining + // arguments, because we don't know what parameters they'll match up + // with. + if (TemplateArgs[ArgIdx-1].getArgument().isPackExpansion()) { + bool InFinalParameterPack = Param != ParamEnd && + Param + 1 == ParamEnd && + (*Param)->isTemplateParameterPack() && + !getExpandedPackSize(*Param); + + if (!InFinalParameterPack && !ArgumentPack.empty()) { + // If we were part way through filling in an expanded parameter pack, + // fall back to just producing individual arguments. + Converted.insert(Converted.end(), + ArgumentPack.begin(), ArgumentPack.end()); + ArgumentPack.clear(); + } + + while (ArgIdx < NumArgs) { + if (InFinalParameterPack) + ArgumentPack.push_back(TemplateArgs[ArgIdx].getArgument()); + else + Converted.push_back(TemplateArgs[ArgIdx].getArgument()); + ++ArgIdx; + } + + // Push the argument pack onto the list of converted arguments. + if (InFinalParameterPack) { + if (ArgumentPack.empty()) + Converted.push_back(TemplateArgument(0, 0)); + else { + Converted.push_back( + TemplateArgument::CreatePackCopy(Context, + ArgumentPack.data(), + ArgumentPack.size())); + ArgumentPack.clear(); + } + } else if (ExpansionIntoFixedList) { + // We have expanded a pack into a fixed list. + *ExpansionIntoFixedList = true; + } + + return false; } - - ++ArgIdx; + continue; } @@ -3090,14 +3170,34 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, ArgumentPack.data(), ArgumentPack.size())); - return Invalid; + return false; } // If we have a template parameter pack with no more corresponding // arguments, just break out now and we'll fill in the argument pack below. - if ((*Param)->isTemplateParameterPack()) - break; - + if ((*Param)->isTemplateParameterPack()) { + assert(!getExpandedPackSize(*Param) && + "Should have dealt with this already"); + + // A non-expanded parameter pack before the end of the parameter list + // only occurs for an ill-formed template parameter list, unless we've + // got a partial argument list for a function template, so just bail out. + if (Param + 1 != ParamEnd) + return true; + + if (ArgumentPack.empty()) + Converted.push_back(TemplateArgument(0, 0)); + else { + Converted.push_back(TemplateArgument::CreatePackCopy(Context, + ArgumentPack.data(), + ArgumentPack.size())); + ArgumentPack.clear(); + } + + ++Param; + continue; + } + // Check whether we have a default argument. TemplateArgumentLoc Arg; @@ -3184,86 +3284,12 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, ++ArgIdx; } - // If we saw a pack expansion, then directly convert the remaining arguments, - // because we don't know what parameters they'll match up with. - if (SawPackExpansion) { - bool AddToArgumentPack - = Param != ParamEnd && (*Param)->isTemplateParameterPack(); - while (ArgIdx < NumArgs) { - if (AddToArgumentPack) - ArgumentPack.push_back(TemplateArgs[ArgIdx].getArgument()); - else - Converted.push_back(TemplateArgs[ArgIdx].getArgument()); - ++ArgIdx; - } - - // Push the argument pack onto the list of converted arguments. - if (AddToArgumentPack) { - if (ArgumentPack.empty()) - Converted.push_back(TemplateArgument(0, 0)); - else { - Converted.push_back( - TemplateArgument::CreatePackCopy(Context, - ArgumentPack.data(), - ArgumentPack.size())); - ArgumentPack.clear(); - } - } else if (ExpansionIntoFixedList) { - // We have expanded a pack into a fixed list. - *ExpansionIntoFixedList = true; - } - - return Invalid; - } - // If we have any leftover arguments, then there were too many arguments. // Complain and fail. if (ArgIdx < NumArgs) return diagnoseArityMismatch(*this, Template, TemplateLoc, TemplateArgs); - - // If we have an expanded parameter pack, make sure we don't have too - // many arguments. - // FIXME: This really should fall out from the normal arity checking. - if (Param != ParamEnd) { - if (NonTypeTemplateParmDecl *NTTP - = dyn_cast(*Param)) { - if (NTTP->isExpandedParameterPack() && - ArgumentPack.size() < NTTP->getNumExpansionTypes()) { - Diag(TemplateLoc, diag::err_template_arg_list_different_arity) - << false - << (isa(Template)? 0 : - isa(Template)? 1 : - isa(Template)? 2 : 3) - << Template; - Diag(Template->getLocation(), diag::note_template_decl_here) - << Params->getSourceRange(); - return true; - } - } - } - - // Form argument packs for each of the parameter packs remaining. - while (Param != ParamEnd) { - // If we're checking a partial list of template arguments, don't fill - // in arguments for non-template parameter packs. - if ((*Param)->isTemplateParameterPack()) { - if (!HasParameterPack) - return true; - if (ArgumentPack.empty()) - Converted.push_back(TemplateArgument(0, 0)); - else { - Converted.push_back(TemplateArgument::CreatePackCopy(Context, - ArgumentPack.data(), - ArgumentPack.size())); - ArgumentPack.clear(); - } - } else if (!PartialTemplateArgs) - return diagnoseArityMismatch(*this, Template, TemplateLoc, TemplateArgs); - ++Param; - } - - return Invalid; + return false; } namespace { @@ -4420,7 +4446,8 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, /// This routine implements the semantics of C++ [temp.arg.template]. /// It returns true if an error occurred, and false otherwise. bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, - const TemplateArgumentLoc &Arg) { + const TemplateArgumentLoc &Arg, + unsigned ArgumentPackIndex) { TemplateName Name = Arg.getArgument().getAsTemplate(); TemplateDecl *Template = Name.getAsTemplateDecl(); if (!Template) { @@ -4451,8 +4478,12 @@ bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param, << Template; } + TemplateParameterList *Params = Param->getTemplateParameters(); + if (Param->isExpandedParameterPack()) + Params = Param->getExpansionTemplateParameters(ArgumentPackIndex); + return !TemplateParameterListsAreEqual(Template->getTemplateParameters(), - Param->getTemplateParameters(), + Params, true, TPL_TemplateTemplateArgumentMatch, Arg.getLocation()); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index b8f0bca296..b236f2861e 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -573,7 +573,7 @@ static void PrepareArgumentPackDeduction(Sema &S, if (!S.CurrentInstantiationScope) continue; - // If the template arugment pack was explicitly specified, add that to + // If the template argument pack was explicitly specified, add that to // the set of deduced arguments. const TemplateArgument *ExplicitArgs; unsigned NumExplicitArgs; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 28e4116534..55462ff09b 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1657,7 +1657,7 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( IsExpandedParameterPack = true; DI = D->getTypeSourceInfo(); T = DI->getType(); - } else if (isa(TL)) { + } else if (D->isPackExpansion()) { // 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. @@ -1771,27 +1771,121 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( return Param; } +static void collectUnexpandedParameterPacks( + Sema &S, + TemplateParameterList *Params, + SmallVectorImpl &Unexpanded) { + for (TemplateParameterList::const_iterator I = Params->begin(), + E = Params->end(); I != E; ++I) { + if ((*I)->isTemplateParameterPack()) + continue; + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(*I)) + S.collectUnexpandedParameterPacks(NTTP->getTypeSourceInfo()->getTypeLoc(), + Unexpanded); + if (TemplateTemplateParmDecl *TTP = dyn_cast(*I)) + collectUnexpandedParameterPacks(S, TTP->getTemplateParameters(), + Unexpanded); + } +} + Decl * TemplateDeclInstantiator::VisitTemplateTemplateParmDecl( TemplateTemplateParmDecl *D) { // Instantiate the template parameter list of the template template parameter. TemplateParameterList *TempParams = D->getTemplateParameters(); TemplateParameterList *InstParams; - { + SmallVector ExpandedParams; + + bool IsExpandedParameterPack = false; + + if (D->isExpandedParameterPack()) { + // The template template parameter pack is an already-expanded pack + // expansion of template parameters. Substitute into each of the expanded + // parameters. + ExpandedParams.reserve(D->getNumExpansionTemplateParameters()); + for (unsigned I = 0, N = D->getNumExpansionTemplateParameters(); + I != N; ++I) { + LocalInstantiationScope Scope(SemaRef); + TemplateParameterList *Expansion = + SubstTemplateParams(D->getExpansionTemplateParameters(I)); + if (!Expansion) + return 0; + ExpandedParams.push_back(Expansion); + } + + IsExpandedParameterPack = true; + InstParams = TempParams; + } else if (D->isPackExpansion()) { + // The template template parameter pack expands to a pack of template + // template parameters. Determine whether we need to expand this parameter + // pack into separate parameters. + SmallVector Unexpanded; + collectUnexpandedParameterPacks(SemaRef, D->getTemplateParameters(), + Unexpanded); + + // Determine whether the set of unexpanded parameter packs can and should + // be expanded. + bool Expand = true; + bool RetainExpansion = false; + llvm::Optional NumExpansions; + if (SemaRef.CheckParameterPacksForExpansion(D->getLocation(), + TempParams->getSourceRange(), + Unexpanded, + TemplateArgs, + Expand, RetainExpansion, + NumExpansions)) + return 0; + + if (Expand) { + for (unsigned I = 0; I != *NumExpansions; ++I) { + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I); + LocalInstantiationScope Scope(SemaRef); + TemplateParameterList *Expansion = SubstTemplateParams(TempParams); + if (!Expansion) + return 0; + ExpandedParams.push_back(Expansion); + } + + // 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; + InstParams = TempParams; + } else { + // We cannot fully expand the pack expansion now, so just substitute + // into the pattern. + Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, -1); + + LocalInstantiationScope Scope(SemaRef); + InstParams = SubstTemplateParams(TempParams); + if (!InstParams) + return 0; + } + } else { // Perform the actual substitution of template parameters within a new, // local instantiation scope. LocalInstantiationScope Scope(SemaRef); InstParams = SubstTemplateParams(TempParams); if (!InstParams) - return NULL; + return 0; } // Build the template template parameter. - TemplateTemplateParmDecl *Param - = TemplateTemplateParmDecl::Create(SemaRef.Context, Owner, D->getLocation(), + TemplateTemplateParmDecl *Param; + if (IsExpandedParameterPack) + Param = TemplateTemplateParmDecl::Create(SemaRef.Context, Owner, + D->getLocation(), + D->getDepth() - TemplateArgs.getNumLevels(), + D->getPosition(), + D->getIdentifier(), InstParams, + ExpandedParams); + else + Param = TemplateTemplateParmDecl::Create(SemaRef.Context, Owner, + D->getLocation(), D->getDepth() - TemplateArgs.getNumLevels(), - D->getPosition(), D->isParameterPack(), - D->getIdentifier(), InstParams); + D->getPosition(), + D->isParameterPack(), + D->getIdentifier(), InstParams); Param->setDefaultArgument(D->getDefaultArgument(), false); Param->setAccess(AS_public); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 2418f3a167..ba830d172c 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1486,11 +1486,18 @@ void ASTDeclReader::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { // TemplateParmPosition. D->setDepth(Record[Idx++]); D->setPosition(Record[Idx++]); - // Rest of TemplateTemplateParmDecl. - TemplateArgumentLoc Arg = Reader.ReadTemplateArgumentLoc(F, Record, Idx); - bool IsInherited = Record[Idx++]; - D->setDefaultArgument(Arg, IsInherited); - D->ParameterPack = Record[Idx++]; + if (D->isExpandedParameterPack()) { + void **Data = reinterpret_cast(D + 1); + for (unsigned I = 0, N = D->getNumExpansionTemplateParameters(); + I != N; ++I) + Data[I] = Reader.ReadTemplateParameterList(F, Record, Idx); + } else { + // Rest of TemplateTemplateParmDecl. + TemplateArgumentLoc Arg = Reader.ReadTemplateArgumentLoc(F, Record, Idx); + bool IsInherited = Record[Idx++]; + D->setDefaultArgument(Arg, IsInherited); + D->ParameterPack = Record[Idx++]; + } } void ASTDeclReader::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { @@ -2004,6 +2011,10 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_TEMPLATE_TEMPLATE_PARM: D = TemplateTemplateParmDecl::CreateDeserialized(Context, ID); break; + case DECL_EXPANDED_TEMPLATE_TEMPLATE_PARM_PACK: + D = TemplateTemplateParmDecl::CreateDeserialized(Context, ID, + Record[Idx++]); + break; case DECL_TYPE_ALIAS_TEMPLATE: D = TypeAliasTemplateDecl::CreateDeserialized(Context, ID); break; diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 602943b9ba..baa8a0e4cd 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -1172,7 +1172,8 @@ 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 + // so that it's easier for deserialization to allocate the right amount of + // memory. if (D->isExpandedParameterPack()) Record.push_back(D->getNumExpansionTypes()); @@ -1201,15 +1202,30 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { } void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + // For an expanded parameter pack, record the number of expansion types here + // so that it's easier for deserialization to allocate the right amount of + // memory. + if (D->isExpandedParameterPack()) + Record.push_back(D->getNumExpansionTemplateParameters()); + VisitTemplateDecl(D); // TemplateParmPosition. Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); - // Rest of TemplateTemplateParmDecl. - Writer.AddTemplateArgumentLoc(D->getDefaultArgument(), Record); - Record.push_back(D->defaultArgumentWasInherited()); - Record.push_back(D->isParameterPack()); - Code = serialization::DECL_TEMPLATE_TEMPLATE_PARM; + + if (D->isExpandedParameterPack()) { + for (unsigned I = 0, N = D->getNumExpansionTemplateParameters(); + I != N; ++I) + Writer.AddTemplateParameterList(D->getExpansionTemplateParameters(I), + Record); + Code = serialization::DECL_EXPANDED_TEMPLATE_TEMPLATE_PARM_PACK; + } else { + // Rest of TemplateTemplateParmDecl. + Writer.AddTemplateArgumentLoc(D->getDefaultArgument(), Record); + Record.push_back(D->defaultArgumentWasInherited()); + Record.push_back(D->isParameterPack()); + Code = serialization::DECL_TEMPLATE_TEMPLATE_PARM; + } } void ASTDeclWriter::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { diff --git a/test/CXX/temp/temp.param/p15-cxx0x.cpp b/test/CXX/temp/temp.param/p15-cxx0x.cpp index 5fc57a43f5..4cc1f17a11 100644 --- a/test/CXX/temp/temp.param/p15-cxx0x.cpp +++ b/test/CXX/temp/temp.param/p15-cxx0x.cpp @@ -22,3 +22,153 @@ void f(const X x) { template struct X1 { }; X1> x1a; + + +namespace ParameterPackExpansions { + +// A template parameter pack that [contains an unexpanded parameter pack] is a +// pack expansion. + +template struct Outer { + // From [temp.variadic]p4: + // In a template parameter pack that is a pack expansion, the pattern is + // [...the template-parameter...] without the ellipsis. + // Therefore the resulting sequence of parameters is not a parameter pack, + // so is not required to be the last template parameter. + template class ...Bs, typename ...Cs> struct Inner { + struct Check : Bs... { + Check(Cs...); + }; + }; +}; + +template struct TemplateInt {}; +template struct TemplateChar {}; +template struct TemplateIntPtr {}; +int x; + +Outer:: +Inner<12345, 'x', &x, + TemplateInt, TemplateChar, TemplateIntPtr, + int*>:: +Check check(&x); + + +template struct types; + +enum place { _ }; +template struct places {}; + +template struct append_places; +template +struct append_places, places> { + typedef places type; +}; + +template +struct make_places : append_places::type, + typename make_places::type> {}; +template<> struct make_places<0> { typedef places<> type; }; +template<> struct make_places<1> { typedef places<_> type; }; + +template struct wrap { + template struct inner { typedef T type; }; +}; + +template struct takedrop_impl; +template struct takedrop_impl> { + template class ...Take, + template class ...Drop> + struct inner { // expected-note 2{{declared}} + typedef types::type...> take; + typedef types::type...> drop; + }; +}; + +template struct take { + using type = typename takedrop_impl::type>:: + template inner::template inner...>::take; // expected-error {{too few template arguments}} +}; +template struct drop { + using type = typename takedrop_impl::type>:: + template inner::template inner...>::drop; // expected-error {{too few template arguments}} +}; + +using T1 = take<3, int, char, double, long>::type; // expected-note {{previous}} +using T1 = types; // expected-error {{'types' vs 'types'}} +using D1 = drop<3, int, char, double, long>::type; +using D1 = types; + +using T2 = take<4, int, char, double, long>::type; // expected-note {{previous}} +using T2 = types; +using T2 = types; // expected-error {{'types' vs 'types'}} +using D2 = drop<4, int, char, double, long>::type; +using D2 = types<>; + +using T3 = take<5, int, char, double, long>::type; // expected-note {{in instantiation of}} +using D3 = drop<5, int, char, double, long>::type; // expected-note {{in instantiation of}} + + +// FIXME: We should accept this code. A parameter pack within a default argument +// in a template template parameter pack is expanded, because the pack is +// implicitly a pack expansion. +template struct DefArg { + template class ...Classes> struct Inner { // expected-error {{default argument contains unexpanded parameter pack}} expected-note {{here}} + Inner(Classes<>...); // expected-error {{too few}} + }; +}; +template struct vector {}; +template struct list {}; +vector vi; +list lc; +DefArg::Inner defarg(vi, lc); + + +// FIXME: +// A template parameter pack that is a pack expansion shall not expand a +// parameter pack declared in the same template-parameter-list. +template void error(); // desired-error + +// This case should not produce an error, because in A's instantiation, Cs is +// not a parameter pack. +template void consume(Ts...); +template struct A { + template class ...Cs, Cs ...Vs> struct B { // ok + B() { + consume([]{ + int arr[Vs]; // expected-error {{negative size}} + }...); + } + }; +}; +template using Int = int; +template using Char = char; +A::B b; // expected-note {{here}} + +} + +namespace PR9023 { + template struct A { + template class ...> struct B { + }; + }; + + template struct C { }; + template struct D { }; + + int main() { + A::B e; + } +} + +namespace std_examples { + template class Tuple; + template struct multi_array; + template struct value_holder { + template struct apply { }; + }; + template struct static_array; // expected-error {{must be the last}} + + int n; + value_holder::apply<12345, 'x', &n> test; +} diff --git a/test/PCH/cxx-variadic-templates.cpp b/test/PCH/cxx-variadic-templates.cpp index 5b586931d5..c78a1a5704 100644 --- a/test/PCH/cxx-variadic-templates.cpp +++ b/test/PCH/cxx-variadic-templates.cpp @@ -9,3 +9,7 @@ // CHECK: allocate_shared shared_ptr spi = shared_ptr::allocate_shared(1, 2); + +template struct A {}; +template struct B {}; +outer::inner<1, 2, A, B> i(A<1>{}, B<2>{}); diff --git a/test/PCH/cxx-variadic-templates.h b/test/PCH/cxx-variadic-templates.h index f6ee7876c2..50596cdf5d 100644 --- a/test/PCH/cxx-variadic-templates.h +++ b/test/PCH/cxx-variadic-templates.h @@ -16,3 +16,10 @@ shared_ptr<_Tp>::allocate_shared(const _Alloc& __a, _Args&& ...__args) shared_ptr<_Tp> __r; return __r; } + +template struct outer { + template class ...Cs> struct inner { + inner(Cs...); + }; +}; +template struct outer; -- 2.40.0