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.

llvm-svn: 149685
This commit is contained in:
Douglas Gregor 2012-02-03 07:34:46 +00:00
parent 7edbdfc97c
commit 8e07261362
2 changed files with 221 additions and 45 deletions

View File

@ -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;

View File

@ -0,0 +1,120 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
template<typename T, typename U> struct pair { };
template<typename ...Types> struct tuple { };
template<typename T, typename U>
struct is_same {
static const bool value = false;
};
template<typename T>
struct is_same<T, T> {
static const bool value = true;
};
namespace ExpandIntoFixed {
template<typename T,
typename U,
typename V = pair<T, U>,
typename W = V*>
class X0 { };
template<typename ...Ts>
class X1 {
public:
typedef X0<Ts...> type;
};
static_assert(is_same<X1<int, int>::type,
X0<int, int, pair<int, int>, pair<int, int>*>>::value,
"fails with two default arguments");
static_assert(is_same<X1<int, int, float>::type,
X0<int, int, float, float*>>::value,
"fails with one default argument");
static_assert(is_same<X1<int, int, float, double>::type,
X0<int, int, float, double>>::value,
"fails with no default arguments");
}
namespace ExpandIntoFixedShifted {
template<typename T,
typename U,
typename V = pair<T, U>,
typename W = V*>
class X0 { };
template<typename ...Ts>
class X1 {
public:
typedef X0<char, Ts...> type;
};
static_assert(is_same<X1<int>::type,
X0<char, int, pair<char, int>, pair<char, int>*>>::value,
"fails with two default arguments");
static_assert(is_same<X1<int, float>::type,
X0<char, int, float, float*>>::value,
"fails with one default argument");
static_assert(is_same<X1<int, float, double>::type,
X0<char, int, float, double>>::value,
"fails with no default arguments");
}
namespace Deduction {
template <typename X, typename Y = double> struct Foo {};
template <typename ...Args> tuple<Args...> &foo(Foo<Args...>);
void call_foo(Foo<int, float> foo_if, Foo<int> foo_i) {
tuple<int, float> &t1 = foo(foo_if);
tuple<int, double> &t2 = foo(foo_i);
}
}
namespace PR9021a {
template<typename, typename>
struct A { };
template<typename ...T>
struct B {
A<T...> a1;
};
void test() {
B<int, int> c;
}
}
namespace PR9021b {
template<class, class>
struct t2
{
};
template<template<class...> class M>
struct m
{
template<class... B>
using inner = M<B...>;
};
m<t2> sta2;
}
namespace PartialSpecialization {
template<typename T, typename U, typename V = U>
struct X0; // expected-note{{template is declared here}}
template<typename ...Ts>
struct X0<Ts...> {
};
X0<int> x0i; // expected-error{{too few template arguments for class template 'X0'}}
X0<int, float> x0if;
X0<int, float, double> x0ifd;
}