aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaTemplate.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2012-02-03 07:34:46 +0000
committerDouglas Gregor <dgregor@apple.com>2012-02-03 07:34:46 +0000
commit8fbbae532e3cb5f45e9e862c60d48c78b0997325 (patch)
tree98704d9e6ac89eca9dd0657e6e2f137b8d21b5d3 /lib/Sema/SemaTemplate.cpp
parent09e9cf0927641ee64bb0a354b0a6acca604361cc (diff)
Implement support for a pack expansion into a fixed-length
template. Such pack expansions can easily fail at template instantiation time, if the expanded parameter packs are of the wrong length. Fixes <rdar://problem/10040867>, PR9021, and the example that came up today at Going Native. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149685 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaTemplate.cpp')
-rw-r--r--lib/Sema/SemaTemplate.cpp146
1 files changed, 101 insertions, 45 deletions
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index 322e473215..7cd604d585 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1887,7 +1887,6 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
}
}
-
QualType Sema::CheckTemplateIdType(TemplateName Name,
SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs) {
@@ -1923,9 +1922,6 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
false, Converted))
return QualType();
- assert((Converted.size() == Template->getTemplateParameters()->size()) &&
- "Converted template argument list is too short!");
-
QualType CanonType;
bool InstantiationDependent = false;
@@ -2866,6 +2862,29 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
return false;
}
+/// \brief Diagnose an arity mismatch in the
+static bool diagnoseArityMismatch(Sema &S, TemplateDecl *Template,
+ SourceLocation TemplateLoc,
+ TemplateArgumentListInfo &TemplateArgs) {
+ TemplateParameterList *Params = Template->getTemplateParameters();
+ unsigned NumParams = Params->size();
+ unsigned NumArgs = TemplateArgs.size();
+
+ SourceRange Range;
+ if (NumArgs > NumParams)
+ Range = SourceRange(TemplateArgs[NumParams].getLocation(),
+ TemplateArgs.getRAngleLoc());
+ S.Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+ << (NumArgs > NumParams)
+ << (isa<ClassTemplateDecl>(Template)? 0 :
+ isa<FunctionTemplateDecl>(Template)? 1 :
+ isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
+ << Template << Range;
+ S.Diag(Template->getLocation(), diag::note_template_decl_here)
+ << Params->getSourceRange();
+ return true;
+}
+
/// \brief Check that the given template argument list is well-formed
/// for specializing the given template.
bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
@@ -2883,26 +2902,6 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
bool HasParameterPack =
NumParams > 0 && Params->getParam(NumParams - 1)->isTemplateParameterPack();
- if ((NumArgs > NumParams && !HasParameterPack) ||
- (NumArgs < Params->getMinRequiredArguments() &&
- !PartialTemplateArgs)) {
- // FIXME: point at either the first arg beyond what we can handle,
- // or the '>', depending on whether we have too many or too few
- // arguments.
- SourceRange Range;
- if (NumArgs > NumParams)
- Range = SourceRange(TemplateArgs[NumParams].getLocation(), RAngleLoc);
- Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
- << (NumArgs > NumParams)
- << (isa<ClassTemplateDecl>(Template)? 0 :
- isa<FunctionTemplateDecl>(Template)? 1 :
- isa<TemplateTemplateParmDecl>(Template)? 2 : 3)
- << Template << Range;
- Diag(Template->getLocation(), diag::note_template_decl_here)
- << Params->getSourceRange();
- Invalid = true;
- }
-
// 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
@@ -2914,10 +2913,12 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
ParamEnd = Params->end();
unsigned ArgIdx = 0;
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<NonTypeTemplateParmDecl>(*Param)) {
if (NTTP->isExpandedParameterPack() &&
@@ -2951,6 +2952,15 @@ 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;
+ }
+
++ArgIdx;
continue;
}
@@ -2970,7 +2980,7 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
if ((*Param)->isTemplateParameterPack())
break;
- // We have a default template argument that we will use.
+ // Check whether we have a default argument.
TemplateArgumentLoc Arg;
// Retrieve the default template argument from the template
@@ -2979,10 +2989,9 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
// (when the template parameter was part of a nested template) into
// the default argument.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) {
- if (!TTP->hasDefaultArgument()) {
- assert(Invalid && "Missing default argument");
- break;
- }
+ if (!TTP->hasDefaultArgument())
+ return diagnoseArityMismatch(*this, Template, TemplateLoc,
+ TemplateArgs);
TypeSourceInfo *ArgType = SubstDefaultTemplateArgument(*this,
Template,
@@ -2997,10 +3006,9 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
ArgType);
} else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
- if (!NTTP->hasDefaultArgument()) {
- assert(Invalid && "Missing default argument");
- break;
- }
+ if (!NTTP->hasDefaultArgument())
+ return diagnoseArityMismatch(*this, Template, TemplateLoc,
+ TemplateArgs);
ExprResult E = SubstDefaultTemplateArgument(*this, Template,
TemplateLoc,
@@ -3016,10 +3024,9 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
TemplateTemplateParmDecl *TempParm
= cast<TemplateTemplateParmDecl>(*Param);
- if (!TempParm->hasDefaultArgument()) {
- assert(Invalid && "Missing default argument");
- break;
- }
+ if (!TempParm->hasDefaultArgument())
+ return diagnoseArityMismatch(*this, Template, TemplateLoc,
+ TemplateArgs);
NestedNameSpecifierLoc QualifierLoc;
TemplateName Name = SubstDefaultTemplateArgument(*this, Template,
@@ -3057,11 +3064,65 @@ 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();
+ }
+ }
+
+ 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<NonTypeTemplateParmDecl>(*Param)) {
+ if (NTTP->isExpandedParameterPack() &&
+ ArgumentPack.size() < NTTP->getNumExpansionTypes()) {
+ Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+ << false
+ << (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;
+ }
+ }
+ }
+
// 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;
@@ -3073,7 +3134,8 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template,
ArgumentPack.size()));
ArgumentPack.clear();
}
- }
+ } else if (!PartialTemplateArgs)
+ return diagnoseArityMismatch(*this, Template, TemplateLoc, TemplateArgs);
++Param;
}
@@ -4961,9 +5023,6 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
TemplateArgs, false, Converted))
return true;
- assert((Converted.size() == ClassTemplate->getTemplateParameters()->size()) &&
- "Converted template argument list is too short!");
-
// Find the class template (partial) specialization declaration that
// corresponds to these arguments.
if (isPartialSpecialization) {
@@ -5955,9 +6014,6 @@ Sema::ActOnExplicitInstantiation(Scope *S,
TemplateArgs, false, Converted))
return true;
- assert((Converted.size() == ClassTemplate->getTemplateParameters()->size()) &&
- "Converted template argument list is too short!");
-
// Find the class template specialization declaration that
// corresponds to these arguments.
void *InsertPos = 0;