Implement support for non-type template parameter packs whose type is
a pack expansion, e.g., the parameter pack Values in:
template<typename ...Types>
struct Outer {
template<Types ...Values>
struct Inner;
};
This new implementation approach introduces the notion of an
"expanded" non-type template parameter pack, for which we have already
expanded the types of the parameter pack (to, say, "int*, float*",
for Outer<int*, float*>) but have not yet expanded the values. Aside
from creating these expanded non-type template parameter packs, this
patch updates template argument checking and non-type template
parameter pack instantiation to make use of the appropriate types in
the parameter pack.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123845 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index b2749bf..a164e5c 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -5967,8 +5967,6 @@
cast<NonTypeTemplateParmDecl>(Params->getParam(0));
// The template parameter must be a char parameter pack.
- // FIXME: This test will always fail because non-type parameter packs
- // have not been implemented.
if (PmDecl && PmDecl->isTemplateParameterPack() &&
Context.hasSameType(PmDecl->getType(), Context.CharTy))
Valid = true;
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index f97c650..ad08a11 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -2197,11 +2197,36 @@
/// \brief Check that the given template argument corresponds to the given
/// template parameter.
+///
+/// \param Param The template parameter against which the argument will be
+/// checked.
+///
+/// \param Arg The template argument.
+///
+/// \param Template The template in which the template argument resides.
+///
+/// \param TemplateLoc The location of the template name for the template
+/// whose argument list we're matching.
+///
+/// \param RAngleLoc The location of the right angle bracket ('>') that closes
+/// the template argument list.
+///
+/// \param ArgumentPackIndex The index into the argument pack where this
+/// argument will be placed. Only valid if the parameter is a parameter pack.
+///
+/// \param Converted The checked, converted argument will be added to the
+/// end of this small vector.
+///
+/// \param CTAK Describes how we arrived at this particular template argument:
+/// explicitly written, deduced, etc.
+///
+/// \returns true on error, false otherwise.
bool Sema::CheckTemplateArgument(NamedDecl *Param,
const TemplateArgumentLoc &Arg,
NamedDecl *Template,
SourceLocation TemplateLoc,
SourceLocation RAngleLoc,
+ unsigned ArgumentPackIndex,
llvm::SmallVectorImpl<TemplateArgument> &Converted,
CheckTemplateArgumentKind CTAK) {
// Check template type parameters.
@@ -2214,6 +2239,9 @@
// with the template arguments we've seen thus far. But if the
// template has a dependent context then we cannot substitute yet.
QualType NTTPType = NTTP->getType();
+ if (NTTP->isParameterPack() && NTTP->isExpandedParameterPack())
+ NTTPType = NTTP->getExpansionType(ArgumentPackIndex);
+
if (NTTPType->isDependentType() &&
!isa<TemplateTemplateParmDecl>(Template) &&
!Template->getDeclContext()->isDependentContext()) {
@@ -2447,9 +2475,28 @@
break;
if (ArgIdx < NumArgs) {
+ // If we have an expanded parameter pack, make sure we don't have too
+ // many arguments.
+ if (NonTypeTemplateParmDecl *NTTP
+ = dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
+ if (NTTP->isExpandedParameterPack() &&
+ ArgumentPack.size() >= NTTP->getNumExpansionTypes()) {
+ Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+ << true
+ << (isa<ClassTemplateDecl>(Template)? 0 :
+ isa<FunctionTemplateDecl>(Template)? 1 :
+ isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
+ << Template;
+ Diag(Template->getLocation(), diag::note_template_decl_here)
+ << Params->getSourceRange();
+ return true;
+ }
+ }
+
// Check the template argument we were given.
if (CheckTemplateArgument(*Param, TemplateArgs[ArgIdx], Template,
- TemplateLoc, RAngleLoc, Converted))
+ TemplateLoc, RAngleLoc,
+ ArgumentPack.size(), Converted))
return true;
if ((*Param)->isTemplateParameterPack()) {
@@ -2544,7 +2591,7 @@
// Check the default template argument.
if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc,
- RAngleLoc, Converted))
+ RAngleLoc, 0, Converted))
return true;
// Move to the next template parameter and argument.
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index 812c633..fc48039 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -1705,6 +1705,7 @@
DeducedTemplateArgument Arg,
NamedDecl *Template,
QualType NTTPType,
+ unsigned ArgumentPackIndex,
TemplateDeductionInfo &Info,
bool InFunctionTemplate,
llvm::SmallVectorImpl<TemplateArgument> &Output) {
@@ -1721,8 +1722,8 @@
DeducedTemplateArgument InnerArg(*PA);
InnerArg.setDeducedFromArrayBound(Arg.wasDeducedFromArrayBound());
if (ConvertDeducedTemplateArgument(S, Param, InnerArg, Template,
- NTTPType, Info,
- InFunctionTemplate, Output))
+ NTTPType, PackedArgsBuilder.size(),
+ Info, InFunctionTemplate, Output))
return true;
// Move the converted template argument into our argument pack.
@@ -1748,6 +1749,7 @@
Template,
Template->getLocation(),
Template->getSourceRange().getEnd(),
+ ArgumentPackIndex,
Output,
InFunctionTemplate
? (Arg.wasDeducedFromArrayBound()
@@ -1810,7 +1812,7 @@
}
if (ConvertDeducedTemplateArgument(S, Param, Deduced[I],
- Partial, NTTPType, Info, false,
+ Partial, NTTPType, 0, Info, false,
Builder)) {
Info.Param = makeTemplateParameter(Param);
// FIXME: These template arguments are temporary. Free them!
@@ -2160,7 +2162,7 @@
}
if (ConvertDeducedTemplateArgument(*this, Param, Deduced[I],
- FunctionTemplate, NTTPType, Info,
+ FunctionTemplate, NTTPType, 0, Info,
true, Builder)) {
Info.Param = makeTemplateParameter(Param);
// FIXME: These template arguments are temporary. Free them!
@@ -2212,7 +2214,7 @@
FunctionTemplate,
FunctionTemplate->getLocation(),
FunctionTemplate->getSourceRange().getEnd(),
- Builder,
+ 0, Builder,
CTAK_Deduced)) {
Info.Param = makeTemplateParameter(
const_cast<NamedDecl *>(TemplateParams->getParam(I)));
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 0ae8804..45fde29 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1016,9 +1016,19 @@
// Derive the type we want the substituted decl to have. This had
// better be non-dependent, or these checks will have serious problems.
- QualType TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs,
- E->getLocation(),
- DeclarationName());
+ QualType TargetType;
+ if (NTTP->isExpandedParameterPack())
+ TargetType = NTTP->getExpansionType(
+ getSema().ArgumentPackSubstitutionIndex);
+ else if (NTTP->isParameterPack() &&
+ isa<PackExpansionType>(NTTP->getType())) {
+ TargetType = SemaRef.SubstType(
+ cast<PackExpansionType>(NTTP->getType())->getPattern(),
+ TemplateArgs, E->getLocation(),
+ NTTP->getDeclName());
+ } else
+ TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs,
+ E->getLocation(), NTTP->getDeclName());
assert(!TargetType.isNull() && "type substitution failed for param type");
assert(!TargetType->isDependentType() && "param type still dependent");
return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg,
@@ -1038,7 +1048,6 @@
return getSema().Owned(E);
}
- // FIXME: Variadic templates select Nth from type?
const TemplateArgument &ArgPack = E->getArgumentPack();
unsigned Index = (unsigned)getSema().ArgumentPackSubstitutionIndex;
assert(Index < ArgPack.pack_size() && "Substitution index out-of-range");
@@ -1058,8 +1067,17 @@
if (!VD)
return ExprError();
- return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg,
- E->getType(),
+ QualType T;
+ NonTypeTemplateParmDecl *NTTP = E->getParameterPack();
+ if (NTTP->isExpandedParameterPack())
+ T = NTTP->getExpansionType(getSema().ArgumentPackSubstitutionIndex);
+ else if (const PackExpansionType *Expansion
+ = dyn_cast<PackExpansionType>(NTTP->getType()))
+ T = SemaRef.SubstType(Expansion->getPattern(), TemplateArgs,
+ E->getParameterPackLocation(), NTTP->getDeclName());
+ else
+ T = E->getType();
+ return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg, T,
E->getParameterPackLocation());
}
@@ -1778,6 +1796,11 @@
}
}
+ // If we're dealing with a member template where the template parameters
+ // have been instantiated, this provides the original template parameters
+ // from which the member template's parameters were instantiated.
+ llvm::SmallVector<const NamedDecl *, 4> InstantiatedTemplateParameters;
+
if (Matched.size() >= 1) {
llvm::SmallVector<MatchResult, 4>::iterator Best = Matched.begin();
if (Matched.size() == 1) {
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b4ccdd5..fd7325c 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1455,28 +1455,140 @@
Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl(
NonTypeTemplateParmDecl *D) {
// Substitute into the type of the non-type template parameter.
+ TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc();
+ llvm::SmallVector<TypeSourceInfo *, 4> ExpandedParameterPackTypesAsWritten;
+ llvm::SmallVector<QualType, 4> ExpandedParameterPackTypes;
+ bool IsExpandedParameterPack = false;
+ TypeSourceInfo *DI;
QualType T;
- TypeSourceInfo *DI = SemaRef.SubstType(D->getTypeSourceInfo(), TemplateArgs, D->getLocation(),
- D->getDeclName());
- if (!DI)
- return 0;
-
- T = DI->getType();
-
- // Check that this type is acceptable for a non-type template parameter.
bool Invalid = false;
- T = SemaRef.CheckNonTypeTemplateParameterType(T, D->getLocation());
- if (T.isNull()) {
- T = SemaRef.Context.IntTy;
- Invalid = true;
+
+ if (D->isExpandedParameterPack()) {
+ // The non-type template parameter pack is an already-expanded pack
+ // expansion of types. Substitute into each of the expanded types.
+ ExpandedParameterPackTypes.reserve(D->getNumExpansionTypes());
+ ExpandedParameterPackTypesAsWritten.reserve(D->getNumExpansionTypes());
+ for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) {
+ TypeSourceInfo *NewDI =SemaRef.SubstType(D->getExpansionTypeSourceInfo(I),
+ TemplateArgs,
+ D->getLocation(),
+ D->getDeclName());
+ if (!NewDI)
+ return 0;
+
+ ExpandedParameterPackTypesAsWritten.push_back(NewDI);
+ QualType NewT =SemaRef.CheckNonTypeTemplateParameterType(NewDI->getType(),
+ D->getLocation());
+ if (NewT.isNull())
+ return 0;
+ ExpandedParameterPackTypes.push_back(NewT);
+ }
+
+ IsExpandedParameterPack = true;
+ DI = D->getTypeSourceInfo();
+ T = DI->getType();
+ } else if (isa<PackExpansionTypeLoc>(TL)) {
+ // The non-type template parameter pack's type is a pack expansion of types.
+ // Determine whether we need to expand this parameter pack into separate
+ // types.
+ PackExpansionTypeLoc Expansion = cast<PackExpansionTypeLoc>(TL);
+ TypeLoc Pattern = Expansion.getPatternLoc();
+ llvm::SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+
+ // Determine whether the set of unexpanded parameter packs can and should
+ // be expanded.
+ bool Expand = true;
+ bool RetainExpansion = false;
+ llvm::Optional<unsigned> OrigNumExpansions
+ = Expansion.getTypePtr()->getNumExpansions();
+ llvm::Optional<unsigned> NumExpansions = OrigNumExpansions;
+ if (SemaRef.CheckParameterPacksForExpansion(Expansion.getEllipsisLoc(),
+ Pattern.getSourceRange(),
+ Unexpanded.data(),
+ Unexpanded.size(),
+ TemplateArgs,
+ Expand, RetainExpansion,
+ NumExpansions))
+ return 0;
+
+ if (Expand) {
+ for (unsigned I = 0; I != *NumExpansions; ++I) {
+ Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, I);
+ TypeSourceInfo *NewDI = SemaRef.SubstType(Pattern, TemplateArgs,
+ D->getLocation(),
+ D->getDeclName());
+ if (!NewDI)
+ return 0;
+
+ ExpandedParameterPackTypesAsWritten.push_back(NewDI);
+ QualType NewT = SemaRef.CheckNonTypeTemplateParameterType(
+ NewDI->getType(),
+ D->getLocation());
+ if (NewT.isNull())
+ return 0;
+ ExpandedParameterPackTypes.push_back(NewT);
+ }
+
+ // Note that we have an expanded parameter pack. The "type" of this
+ // expanded parameter pack is the original expansion type, but callers
+ // will end up using the expanded parameter pack types for type-checking.
+ IsExpandedParameterPack = true;
+ DI = D->getTypeSourceInfo();
+ T = DI->getType();
+ } else {
+ // We cannot fully expand the pack expansion now, so substitute into the
+ // pattern and create a new pack expansion type.
+ Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(SemaRef, -1);
+ TypeSourceInfo *NewPattern = SemaRef.SubstType(Pattern, TemplateArgs,
+ D->getLocation(),
+ D->getDeclName());
+ if (!NewPattern)
+ return 0;
+
+ DI = SemaRef.CheckPackExpansion(NewPattern, Expansion.getEllipsisLoc(),
+ NumExpansions);
+ if (!DI)
+ return 0;
+
+ T = DI->getType();
+ }
+ } else {
+ // Simple case: substitution into a parameter that is not a parameter pack.
+ DI = SemaRef.SubstType(D->getTypeSourceInfo(), TemplateArgs,
+ D->getLocation(), D->getDeclName());
+ if (!DI)
+ return 0;
+
+ // Check that this type is acceptable for a non-type template parameter.
+ bool Invalid = false;
+ T = SemaRef.CheckNonTypeTemplateParameterType(DI->getType(),
+ D->getLocation());
+ if (T.isNull()) {
+ T = SemaRef.Context.IntTy;
+ Invalid = true;
+ }
}
- // FIXME: Variadic templates.
- NonTypeTemplateParmDecl *Param
- = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner, D->getLocation(),
+ NonTypeTemplateParmDecl *Param;
+ if (IsExpandedParameterPack)
+ Param = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner,
+ D->getLocation(),
D->getDepth() - TemplateArgs.getNumLevels(),
- D->getPosition(), D->getIdentifier(), T,
- D->isParameterPack(), DI);
+ D->getPosition(),
+ D->getIdentifier(), T,
+ DI,
+ ExpandedParameterPackTypes.data(),
+ ExpandedParameterPackTypes.size(),
+ ExpandedParameterPackTypesAsWritten.data());
+ else
+ Param = NonTypeTemplateParmDecl::Create(SemaRef.Context, Owner,
+ D->getLocation(),
+ D->getDepth() - TemplateArgs.getNumLevels(),
+ D->getPosition(),
+ D->getIdentifier(), T,
+ D->isParameterPack(), DI);
+
if (Invalid)
Param->setInvalidDecl();