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:
parent
7edbdfc97c
commit
8e07261362
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue