From: Douglas Gregor Date: Fri, 7 Jan 2011 16:43:16 +0000 (+0000) Subject: Implement substitution of a function parameter pack for its set of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=12c9c00024a01819e3a70ef6d951d32efaeb9312;p=clang Implement substitution of a function parameter pack for its set of instantiated function parameters, enabling instantiation of arbitrary pack expansions involving function parameter packs. At this point, we can now correctly compile a simple, variadic print() example: #include #include void print() {} template void print(const Head &head, const Tail &...tail) { std::cout << head; print(tail...); } int main() { std::string hello = "Hello"; print(hello, ", world!", " ", 2011, '\n'); } git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123000 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 101cf6acc2..096a8e2564 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3893,7 +3893,8 @@ public: bool SubstParmTypes(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, const MultiLevelTemplateArgumentList &TemplateArgs, - llvm::SmallVectorImpl &ParamTypes); + llvm::SmallVectorImpl &ParamTypes, + llvm::SmallVectorImpl *OutParams = 0); ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index 484d58dd0b..929da6c564 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -166,10 +166,19 @@ namespace clang { /// instantiate a new function declaration, which will have its own /// set of parameter declarations. class LocalInstantiationScope { + public: + /// \brief A set of declarations. + typedef llvm::SmallVector DeclArgumentPack; + + private: /// \brief Reference to the semantic analysis that is performing /// this template instantiation. Sema &SemaRef; + typedef llvm::DenseMap > + LocalDeclsMap; + /// \brief A mapping from local declarations that occur /// within a template to their instantiations. /// @@ -184,8 +193,15 @@ namespace clang { /// when we instantiate add, we will introduce a mapping from /// the ParmVarDecl for 'x' that occurs in the template to the /// instantiated ParmVarDecl for 'x'. - llvm::DenseMap LocalDecls; + /// + /// For a parameter pack, the local instantiation scope may contain a + /// set of instantiated parameters. This is stored as a DeclArgumentPack + /// pointer. + LocalDeclsMap LocalDecls; + /// \brief The set of argument packs we've allocated. + llvm::SmallVector ArgumentPacks; + /// \brief The outer scope, which contains local variable /// definitions from some other instantiation (that may not be /// relevant to this particular scope). @@ -219,12 +235,26 @@ namespace clang { if (Exited) return; + for (unsigned I = 0, N = ArgumentPacks.size(); I != N; ++I) + delete ArgumentPacks[I]; + SemaRef.CurrentInstantiationScope = Outer; Exited = true; } Decl *getInstantiationOf(const Decl *D); + /// \brief Find the instantiation of the declaration D within the current + /// instantiation scope. + /// + /// \param D The declaration whose instantiation we are searching for. + /// + /// \returns A pointer to the declaration or argument pack of declarations + /// to which the declaration \c D is instantiataed, if found. Otherwise, + /// returns NULL. + llvm::PointerUnion * + findInstantiationOf(const Decl *D); + VarDecl *getInstantiationOf(const VarDecl *Var) { return cast(getInstantiationOf(cast(Var))); } @@ -239,6 +269,8 @@ namespace clang { } void InstantiatedLocal(const Decl *D, Decl *Inst); + void InstantiatedLocalPackArg(const Decl *D, Decl *Inst); + void MakeInstantiatedLocalArgPack(const Decl *D); }; class TemplateDeclInstantiator diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 644ff35c3e..8832447314 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -542,7 +542,6 @@ DeduceTemplateArguments(Sema &S, llvm::SmallVectorImpl &Deduced, unsigned TDF) { // Fast-path check to see if we have too many/too few arguments. - // FIXME: Variadic templates broken! if (NumParams != NumArgs && !(NumParams && isa(Params[NumParams - 1])) && !(NumArgs && isa(Args[NumArgs - 1]))) diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index c4f91f9ff6..71ccb06212 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -617,6 +617,10 @@ namespace { NumExpansions); } + void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { + SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack); + } + /// \brief Transform the given declaration by instantiating a reference to /// this declaration. Decl *TransformDecl(SourceLocation Loc, Decl *D); @@ -1154,12 +1158,9 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo(); TypeSourceInfo *NewDI = 0; - bool WasParameterPack = false; - bool IsParameterPack = false; TypeLoc OldTL = OldDI->getTypeLoc(); if (isa(OldTL)) { PackExpansionTypeLoc ExpansionTL = cast(OldTL); - WasParameterPack = true; // We have a function parameter pack. Substitute into the pattern of the // expansion. @@ -1173,7 +1174,6 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, // our function parameter is still a function parameter pack. // Therefore, make its type a pack expansion type. NewDI = CheckPackExpansion(NewDI, ExpansionTL.getEllipsisLoc()); - IsParameterPack = true; } } else { NewDI = SubstType(OldDI, TemplateArgs, OldParm->getLocation(), @@ -1211,8 +1211,13 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, // FIXME: When OldParm is a parameter pack and NewParm is not a parameter // pack, we actually have a set of instantiated locations. Maintain this set! - if (!WasParameterPack || IsParameterPack) + if (OldParm->isParameterPack() && !NewParm->isParameterPack()) { + // Add the new parameter to + CurrentInstantiationScope->InstantiatedLocalPackArg(OldParm, NewParm); + } else { + // Introduce an Old -> New mapping CurrentInstantiationScope->InstantiatedLocal(OldParm, NewParm); + } // FIXME: OldParm may come from a FunctionProtoType, in which case CurContext // can be anything, is this right ? @@ -1227,7 +1232,8 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, bool Sema::SubstParmTypes(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, const MultiLevelTemplateArgumentList &TemplateArgs, - llvm::SmallVectorImpl &ParamTypes) { + llvm::SmallVectorImpl &ParamTypes, + llvm::SmallVectorImpl *OutParams) { assert(!ActiveTemplateInstantiations.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -1235,7 +1241,7 @@ bool Sema::SubstParmTypes(SourceLocation Loc, TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, DeclarationName()); return Instantiator.TransformFunctionTypeParams(Loc, Params, NumParams, 0, - ParamTypes, 0); + ParamTypes, OutParams); } /// \brief Perform substitution on the base class specifiers of the @@ -1899,15 +1905,27 @@ bool Sema::Subst(const TemplateArgumentLoc *Args, unsigned NumArgs, } Decl *LocalInstantiationScope::getInstantiationOf(const Decl *D) { + llvm::PointerUnion *Found= findInstantiationOf(D); + if (!Found) + return 0; + + if (Found->is()) + return Found->get(); + + return (*Found->get())[ + SemaRef.ArgumentPackSubstitutionIndex]; +} + +llvm::PointerUnion * +LocalInstantiationScope::findInstantiationOf(const Decl *D) { for (LocalInstantiationScope *Current = this; Current; Current = Current->Outer) { // Check if we found something within this scope. const Decl *CheckD = D; do { - llvm::DenseMap::iterator Found - = Current->LocalDecls.find(CheckD); + LocalDeclsMap::iterator Found = Current->LocalDecls.find(CheckD); if (Found != Current->LocalDecls.end()) - return Found->second; + return &Found->second; // If this is a tag declaration, it's possible that we need to look for // a previous declaration. @@ -1928,7 +1946,23 @@ Decl *LocalInstantiationScope::getInstantiationOf(const Decl *D) { } void LocalInstantiationScope::InstantiatedLocal(const Decl *D, Decl *Inst) { - Decl *&Stored = LocalDecls[D]; - assert((!Stored || Stored == Inst)&& "Already instantiated this local"); + llvm::PointerUnion &Stored = LocalDecls[D]; + assert((Stored.isNull() || + (Stored.get() == Inst)) && "Already instantiated this local"); Stored = Inst; } + +void LocalInstantiationScope::InstantiatedLocalPackArg(const Decl *D, + Decl *Inst) { + DeclArgumentPack *Pack = LocalDecls[D].get(); + Pack->push_back(Inst); +} + +void LocalInstantiationScope::MakeInstantiatedLocalArgPack(const Decl *D) { + llvm::PointerUnion &Stored = LocalDecls[D]; + assert(Stored.isNull() && "Already instantiated this local"); + DeclArgumentPack *Pack = new DeclArgumentPack; + Stored = Pack; + ArgumentPacks.push_back(Pack); +} + diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 92521ae16e..2ef688b0eb 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1271,14 +1271,11 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, // synthesized in the method declaration. if (!isa(T.IgnoreParens())) { assert(!Params.size() && "Instantiating type could not yield parameters"); - for (unsigned I = 0, N = D->getNumParams(); I != N; ++I) { - ParmVarDecl *P = SemaRef.SubstParmVarDecl(D->getParamDecl(I), - TemplateArgs); - if (!P) - return 0; - - Params.push_back(P); - } + llvm::SmallVector ParamTypes; + if (SemaRef.SubstParmTypes(D->getLocation(), D->param_begin(), + D->getNumParams(), TemplateArgs, ParamTypes, + &Params)) + return 0; } NestedNameSpecifier *Qualifier = D->getQualifier(); @@ -1904,12 +1901,31 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, TypeLoc NewTL = NewTInfo->getTypeLoc().IgnoreParens(); FunctionProtoTypeLoc *NewProtoLoc = cast(&NewTL); assert(NewProtoLoc && "Missing prototype?"); - for (unsigned i = 0, i_end = NewProtoLoc->getNumArgs(); i != i_end; ++i) { - // FIXME: Variadic templates will break this. - Params.push_back(NewProtoLoc->getArg(i)); - SemaRef.CurrentInstantiationScope->InstantiatedLocal( - OldProtoLoc->getArg(i), - NewProtoLoc->getArg(i)); + unsigned NewIdx = 0, NumNewParams = NewProtoLoc->getNumArgs(); + for (unsigned OldIdx = 0, NumOldParams = OldProtoLoc->getNumArgs(); + OldIdx != NumOldParams; ++OldIdx) { + ParmVarDecl *OldParam = OldProtoLoc->getArg(OldIdx); + if (!OldParam->isParameterPack() || + (NewIdx < NumNewParams && + NewProtoLoc->getArg(NewIdx)->isParameterPack())) { + // Simple case: normal parameter, or a parameter pack that's + // instantiated to a (still-dependent) parameter pack. + ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++); + Params.push_back(NewParam); + SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldParam, + NewParam); + continue; + } + + // Parameter pack: make the instantiation an argument pack. + SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack( + OldParam); + while (NewIdx < NumNewParams) { + ParmVarDecl *NewParam = NewProtoLoc->getArg(NewIdx++); + Params.push_back(NewParam); + SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(OldParam, + NewParam); + } } } } else { @@ -2179,11 +2195,28 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, // Introduce the instantiated function parameters into the local // instantiation scope, and set the parameter names to those used // in the template. + unsigned FParamIdx = 0; for (unsigned I = 0, N = PatternDecl->getNumParams(); I != N; ++I) { const ParmVarDecl *PatternParam = PatternDecl->getParamDecl(I); - ParmVarDecl *FunctionParam = Function->getParamDecl(I); - FunctionParam->setDeclName(PatternParam->getDeclName()); - Scope.InstantiatedLocal(PatternParam, FunctionParam); + if (!PatternParam->isParameterPack()) { + // Simple case: not a parameter pack. + assert(FParamIdx < Function->getNumParams()); + ParmVarDecl *FunctionParam = Function->getParamDecl(I); + FunctionParam->setDeclName(PatternParam->getDeclName()); + Scope.InstantiatedLocal(PatternParam, FunctionParam); + ++FParamIdx; + continue; + } + + // Expand the parameter pack. + Scope.MakeInstantiatedLocalArgPack(PatternParam); + for (unsigned NumFParams = Function->getNumParams(); + FParamIdx < NumFParams; + ++FParamIdx) { + ParmVarDecl *FunctionParam = Function->getParamDecl(FParamIdx); + FunctionParam->setDeclName(PatternParam->getDeclName()); + Scope.InstantiatedLocalPackArg(PatternParam, FunctionParam); + } } // Enter the scope of this instantiation. We don't use diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 3090d1532d..1928c59642 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -67,13 +67,8 @@ namespace { /// \brief Record occurrences of (FIXME: function and) non-type template /// parameter packs in an expression. bool VisitDeclRefExpr(DeclRefExpr *E) { - if (NonTypeTemplateParmDecl *NTTP - = dyn_cast(E->getDecl())) { - if (NTTP->isParameterPack()) - Unexpanded.push_back(std::make_pair(NTTP, E->getLocation())); - } - - // FIXME: Function parameter packs. + if (E->getDecl()->isParameterPack()) + Unexpanded.push_back(std::make_pair(E->getDecl(), E->getLocation())); return true; } @@ -440,6 +435,7 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, unsigned Depth; unsigned Index; IdentifierInfo *Name; + bool IsFunctionParameterPack = false; if (const TemplateTypeParmType *TTP = Unexpanded[I].first.dyn_cast()) { @@ -455,26 +451,50 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, = dyn_cast(ND)) { Depth = NTTP->getDepth(); Index = NTTP->getIndex(); - } else { - TemplateTemplateParmDecl *TTP = cast(ND); + } else if (TemplateTemplateParmDecl *TTP + = dyn_cast(ND)) { Depth = TTP->getDepth(); Index = TTP->getIndex(); + } else { + assert(cast(ND)->isParameterPack()); + IsFunctionParameterPack = true; } - // FIXME: Variadic templates function parameter packs? Name = ND->getIdentifier(); } - // If we don't have a template argument at this depth/index, then we - // cannot expand the pack expansion. Make a note of this, but we still - // want to check any parameter packs we *do* have arguments for. - if (Depth >= TemplateArgs.getNumLevels() || - !TemplateArgs.hasTemplateArgument(Depth, Index)) { - ShouldExpand = false; - continue; + // Determine the size of this argument pack. + unsigned NewPackSize; + if (IsFunctionParameterPack) { + // Figure out whether we're instantiating to an argument pack or not. + typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack; + + llvm::PointerUnion *Instantiation + = CurrentInstantiationScope->findInstantiationOf( + Unexpanded[I].first.get()); + if (Instantiation && + Instantiation->is()) { + // We could expand this function parameter pack. + NewPackSize = Instantiation->get()->size(); + } else { + // We can't expand this function parameter pack, so we can't expand + // the pack expansion. + ShouldExpand = false; + continue; + } + } else { + // If we don't have a template argument at this depth/index, then we + // cannot expand the pack expansion. Make a note of this, but we still + // want to check any parameter packs we *do* have arguments for. + if (Depth >= TemplateArgs.getNumLevels() || + !TemplateArgs.hasTemplateArgument(Depth, Index)) { + ShouldExpand = false; + continue; + } + + // Determine the size of the argument pack. + NewPackSize = TemplateArgs(Depth, Index).pack_size(); } - // Determine the size of the argument pack. - unsigned NewPackSize = TemplateArgs(Depth, Index).pack_size(); if (!HaveFirstPack) { // The is the first pack we've seen for which we have an argument. // Record it. diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 60ec950c12..0624aaa7ee 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -222,6 +222,10 @@ public: return false; } + /// \brief Note to the derived class when a function parameter pack is + /// being expanded. + void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { } + /// \brief Transforms the given type into another type. /// /// By default, this routine transforms a type by creating a @@ -3423,6 +3427,7 @@ bool TreeTransform:: if (ShouldExpand) { // Expand the function parameter pack into multiple, separate // parameters. + getDerived().ExpandingFunctionParameterPack(OldParm); for (unsigned I = 0; I != NumExpansions; ++I) { Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I); ParmVarDecl *NewParm @@ -6875,6 +6880,7 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { llvm::SmallVector ParamTypes; // Parameter substitution. + // FIXME: Variadic templates const BlockDecl *BD = E->getBlockDecl(); for (BlockDecl::param_const_iterator P = BD->param_begin(), EN = BD->param_end(); P != EN; ++P) { diff --git a/test/CXX/expr/expr.unary/expr.sizeof/p5-0x.cpp b/test/CXX/expr/expr.unary/expr.sizeof/p5-0x.cpp index 6ab93db77f..3824615415 100644 --- a/test/CXX/expr/expr.unary/expr.sizeof/p5-0x.cpp +++ b/test/CXX/expr/expr.unary/expr.sizeof/p5-0x.cpp @@ -13,6 +13,18 @@ template struct count_ints { int check_types[count_types::value == 3? 1 : -1]; int check_ints[count_ints<1, 2, 3, 4, 5>::value == 5? 1 : -1]; +// Test instantiation involving function parameter packs. +struct any { + template any(T); +}; + +template +void init_me(Inits ...inits) { + any array[sizeof...(inits)] = { inits... }; +} + +template void init_me(int, float, double*); + // Test parser and semantic recovery. template struct count_ints_2 { static const unsigned value = sizeof...(Value); // expected-error{{'Value' does not refer to the name of a parameter pack}} diff --git a/test/CXX/temp/temp.decls/temp.variadic/p2.cpp b/test/CXX/temp/temp.decls/temp.variadic/p2.cpp new file mode 100644 index 0000000000..100ae2c52f --- /dev/null +++ b/test/CXX/temp/temp.decls/temp.variadic/p2.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +template void f(Types ... args); + +void test() { + f(); + f(1); + f(2, 1.0); +} + +// Test simple recursive variadic function template +template +void recurse_until_fail(const Head &, const Tail &...tail) { // expected-note{{candidate function template not viable: requires at least 1 argument, but 0 were provided}} + recurse_until_fail(tail...); // expected-error{{no matching function for call to 'recurse_until_fail'}} \ + // expected-note{{in instantiation of function template specialization 'recurse_until_fail' requested here}} \ + // expected-note{{in instantiation of function template specialization 'recurse_until_fail' requested here}} +} + +void test_recurse_until_fail() { + recurse_until_fail(1, 3.14159, "string"); // expected-note{{in instantiation of function template specialization 'recurse_until_fail' requested here}} + +} diff --git a/test/CXX/temp/temp.decls/temp.variadic/p5.cpp b/test/CXX/temp/temp.decls/temp.variadic/p5.cpp index f0822c3452..a8d8928022 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/p5.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/p5.cpp @@ -237,3 +237,30 @@ struct MemberTemplatePPNames { }; }; +// Example from working paper +namespace WorkingPaperExample { + template struct Tuple {}; + template struct Pair {}; + + template struct zip { + template struct with { + typedef Tuple ... > type; // expected-error{{pack expansion contains parameter packs 'Args1' and 'Args2' that have different lengths (1 vs. 2)}} + }; + }; + + typedef zip::with::type T1; // T1 is Tuple, Pair> + typedef Tuple, Pair> T1; + + typedef zip::with::type T2; // expected-note{{in instantiation of template class}} + + template void f(Args...); + template void h(Args...); + + template + void g(Args ... args) { + f(const_cast(&args)...); // OK: "Args" and "args" are expanded within f + f(5 ...); // expected-error{{pack expansion does not contain any unexpanded parameter packs}} + f(args); // expected-error{{expression contains unexpanded parameter pack 'args'}} + f(h(args ...) + args ...); + } +}