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 <iostream>
#include <string>
void print() {}
template<typename Head, typename ...Tail>
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/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index 644ff35..8832447 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -542,7 +542,6 @@
llvm::SmallVectorImpl<DeducedTemplateArgument> &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<PackExpansionType>(Params[NumParams - 1])) &&
!(NumArgs && isa<PackExpansionType>(Args[NumArgs - 1])))
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index c4f91f9..71ccb06 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -617,6 +617,10 @@
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 @@
TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo();
TypeSourceInfo *NewDI = 0;
- bool WasParameterPack = false;
- bool IsParameterPack = false;
TypeLoc OldTL = OldDI->getTypeLoc();
if (isa<PackExpansionTypeLoc>(OldTL)) {
PackExpansionTypeLoc ExpansionTL = cast<PackExpansionTypeLoc>(OldTL);
- WasParameterPack = true;
// We have a function parameter pack. Substitute into the pattern of the
// expansion.
@@ -1173,7 +1174,6 @@
// 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 @@
// 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 @@
bool Sema::SubstParmTypes(SourceLocation Loc,
ParmVarDecl **Params, unsigned NumParams,
const MultiLevelTemplateArgumentList &TemplateArgs,
- llvm::SmallVectorImpl<QualType> &ParamTypes) {
+ llvm::SmallVectorImpl<QualType> &ParamTypes,
+ llvm::SmallVectorImpl<ParmVarDecl *> *OutParams) {
assert(!ActiveTemplateInstantiations.empty() &&
"Cannot perform an instantiation without some context on the "
"instantiation stack");
@@ -1235,7 +1241,7 @@
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 @@
}
Decl *LocalInstantiationScope::getInstantiationOf(const Decl *D) {
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found= findInstantiationOf(D);
+ if (!Found)
+ return 0;
+
+ if (Found->is<Decl *>())
+ return Found->get<Decl *>();
+
+ return (*Found->get<DeclArgumentPack *>())[
+ SemaRef.ArgumentPackSubstitutionIndex];
+}
+
+llvm::PointerUnion<Decl *, LocalInstantiationScope::DeclArgumentPack *> *
+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<const Decl *, Decl *>::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 @@
}
void LocalInstantiationScope::InstantiatedLocal(const Decl *D, Decl *Inst) {
- Decl *&Stored = LocalDecls[D];
- assert((!Stored || Stored == Inst)&& "Already instantiated this local");
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> &Stored = LocalDecls[D];
+ assert((Stored.isNull() ||
+ (Stored.get<Decl *>() == Inst)) && "Already instantiated this local");
Stored = Inst;
}
+
+void LocalInstantiationScope::InstantiatedLocalPackArg(const Decl *D,
+ Decl *Inst) {
+ DeclArgumentPack *Pack = LocalDecls[D].get<DeclArgumentPack *>();
+ Pack->push_back(Inst);
+}
+
+void LocalInstantiationScope::MakeInstantiatedLocalArgPack(const Decl *D) {
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> &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 92521ae..2ef688b 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1271,14 +1271,11 @@
// synthesized in the method declaration.
if (!isa<FunctionProtoType>(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<QualType, 4> ParamTypes;
+ if (SemaRef.SubstParmTypes(D->getLocation(), D->param_begin(),
+ D->getNumParams(), TemplateArgs, ParamTypes,
+ &Params))
+ return 0;
}
NestedNameSpecifier *Qualifier = D->getQualifier();
@@ -1904,12 +1901,31 @@
TypeLoc NewTL = NewTInfo->getTypeLoc().IgnoreParens();
FunctionProtoTypeLoc *NewProtoLoc = cast<FunctionProtoTypeLoc>(&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 @@
// 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 3090d15..1928c59 100644
--- a/lib/Sema/SemaTemplateVariadic.cpp
+++ b/lib/Sema/SemaTemplateVariadic.cpp
@@ -67,13 +67,8 @@
/// \brief Record occurrences of (FIXME: function and) non-type template
/// parameter packs in an expression.
bool VisitDeclRefExpr(DeclRefExpr *E) {
- if (NonTypeTemplateParmDecl *NTTP
- = dyn_cast<NonTypeTemplateParmDecl>(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 @@
unsigned Depth;
unsigned Index;
IdentifierInfo *Name;
+ bool IsFunctionParameterPack = false;
if (const TemplateTypeParmType *TTP
= Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
@@ -455,26 +451,50 @@
= dyn_cast<NonTypeTemplateParmDecl>(ND)) {
Depth = NTTP->getDepth();
Index = NTTP->getIndex();
- } else {
- TemplateTemplateParmDecl *TTP = cast<TemplateTemplateParmDecl>(ND);
+ } else if (TemplateTemplateParmDecl *TTP
+ = dyn_cast<TemplateTemplateParmDecl>(ND)) {
Depth = TTP->getDepth();
Index = TTP->getIndex();
+ } else {
+ assert(cast<ParmVarDecl>(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<Decl *, DeclArgumentPack *> *Instantiation
+ = CurrentInstantiationScope->findInstantiationOf(
+ Unexpanded[I].first.get<NamedDecl *>());
+ if (Instantiation &&
+ Instantiation->is<DeclArgumentPack *>()) {
+ // We could expand this function parameter pack.
+ NewPackSize = Instantiation->get<DeclArgumentPack *>()->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 60ec950..0624aaa 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -222,6 +222,10 @@
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 @@
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 @@
llvm::SmallVector<QualType, 4> 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) {