From d3731198193eee92796ddeb493973b7a598b003e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 10 Jan 2011 07:32:04 +0000 Subject: [PATCH] Work-in-progress implementation of C++0x [temp.arg.explicit]p9, which allows an argument pack determines via explicit specification of function template arguments to be extended by further, deduced arguments. For example: template void f(Types ... values); void g() { f(0, 0, 0); // Types is deduced to the sequence int*, float*, int } There are a number of FIXMEs in here that indicate places where we need to implement + test retained expansions, plus a number of other places in deduction where we need to correctly cope with the explicitly-specified arguments when deducing an argument pack. Furthermore, it appears that the RecursiveASTVisitor needs to be auditied; it's missing some traversals (especially w.r.t. template arguments) that cause it not to find unexpanded parameter packs when it should. The good news, however, is that the tr1::tuple implementation now works fully, and the tr1::bind example (both from N2080) is actually working now. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123163 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 16 +- include/clang/AST/TemplateBase.h | 4 +- include/clang/Sema/Sema.h | 6 + include/clang/Sema/Template.h | 51 ++- lib/Sema/SemaTemplate.cpp | 11 +- lib/Sema/SemaTemplateDeduction.cpp | 120 ++++-- lib/Sema/SemaTemplateInstantiate.cpp | 100 ++++- lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 +- lib/Sema/SemaTemplateVariadic.cpp | 45 ++- lib/Sema/TreeTransform.h | 84 ++++- .../temp.decls/temp.variadic/example-bind.cpp | 352 ++++++++++++++++++ .../temp.variadic/example-tuple.cpp | 4 - .../temp.fct.spec/temp.arg.explicit/p9-0x.cpp | 8 +- 13 files changed, 738 insertions(+), 70 deletions(-) create mode 100644 test/CXX/temp/temp.decls/temp.variadic/example-bind.cpp diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index ad9f6ddef3..b1fbaa32bb 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1801,7 +1801,7 @@ DEF_TRAVERSE_STMT(CXXDefaultArgExpr, { }) DEF_TRAVERSE_STMT(CXXDeleteExpr, { }) DEF_TRAVERSE_STMT(ExprWithCleanups, { }) DEF_TRAVERSE_STMT(CXXNullPtrLiteralExpr, { }) -DEF_TRAVERSE_STMT(CXXPseudoDestructorExpr, { }) + DEF_TRAVERSE_STMT(CXXPseudoDestructorExpr, { }) // FIXME: Incomplete! DEF_TRAVERSE_STMT(CXXThisExpr, { }) DEF_TRAVERSE_STMT(CXXThrowExpr, { }) DEF_TRAVERSE_STMT(DesignatedInitExpr, { }) @@ -1820,8 +1820,18 @@ DEF_TRAVERSE_STMT(ParenListExpr, { }) DEF_TRAVERSE_STMT(PredefinedExpr, { }) DEF_TRAVERSE_STMT(ShuffleVectorExpr, { }) DEF_TRAVERSE_STMT(StmtExpr, { }) -DEF_TRAVERSE_STMT(UnresolvedLookupExpr, { }) -DEF_TRAVERSE_STMT(UnresolvedMemberExpr, { }) +DEF_TRAVERSE_STMT(UnresolvedLookupExpr, { + TRY_TO(TraverseNestedNameSpecifier(S->getQualifier())); + TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(), + S->getNumTemplateArgs())); +}) + +DEF_TRAVERSE_STMT(UnresolvedMemberExpr, { + TRY_TO(TraverseNestedNameSpecifier(S->getQualifier())); + TRY_TO(TraverseTemplateArgumentLocsHelper(S->getTemplateArgs(), + S->getNumTemplateArgs())); +}) + DEF_TRAVERSE_STMT(CXXOperatorCallExpr, { }) DEF_TRAVERSE_STMT(OpaqueValueExpr, { }) diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index 22ca164d38..5c7dae0a7c 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -77,7 +77,7 @@ private: void *Type; } Integer; struct { - TemplateArgument *Args; + const TemplateArgument *Args; unsigned NumArgs; } Args; }; @@ -136,7 +136,7 @@ public: /// /// We assume that storage for the template arguments provided /// outlives the TemplateArgument itself. - TemplateArgument(TemplateArgument *Args, unsigned NumArgs) : Kind(Pack) { + TemplateArgument(const TemplateArgument *Args, unsigned NumArgs) : Kind(Pack){ this->Args.Args = Args; this->Args.NumArgs = NumArgs; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 0f15ab85d3..368fe9dffa 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3361,6 +3361,11 @@ public: /// expand the corresponding pack expansions into separate arguments. When /// set, \c NumExpansions must also be set. /// + /// \param RetainExpansion Whether the caller should add an unexpanded + /// pack expansion after all of the expanded arguments. This is used + /// when extending explicitly-specified template argument packs per + /// C++0x [temp.arg.explicit]p9. + /// /// \param NumExpansions The number of separate arguments that will be in /// the expanded form of the corresponding pack expansion. Must be set when /// \c ShouldExpand is \c true. @@ -3375,6 +3380,7 @@ public: unsigned NumUnexpanded, const MultiLevelTemplateArgumentList &TemplateArgs, bool &ShouldExpand, + bool &RetainExpansion, unsigned &NumExpansions); /// \brief Determine whether the given declarator contains any unexpanded diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index 929da6c564..abdec2b8d5 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -81,6 +81,16 @@ namespace clang { return !(*this)(Depth, Index).isNull(); } + /// \brief Clear out a specific template argument. + void setArgument(unsigned Depth, unsigned Index, + TemplateArgument Arg) { + assert(Depth < TemplateArgumentLists.size()); + assert(Index < TemplateArgumentLists[getNumLevels() - Depth - 1].second); + const_cast( + TemplateArgumentLists[getNumLevels() - Depth - 1].first[Index]) + = Arg; + } + /// \brief Add a new outermost level to the multi-level template argument /// list. void addOuterTemplateArguments(const TemplateArgumentList *TemplateArgs) { @@ -141,7 +151,7 @@ namespace clang { : TemplateArgument(Arg), DeducedFromArrayBound(DeducedFromArrayBound) { } /// \brief Construct an integral non-type template argument that - /// has been deduced, possible from an array bound. + /// has been deduced, possibly from an array bound. DeducedTemplateArgument(const llvm::APSInt &Value, QualType ValueType, bool DeducedFromArrayBound) @@ -214,6 +224,19 @@ namespace clang { /// lookup will search our outer scope. bool CombineWithOuterScope; + /// \brief If non-NULL, the template parameter pack that has been + /// partially substituted per C++0x [temp.arg.explicit]p9. + NamedDecl *PartiallySubstitutedPack; + + /// \brief If \c PartiallySubstitutedPack is non-null, the set of + /// explicitly-specified template arguments in that pack. + const TemplateArgument *ArgsInPartiallySubstitutedPack; + + /// \brief If \c PartiallySubstitutedPack, the number of + /// explicitly-specified template arguments in + /// ArgsInPartiallySubstitutedPack. + unsigned NumArgsInPartiallySubstitutedPack; + // This class is non-copyable LocalInstantiationScope(const LocalInstantiationScope &); LocalInstantiationScope &operator=(const LocalInstantiationScope &); @@ -221,7 +244,8 @@ namespace clang { public: LocalInstantiationScope(Sema &SemaRef, bool CombineWithOuterScope = false) : SemaRef(SemaRef), Outer(SemaRef.CurrentInstantiationScope), - Exited(false), CombineWithOuterScope(CombineWithOuterScope) + Exited(false), CombineWithOuterScope(CombineWithOuterScope), + PartiallySubstitutedPack(0) { SemaRef.CurrentInstantiationScope = this; } @@ -271,6 +295,29 @@ namespace clang { void InstantiatedLocal(const Decl *D, Decl *Inst); void InstantiatedLocalPackArg(const Decl *D, Decl *Inst); void MakeInstantiatedLocalArgPack(const Decl *D); + + /// \brief Note that the given parameter pack has been partially substituted + /// via explicit specification of template arguments + /// (C++0x [temp.arg.explicit]p9). + /// + /// \param Pack The parameter pack, which will always be a template + /// parameter pack. + /// + /// \param ExplicitArgs The explicitly-specified template arguments provided + /// for this parameter pack. + /// + /// \param NumExplicitArgs The number of explicitly-specified template + /// arguments provided for this parameter pack. + void SetPartiallySubstitutedPack(NamedDecl *Pack, + const TemplateArgument *ExplicitArgs, + unsigned NumExplicitArgs); + + /// \brief Retrieve the partially-substitued template parameter pack. + /// + /// If there is no partially-substituted parameter pack, returns NULL. + NamedDecl *getPartiallySubstitutedPack( + const TemplateArgument **ExplicitArgs = 0, + unsigned *NumExplicitArgs = 0) const; }; class TemplateDeclInstantiator diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 2c9a4307ed..f540c7b935 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2550,10 +2550,13 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // 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()) { - // The parameter pack takes the contents of the current argument pack, - // which we built up earlier. - if (ArgumentPack.empty()) { + if (PartialTemplateArgs && ArgumentPack.empty()) { + Converted.push_back(TemplateArgument()); + } else if (ArgumentPack.empty()) { Converted.push_back(TemplateArgument(0, 0)); } else { TemplateArgument *PackedArgs @@ -3600,7 +3603,7 @@ ExprResult Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, SourceLocation Loc) { assert(Arg.getKind() == TemplateArgument::Integral && - "Operation is only value for integral template arguments"); + "Operation is only valid for integral template arguments"); QualType T = Arg.getIntegralType(); if (T->isCharType() || T->isWideCharType()) return Owned(new (Context) CharacterLiteral( diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 8832447314..ff084c34c2 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -478,14 +478,9 @@ static bool IsPossiblyOpaquelyQualifiedType(QualType T) { } } -/// \brief Retrieve the depth and index of an unexpanded parameter pack. +/// \brief Retrieve the depth and index of a template parameter. static std::pair -getDepthAndIndex(UnexpandedParameterPack UPP) { - if (const TemplateTypeParmType *TTP - = UPP.first.dyn_cast()) - return std::make_pair(TTP->getDepth(), TTP->getIndex()); - - NamedDecl *ND = UPP.first.get(); +getDepthAndIndex(NamedDecl *ND) { if (TemplateTypeParmDecl *TTP = dyn_cast(ND)) return std::make_pair(TTP->getDepth(), TTP->getIndex()); @@ -496,6 +491,16 @@ getDepthAndIndex(UnexpandedParameterPack UPP) { return std::make_pair(TTP->getDepth(), TTP->getIndex()); } +/// \brief Retrieve the depth and index of an unexpanded parameter pack. +static std::pair +getDepthAndIndex(UnexpandedParameterPack UPP) { + if (const TemplateTypeParmType *TTP + = UPP.first.dyn_cast()) + return std::make_pair(TTP->getDepth(), TTP->getIndex()); + + return getDepthAndIndex(UPP.first.get()); +} + /// \brief Helper function to build a TemplateParameter when we don't /// know its type statically. static TemplateParameter makeTemplateParameter(Decl *D) { @@ -602,6 +607,12 @@ DeduceTemplateArguments(Sema &S, } assert(!PackIndices.empty() && "Pack expansion without unexpanded packs?"); + // Keep track of the deduced template arguments for each parameter pack + // expanded by this pack expansion (the outer index) and for each + // template argument (the inner SmallVectors). + llvm::SmallVector, 2> + NewlyDeducedPacks(PackIndices.size()); + // Save the deduced template arguments for each parameter pack expanded // by this pack expansion, then clear out the deduction. llvm::SmallVector @@ -609,13 +620,21 @@ DeduceTemplateArguments(Sema &S, for (unsigned I = 0, N = PackIndices.size(); I != N; ++I) { SavedPacks[I] = Deduced[PackIndices[I]]; Deduced[PackIndices[I]] = DeducedTemplateArgument(); + + // If the template arugment pack was explicitly specified, add that to + // the set of deduced arguments. + const TemplateArgument *ExplicitArgs; + unsigned NumExplicitArgs; + if (NamedDecl *PartiallySubstitutedPack + = S.CurrentInstantiationScope->getPartiallySubstitutedPack( + &ExplicitArgs, + &NumExplicitArgs)) { + if (getDepthAndIndex(PartiallySubstitutedPack).second == PackIndices[I]) + NewlyDeducedPacks[I].append(ExplicitArgs, + ExplicitArgs + NumExplicitArgs); + } } - // Keep track of the deduced template arguments for each parameter pack - // expanded by this pack expansion (the outer index) and for each - // template argument (the inner SmallVectors). - llvm::SmallVector, 2> - NewlyDeducedPacks(PackIndices.size()); bool HasAnyArguments = false; for (; ArgIdx < NumArgs; ++ArgIdx) { HasAnyArguments = true; @@ -1879,13 +1898,30 @@ Sema::SubstituteExplicitTemplateArguments( TemplateArgumentList *ExplicitArgumentList = TemplateArgumentList::CreateCopy(Context, Builder.data(), Builder.size()); Info.reset(ExplicitArgumentList); - + // Template argument deduction and the final substitution should be // done in the context of the templated declaration. Explicit // argument substitution, on the other hand, needs to happen in the // calling context. ContextRAII SavedContext(*this, FunctionTemplate->getTemplatedDecl()); + // If we deduced template arguments for a template parameter pack, + // note that the template argument pack is partially substituted and record + // the explicit template arguments. They'll be used as part of deduction + // for this template parameter pack. + bool HasPartiallySubstitutedPack = false; + for (unsigned I = 0, N = Builder.size(); I != N; ++I) { + const TemplateArgument &Arg = Builder[I]; + if (Arg.getKind() == TemplateArgument::Pack) { + HasPartiallySubstitutedPack = true; + CurrentInstantiationScope->SetPartiallySubstitutedPack( + TemplateParams->getParam(I), + Arg.pack_begin(), + Arg.pack_size()); + break; + } + } + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments. if (SubstParmTypes(Function->getLocation(), @@ -1927,11 +1963,18 @@ Sema::SubstituteExplicitTemplateArguments( // template arguments can be deduced, they may all be omitted; in this // case, the empty template argument list <> itself may also be omitted. // - // Take all of the explicitly-specified arguments and put them into the - // set of deduced template arguments. + // Take all of the explicitly-specified arguments and put them into + // the set of deduced template arguments. Explicitly-specified + // parameter packs, however, will be set to NULL since the deduction + // mechanisms handle explicitly-specified argument packs directly. Deduced.reserve(TemplateParams->size()); - for (unsigned I = 0, N = ExplicitArgumentList->size(); I != N; ++I) - Deduced.push_back(ExplicitArgumentList->get(I)); + for (unsigned I = 0, N = ExplicitArgumentList->size(); I != N; ++I) { + const TemplateArgument &Arg = ExplicitArgumentList->get(I); + if (Arg.getKind() == TemplateArgument::Pack) + Deduced.push_back(DeducedTemplateArgument()); + else + Deduced.push_back(Arg); + } return TDK_Success; } @@ -2025,7 +2068,18 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, // be deduced to an empty sequence of template arguments. // FIXME: Where did the word "trailing" come from? if (Param->isTemplateParameterPack()) { - Builder.push_back(TemplateArgument(0, 0)); + // We may have had explicitly-specified template arguments for this + // template parameter pack. If so, our empty deduction extends the + // explicitly-specified set (C++0x [temp.arg.explicit]p9). + const TemplateArgument *ExplicitArgs; + unsigned NumExplicitArgs; + if (CurrentInstantiationScope->getPartiallySubstitutedPack(&ExplicitArgs, + &NumExplicitArgs) + == Param) + Builder.push_back(TemplateArgument(ExplicitArgs, NumExplicitArgs)); + else + Builder.push_back(TemplateArgument(0, 0)); + continue; } @@ -2446,20 +2500,36 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, } assert(!PackIndices.empty() && "Pack expansion without unexpanded packs?"); + // Keep track of the deduced template arguments for each parameter pack + // expanded by this pack expansion (the outer index) and for each + // template argument (the inner SmallVectors). + llvm::SmallVector, 2> + NewlyDeducedPacks(PackIndices.size()); + // Save the deduced template arguments for each parameter pack expanded // by this pack expansion, then clear out the deduction. llvm::SmallVector - SavedPacks(PackIndices.size()); + SavedPacks(PackIndices.size()); for (unsigned I = 0, N = PackIndices.size(); I != N; ++I) { + // Save the previously-deduced argument pack, then clear it out so that we + // can deduce a new argument pack. SavedPacks[I] = Deduced[PackIndices[I]]; - Deduced[PackIndices[I]] = DeducedTemplateArgument(); + Deduced[PackIndices[I]] = TemplateArgument(); + + // If the template arugment pack was explicitly specified, add that to + // the set of deduced arguments. + const TemplateArgument *ExplicitArgs; + unsigned NumExplicitArgs; + if (NamedDecl *PartiallySubstitutedPack + = CurrentInstantiationScope->getPartiallySubstitutedPack( + &ExplicitArgs, + &NumExplicitArgs)) { + if (getDepthAndIndex(PartiallySubstitutedPack).second == PackIndices[I]) + NewlyDeducedPacks[I].append(ExplicitArgs, + ExplicitArgs + NumExplicitArgs); + } } - // Keep track of the deduced template arguments for each parameter pack - // expanded by this pack expansion (the outer index) and for each - // template argument (the inner SmallVectors). - llvm::SmallVector, 2> - NewlyDeducedPacks(PackIndices.size()); bool HasAnyArguments = false; for (; ArgIdx < NumArgs; ++ArgIdx) { HasAnyArguments = true; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 394f50ef69..5f181fb5e1 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -564,6 +564,19 @@ TemplateDeductionInfo *Sema::isSFINAEContext() const { return 0; } +/// \brief Retrieve the depth and index of a parameter pack. +static std::pair +getDepthAndIndex(NamedDecl *ND) { + if (TemplateTypeParmDecl *TTP = dyn_cast(ND)) + return std::make_pair(TTP->getDepth(), TTP->getIndex()); + + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(ND)) + return std::make_pair(NTTP->getDepth(), NTTP->getIndex()); + + TemplateTemplateParmDecl *TTP = cast(ND); + return std::make_pair(TTP->getDepth(), TTP->getIndex()); +} + //===----------------------------------------------------------------------===/ // Template Instantiation for Types //===----------------------------------------------------------------------===/ @@ -608,12 +621,14 @@ namespace { const UnexpandedParameterPack *Unexpanded, unsigned NumUnexpanded, bool &ShouldExpand, + bool &RetainExpansion, unsigned &NumExpansions) { return getSema().CheckParameterPacksForExpansion(EllipsisLoc, PatternRange, Unexpanded, NumUnexpanded, TemplateArgs, ShouldExpand, + RetainExpansion, NumExpansions); } @@ -621,6 +636,37 @@ namespace { SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack); } + TemplateArgument ForgetPartiallySubstitutedPack() { + TemplateArgument Result; + if (NamedDecl *PartialPack + = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){ + MultiLevelTemplateArgumentList &TemplateArgs + = const_cast(this->TemplateArgs); + unsigned Depth, Index; + llvm::tie(Depth, Index) = getDepthAndIndex(PartialPack); + if (TemplateArgs.hasTemplateArgument(Depth, Index)) { + Result = TemplateArgs(Depth, Index); + TemplateArgs.setArgument(Depth, Index, TemplateArgument()); + } + } + + return Result; + } + + void RememberPartiallySubstitutedPack(TemplateArgument Arg) { + if (Arg.isNull()) + return; + + if (NamedDecl *PartialPack + = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){ + MultiLevelTemplateArgumentList &TemplateArgs + = const_cast(this->TemplateArgs); + unsigned Depth, Index; + llvm::tie(Depth, Index) = getDepthAndIndex(PartialPack); + TemplateArgs.setArgument(Depth, Index, Arg); + } + } + /// \brief Transform the given declaration by instantiating a reference to /// this declaration. Decl *TransformDecl(SourceLocation Loc, Decl *D); @@ -713,6 +759,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { return 0; } + assert(getSema().ArgumentPackSubstitutionIndex < (int)Arg.pack_size()); Arg = Arg.pack_begin()[getSema().ArgumentPackSubstitutionIndex]; } @@ -761,6 +808,7 @@ TemplateInstantiator::TransformFirstQualifierInScope(NamedDecl *D, return 0; } + assert(getSema().ArgumentPackSubstitutionIndex < (int)Arg.pack_size()); Arg = Arg.pack_begin()[getSema().ArgumentPackSubstitutionIndex]; } @@ -877,6 +925,7 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, return ExprError(); } + assert(getSema().ArgumentPackSubstitutionIndex < (int)Arg.pack_size()); Arg = Arg.pack_begin()[getSema().ArgumentPackSubstitutionIndex]; } @@ -981,6 +1030,7 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB, return QualType(); } + assert(getSema().ArgumentPackSubstitutionIndex < (int)Arg.pack_size()); Arg = Arg.pack_begin()[getSema().ArgumentPackSubstitutionIndex]; } @@ -1272,11 +1322,13 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation, collectUnexpandedParameterPacks(Base->getTypeSourceInfo()->getTypeLoc(), Unexpanded); bool ShouldExpand = false; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (CheckParameterPacksForExpansion(Base->getEllipsisLoc(), Base->getSourceRange(), Unexpanded.data(), Unexpanded.size(), TemplateArgs, ShouldExpand, + RetainExpansion, NumExpansions)) { Invalid = true; continue; @@ -1959,9 +2011,13 @@ LocalInstantiationScope::findInstantiationOf(const Decl *D) { void LocalInstantiationScope::InstantiatedLocal(const Decl *D, Decl *Inst) { llvm::PointerUnion &Stored = LocalDecls[D]; - assert((Stored.isNull() || - (Stored.get() == Inst)) && "Already instantiated this local"); - Stored = Inst; + if (Stored.isNull()) + Stored = Inst; + else if (Stored.is()) { + assert(Stored.get() == Inst && "Already instantiated this local"); + Stored = Inst; + } else + LocalDecls[D].get()->push_back(Inst); } void LocalInstantiationScope::InstantiatedLocalPackArg(const Decl *D, @@ -1978,3 +2034,41 @@ void LocalInstantiationScope::MakeInstantiatedLocalArgPack(const Decl *D) { ArgumentPacks.push_back(Pack); } +void LocalInstantiationScope::SetPartiallySubstitutedPack(NamedDecl *Pack, + const TemplateArgument *ExplicitArgs, + unsigned NumExplicitArgs) { + assert((!PartiallySubstitutedPack || PartiallySubstitutedPack == Pack) && + "Already have a partially-substituted pack"); + assert((!PartiallySubstitutedPack + || NumArgsInPartiallySubstitutedPack == NumExplicitArgs) && + "Wrong number of arguments in partially-substituted pack"); + PartiallySubstitutedPack = Pack; + ArgsInPartiallySubstitutedPack = ExplicitArgs; + NumArgsInPartiallySubstitutedPack = NumExplicitArgs; +} + +NamedDecl *LocalInstantiationScope::getPartiallySubstitutedPack( + const TemplateArgument **ExplicitArgs, + unsigned *NumExplicitArgs) const { + if (ExplicitArgs) + *ExplicitArgs = 0; + if (NumExplicitArgs) + *NumExplicitArgs = 0; + + for (const LocalInstantiationScope *Current = this; Current; + Current = Current->Outer) { + if (Current->PartiallySubstitutedPack) { + if (ExplicitArgs) + *ExplicitArgs = Current->ArgsInPartiallySubstitutedPack; + if (NumExplicitArgs) + *NumExplicitArgs = Current->NumArgsInPartiallySubstitutedPack; + + return Current->PartiallySubstitutedPack; + } + + if (!Current->CombineWithOuterScope) + break; + } + + return 0; +} diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e59f94a2ca..c29ff865b2 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1973,13 +1973,16 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, "Pack expansion without parameter packs?"); bool Expand = false; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (SemaRef.CheckParameterPacksForExpansion(New->getLocation(), SourceRange(), Unexpanded.data(), Unexpanded.size(), TemplateArgs, - Expand, NumExpansions)) + Expand, + RetainExpansion, + NumExpansions)) break; if (!Expand) { @@ -2377,12 +2380,14 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New, llvm::SmallVector Unexpanded; collectUnexpandedParameterPacks(BaseTL, Unexpanded); bool ShouldExpand = false; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (CheckParameterPacksForExpansion(Init->getEllipsisLoc(), BaseTL.getSourceRange(), Unexpanded.data(), Unexpanded.size(), TemplateArgs, ShouldExpand, + RetainExpansion, NumExpansions)) { AnyErrors = true; New->setInvalidDecl(); diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 1928c59642..09e610c675 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -419,14 +419,29 @@ ExprResult Sema::ActOnPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc) { EllipsisLoc)); } +/// \brief Retrieve the depth and index of a parameter pack. +static std::pair +getDepthAndIndex(NamedDecl *ND) { + if (TemplateTypeParmDecl *TTP = dyn_cast(ND)) + return std::make_pair(TTP->getDepth(), TTP->getIndex()); + + if (NonTypeTemplateParmDecl *NTTP = dyn_cast(ND)) + return std::make_pair(NTTP->getDepth(), NTTP->getIndex()); + + TemplateTemplateParmDecl *TTP = cast(ND); + return std::make_pair(TTP->getDepth(), TTP->getIndex()); +} + bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, SourceRange PatternRange, const UnexpandedParameterPack *Unexpanded, unsigned NumUnexpanded, const MultiLevelTemplateArgumentList &TemplateArgs, bool &ShouldExpand, + bool &RetainExpansion, unsigned &NumExpansions) { ShouldExpand = true; + RetainExpansion = false; std::pair FirstPack; bool HaveFirstPack = false; @@ -444,21 +459,11 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, Name = TTP->getName(); } else { NamedDecl *ND = Unexpanded[I].first.get(); - if (TemplateTypeParmDecl *TTP = dyn_cast(ND)) { - Depth = TTP->getDepth(); - Index = TTP->getIndex(); - } else if (NonTypeTemplateParmDecl *NTTP - = dyn_cast(ND)) { - Depth = NTTP->getDepth(); - Index = NTTP->getIndex(); - } else if (TemplateTemplateParmDecl *TTP - = dyn_cast(ND)) { - Depth = TTP->getDepth(); - Index = TTP->getIndex(); - } else { - assert(cast(ND)->isParameterPack()); + if (isa(ND)) IsFunctionParameterPack = true; - } + else + llvm::tie(Depth, Index) = getDepthAndIndex(ND); + Name = ND->getIdentifier(); } @@ -495,6 +500,18 @@ bool Sema::CheckParameterPacksForExpansion(SourceLocation EllipsisLoc, NewPackSize = TemplateArgs(Depth, Index).pack_size(); } + // C++0x [temp.arg.explicit]p9: + // Template argument deduction can extend the sequence of template + // arguments corresponding to a template parameter pack, even when the + // sequence contains explicitly specified template arguments. + if (NamedDecl *PartialPack + = CurrentInstantiationScope->getPartiallySubstitutedPack()) { + unsigned PartialDepth, PartialIndex; + llvm::tie(PartialDepth, PartialIndex) = getDepthAndIndex(PartialPack); + if (PartialDepth == Depth && PartialIndex == Index) + RetainExpansion = true; + } + 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 e2bfb05088..7eee444174 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -89,6 +89,23 @@ using namespace sema; /// (\c getBaseLocation(), \c getBaseEntity()). template class TreeTransform { + /// \brief Private RAII object that helps us forget and then re-remember + /// the template argument corresponding to a partially-substituted parameter + /// pack. + class ForgetPartiallySubstitutedPackRAII { + Derived &Self; + TemplateArgument Old; + + public: + ForgetPartiallySubstitutedPackRAII(Derived &Self) : Self(Self) { + Old = Self.ForgetPartiallySubstitutedPack(); + } + + ~ForgetPartiallySubstitutedPackRAII() { + Self.RememberPartiallySubstitutedPack(Old); + } + }; + protected: Sema &SemaRef; @@ -204,6 +221,11 @@ public: /// expand the corresponding pack expansions into separate arguments. When /// set, \c NumExpansions must also be set. /// + /// \param RetainExpansion Whether the caller should add an unexpanded + /// pack expansion after all of the expanded arguments. This is used + /// when extending explicitly-specified template argument packs per + /// C++0x [temp.arg.explicit]p9. + /// /// \param NumExpansions The number of separate arguments that will be in /// the expanded form of the corresponding pack expansion. Must be set when /// \c ShouldExpand is \c true. @@ -217,11 +239,28 @@ public: const UnexpandedParameterPack *Unexpanded, unsigned NumUnexpanded, bool &ShouldExpand, + bool &RetainExpansion, unsigned &NumExpansions) { ShouldExpand = false; return false; } + /// \brief "Forget" about the partially-substituted pack template argument, + /// when performing an instantiation that must preserve the parameter pack + /// use. + /// + /// This routine is meant to be overridden by the template instantiator. + TemplateArgument ForgetPartiallySubstitutedPack() { + return TemplateArgument(); + } + + /// \brief "Remember" the partially-substituted pack template argument + /// after performing an instantiation that must preserve the parameter pack + /// use. + /// + /// This routine is meant to be overridden by the template instantiator. + void RememberPartiallySubstitutedPack(TemplateArgument Arg) { } + /// \brief Note to the derived class when a function parameter pack is /// being expanded. void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { } @@ -2250,12 +2289,14 @@ bool TreeTransform::TransformExprs(Expr **Inputs, // Determine whether the set of unexpanded parameter packs can and should // be expanded. bool Expand = true; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (getDerived().TryExpandParameterPacks(Expansion->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded.data(), Unexpanded.size(), - Expand, NumExpansions)) + Expand, RetainExpansion, + NumExpansions)) return true; if (!Expand) { @@ -2770,12 +2811,15 @@ bool TreeTransform::TransformTemplateArguments(InputIterator First, // Determine whether the set of unexpanded parameter packs can and should // be expanded. bool Expand = true; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (getDerived().TryExpandParameterPacks(Ellipsis, Pattern.getSourceRange(), Unexpanded.data(), Unexpanded.size(), - Expand, NumExpansions)) + Expand, + RetainExpansion, + NumExpansions)) return true; if (!Expand) { @@ -2806,6 +2850,8 @@ bool TreeTransform::TransformTemplateArguments(InputIterator First, Outputs.addArgument(Out); } + // FIXME: Variadic templates retain expansion! + continue; } @@ -3415,12 +3461,15 @@ bool TreeTransform:: // Determine whether we should expand the parameter packs. bool ShouldExpand = false; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(), Unexpanded.data(), Unexpanded.size(), - ShouldExpand, NumExpansions)) { + ShouldExpand, + RetainExpansion, + NumExpansions)) { return true; } @@ -3439,7 +3488,21 @@ bool TreeTransform:: if (PVars) PVars->push_back(NewParm); } - + + // If we're supposed to retain a pack expansion, do so by temporarily + // forgetting the partially-substituted parameter pack. + if (RetainExpansion) { + ForgetPartiallySubstitutedPackRAII Forget(getDerived()); + ParmVarDecl *NewParm + = getDerived().TransformFunctionTypeParam(OldParm); + if (!NewParm) + return true; + + OutParamTypes.push_back(NewParm->getType()); + if (PVars) + PVars->push_back(NewParm); + } + // We're done with the pack expansion. continue; } @@ -3472,11 +3535,14 @@ bool TreeTransform:: // Determine whether we should expand the parameter packs. bool ShouldExpand = false; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (getDerived().TryExpandParameterPacks(Loc, SourceRange(), Unexpanded.data(), Unexpanded.size(), - ShouldExpand, NumExpansions)) { + ShouldExpand, + RetainExpansion, + NumExpansions)) { return true; } @@ -3498,6 +3564,8 @@ bool TreeTransform:: continue; } + // FIXME: Variadic templates retain pack expansion! + // We'll substitute the parameter now without expanding the pack // expansion. OldType = Expansion->getPattern(); @@ -6674,13 +6742,15 @@ TreeTransform::TransformSizeOfPackExpr(SizeOfPackExpr *E) { // so UnexpandedParameterPack Unexpanded(E->getPack(), E->getPackLoc()); bool ShouldExpand = false; + bool RetainExpansion = false; unsigned NumExpansions = 0; if (getDerived().TryExpandParameterPacks(E->getOperatorLoc(), E->getPackLoc(), &Unexpanded, 1, - ShouldExpand, NumExpansions)) + ShouldExpand, RetainExpansion, + NumExpansions)) return ExprError(); - if (!ShouldExpand) + if (!ShouldExpand || RetainExpansion) return SemaRef.Owned(E); // We now know the length of the parameter pack, so build a new expression diff --git a/test/CXX/temp/temp.decls/temp.variadic/example-bind.cpp b/test/CXX/temp/temp.decls/temp.variadic/example-bind.cpp new file mode 100644 index 0000000000..83db1719b7 --- /dev/null +++ b/test/CXX/temp/temp.decls/temp.variadic/example-bind.cpp @@ -0,0 +1,352 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +// Example bind implementation from the variadic templates proposal, +// ISO C++ committee document number N2080. + +// Helper type traits +template +struct add_reference { + typedef T &type; +}; + +template +struct add_reference { + typedef T &type; +}; + +template +struct add_const_reference { + typedef T const &type; +}; + +template +struct add_const_reference { + typedef T &type; +}; + +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; + +template +class reference_wrapper { + T *ptr; + +public: + reference_wrapper(T& t) : ptr(&t) { } + operator T&() const { return *ptr; } +}; + +template reference_wrapper ref(T& t) { + return reference_wrapper(t); +} +template reference_wrapper cref(const T& t) { + return reference_wrapper(t); +} + +template class tuple; + +// Basis case: zero-length tuple +template<> class tuple<> { }; + +template +class tuple : private tuple { + typedef tuple inherited; + +public: + tuple() { } + // implicit copy-constructor is okay + + // Construct tuple from separate arguments. + tuple(typename add_const_reference::type v, + typename add_const_reference::type... vtail) + : m_head(v), inherited(vtail...) { } + + // Construct tuple from another tuple. + template tuple(const tuple& other) + : m_head(other.head()), inherited(other.tail()) { } + + template tuple& + operator=(const tuple& other) { + m_head = other.head(); + tail() = other.tail(); + return *this; + } + + typename add_reference::type head() { return m_head; } + typename add_reference::type head() const { return m_head; } + inherited& tail() { return *this; } + const inherited& tail() const { return *this; } + +protected: + Head m_head; +}; + +// Creation functions +template +struct make_tuple_result { + typedef T type; +}; + +template +struct make_tuple_result > { + typedef T& type; +}; + +template +tuple::type...> +make_tuple(const Values&... values) { + return tuple::type...>(values...); +} + +template +tuple tie(Values&... values) { + return tuple(values...); +} + +// Helper classes +template struct tuple_size; + +template struct tuple_size > { + static const int value = sizeof...(Values); +}; + +template struct tuple_element; + +template +struct tuple_element > { + typedef typename tuple_element >::type type; +}; + +template +struct tuple_element<0, tuple > { + typedef Head type; +}; + +// Element access +template class get_impl; +template +class get_impl > { + typedef typename tuple_element >::type Element; + typedef typename add_reference::type RJ; + typedef typename add_const_reference::type PJ; + typedef get_impl > Next; +public: + static RJ get(tuple& t) { return Next::get(t.tail()); } + static PJ get(const tuple& t) { return Next::get(t.tail()); } +}; + +template +class get_impl<0, tuple > { + typedef typename add_reference::type RJ; + typedef typename add_const_reference::type PJ; +public: + static RJ get(tuple& t) { return t.head(); } + static PJ get(const tuple& t) { return t.head(); } +}; + +template typename add_reference< +typename tuple_element >::type >::type +get(tuple& t) { + return get_impl >::get(t); +} + +template typename add_const_reference< +typename tuple_element >::type >::type +get(const tuple& t) { + return get_impl >::get(t); +} + +// Relational operators +inline bool operator==(const tuple<>&, const tuple<>&) { return true; } + +template +bool operator==(const tuple& t, const tuple& u) { + return t.head() == u.head() && t.tail() == u.tail(); +} + +template +bool operator!=(const tuple& t, const tuple& u) { + return !(t == u); +} + +inline bool operator<(const tuple<>&, const tuple<>&) { return false; } + +template +bool operator<(const tuple& t, const tuple& u) { + return (t.head() < u.head() || (!(t.head() < u.head()) && t.tail() < u.tail())); +} + +template +bool operator>(const tuple& t, const tuple& u) { + return u < t; +} + +template +bool operator<=(const tuple& t, const tuple& u) { + return !(u < t); +} + +template +bool operator>=(const tuple& t, const tuple& u) { + return !(t < u); +} + +// make_indices helper +template struct int_tuple {}; +// make_indexes impl is a helper for make_indexes +template struct make_indexes_impl; + +template +struct make_indexes_impl, T, Types...> { + typedef typename make_indexes_impl, Types...>::type type; +}; + +template +struct make_indexes_impl > { + typedef int_tuple type; +}; + +template +struct make_indexes : make_indexes_impl<0, int_tuple<>, Types...> { +}; + +// Bind +template struct is_bind_expression { + static const bool value = false; +}; + +template struct is_placeholder { + static const int value = 0; +}; + + +template class bound_functor { + typedef typename make_indexes::type indexes; +public: + typedef typename F::result_type result_type; + explicit bound_functor(const F& f, const BoundArgs&... bound_args) + : f(f), bound_args(bound_args...) { } template + typename F::result_type operator()(Args&... args); +private: F f; + tuple bound_args; +}; + +template +inline bound_functor bind(const F& f, const BoundArgs&... bound_args) { + return bound_functor(f, bound_args...); +} + +template +struct is_bind_expression > { + static const bool value = true; +}; + +// enable_if helper +template +struct enable_if; + +template +struct enable_if { + typedef T type; +}; + +template +struct enable_if { }; + +// safe_tuple_element helper +template +struct safe_tuple_element { }; + +template +struct safe_tuple_element, + typename enable_if<(I >= 0 && I < tuple_size >::value)>::type> { + typedef typename tuple_element >::type type; +}; + +// mu +template +inline typename safe_tuple_element::value -1, + tuple >::type +mu(Bound& bound_arg, const tuple& args) { + return get::value-1>(args); +} + +template +inline T& mu(reference_wrapper& bound_arg, const tuple&) { + return bound_arg.get(); +} + +template +inline typename F::result_type +unwrap_and_forward(F& f, int_tuple, const tuple& args) { + return f(get(args)...); +} + +template +inline typename enable_if::value, + typename Bound::result_type>::type +mu(Bound& bound_arg, const tuple& args) { + typedef typename make_indexes::type Indexes; + return unwrap_and_forward(bound_arg, Indexes(), args); +} + +template +struct is_reference_wrapper { + static const bool value = false; +}; + +template +struct is_reference_wrapper> { + static const bool value = true; +}; + +template +inline typename enable_if<(!is_bind_expression::value + && !is_placeholder::value + && !is_reference_wrapper::value), + Bound&>::type +mu(Bound& bound_arg, const tuple&) { + return bound_arg; +} + +template +typename F::result_type apply_functor(F& f, tuple& bound_args, + int_tuple, + const tuple& args) { + return f(mu(get(bound_args), args)...); +} + +template +template +typename F::result_type bound_functor::operator()(Args&... args) { + return apply_functor(f, bound_args, indexes(), tie(args...)); +} + +template struct placeholder { }; +template +struct is_placeholder> { + static const int value = N; +}; + +template +struct plus { + typedef T result_type; + + T operator()(T x, T y) { return x + y; } +}; + +placeholder<1> _1; + +// Test bind +void test_bind() { + int x = 17; + int y = 25; + bind(plus(), x, _1)(y); +} diff --git a/test/CXX/temp/temp.decls/temp.variadic/example-tuple.cpp b/test/CXX/temp/temp.decls/temp.variadic/example-tuple.cpp index ff8dfda768..3b4bd7777d 100644 --- a/test/CXX/temp/temp.decls/temp.variadic/example-tuple.cpp +++ b/test/CXX/temp/temp.decls/temp.variadic/example-tuple.cpp @@ -206,9 +206,6 @@ get(const tuple& t) { return get_impl >::get(t); } -#if 0 -// FIXME: Not yet functional, because we aren't currently able to -// extend a partially-explicitly-specified parameter pack. void test_element_access(tuple t3) { int i; float f; @@ -217,7 +214,6 @@ void test_element_access(tuple t3) { get<1>(t3) = &f; get<2>(t3) = &d; } -#endif // Relational operators inline bool operator==(const tuple<>&, const tuple<>&) { return true; } diff --git a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p9-0x.cpp b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p9-0x.cpp index ddee152e69..66ffb82adc 100644 --- a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p9-0x.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p9-0x.cpp @@ -35,13 +35,11 @@ void test_explicit_spec_simple() { // Template argument deduction can extend the sequence of template // arguments corresponding to a template parameter pack, even when the // sequence contains explicitly specified template arguments. -// FIXME: Actually test what this paragraph specifies. -#if 0 -void test_explicit_spec_extension() { +void test_explicit_spec_extension(double *dp) { int *ip1 = first_arg(0, 0); int *ip2 = first_arg(0, 0, 0, 0); float *fp1 = first_arg(0, 0, 0); - int i1 = second_arg(0, 0, 0); + int *i1 = second_arg(0, (int*)0, 0); + double *dp1 = first_arg<>(dp); } -#endif -- 2.40.0