Implement partial ordering of class template partial specializations

and function templates that contain variadic templates. This involves
three small-ish changes:

  (1) When transforming a pack expansion, if the transformed argument
  still contains unexpanded parameter packs, build a pack
  expansion. This can happen during the substitution that occurs into
  class template partial specialiation template arguments during
  partial ordering. 
 
  (2) When performing template argument deduction where the argument
  is a pack expansion, match against the pattern of that pack
  expansion.

  (3) When performing template argument deduction against a non-pack
  parameter, or a non-expansion template argument, deduction fails if
  the argument itself is a pack expansion (C++0x
  [temp.deduct.type]p22).

llvm-svn: 123279
This commit is contained in:
Douglas Gregor 2011-01-11 22:21:24 +00:00
parent cb9c4f85ec
commit 2fcb863b2b
5 changed files with 114 additions and 6 deletions

View File

@ -80,7 +80,7 @@ static Sema::TemplateDeductionResult
DeduceTemplateArguments(Sema &S,
TemplateParameterList *TemplateParams,
const TemplateArgument &Param,
const TemplateArgument &Arg,
TemplateArgument Arg,
TemplateDeductionInfo &Info,
llvm::SmallVectorImpl<DeducedTemplateArgument> &Deduced);
@ -677,6 +677,13 @@ DeduceTemplateArguments(Sema &S,
if (ArgIdx >= NumArgs)
return Sema::TDK_NonDeducedMismatch;
if (isa<PackExpansionType>(Args[ArgIdx])) {
// C++0x [temp.deduct.type]p22:
// If the original function parameter associated with A is a function
// parameter pack and the function parameter associated with P is not
// a function parameter pack, then template argument deduction fails.
return Sema::TDK_NonDeducedMismatch;
}
if (Sema::TemplateDeductionResult Result
= DeduceTemplateArguments(S, TemplateParams,
@ -814,6 +821,12 @@ DeduceTemplateArguments(Sema &S,
QualType Param = S.Context.getCanonicalType(ParamIn);
QualType Arg = S.Context.getCanonicalType(ArgIn);
// If the argument type is a pack expansion, look at its pattern.
// This isn't explicitly called out
if (const PackExpansionType *ArgExpansion
= dyn_cast<PackExpansionType>(Arg))
Arg = ArgExpansion->getPattern();
if (PartialOrdering) {
// C++0x [temp.deduct.partial]p5:
// Before the partial ordering is done, certain transformations are
@ -1273,9 +1286,15 @@ static Sema::TemplateDeductionResult
DeduceTemplateArguments(Sema &S,
TemplateParameterList *TemplateParams,
const TemplateArgument &Param,
const TemplateArgument &Arg,
TemplateArgument Arg,
TemplateDeductionInfo &Info,
llvm::SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
// If the template argument is a pack expansion, perform template argument
// deduction against the pattern of that expansion. This only occurs during
// partial ordering.
if (Arg.isPackExpansion())
Arg = Arg.getPackExpansionPattern();
switch (Param.getKind()) {
case TemplateArgument::Null:
assert(false && "Null template argument in parameter list");
@ -1448,11 +1467,17 @@ DeduceTemplateArguments(Sema &S,
return NumberOfArgumentsMustMatch? Sema::TDK_NonDeducedMismatch
: Sema::TDK_Success;
if (Args[ArgIdx].isPackExpansion()) {
// FIXME: We follow the logic of C++0x [temp.deduct.type]p22 here,
// but applied to pack expansions that are template arguments.
return Sema::TDK_NonDeducedMismatch;
}
// Perform deduction for this Pi/Ai pair.
if (Sema::TemplateDeductionResult Result
= DeduceTemplateArguments(S, TemplateParams,
Params[ParamIdx], Args[ArgIdx],
Info, Deduced))
= DeduceTemplateArguments(S, TemplateParams,
Params[ParamIdx], Args[ArgIdx],
Info, Deduced))
return Result;
// Move to the next argument.
@ -1792,7 +1817,7 @@ FinishTemplateArgumentDeduction(Sema &S,
return Sema::TDK_SubstitutionFailure;
}
}
// Form the template argument list from the deduced template arguments.
TemplateArgumentList *DeducedArgumentList
= TemplateArgumentList::CreateCopy(S.Context, Builder.data(),

View File

@ -2327,6 +2327,12 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs,
if (Out.isInvalid())
return true;
if (Out.get()->containsUnexpandedParameterPack()) {
Out = RebuildPackExpansion(Out.get(), Expansion->getEllipsisLoc());
if (Out.isInvalid())
return true;
}
if (ArgChanged)
*ArgChanged = true;
Outputs.push_back(Out.get());
@ -2847,6 +2853,12 @@ bool TreeTransform<Derived>::TransformTemplateArguments(InputIterator First,
if (getDerived().TransformTemplateArgument(Pattern, Out))
return true;
if (Out.getArgument().containsUnexpandedParameterPack()) {
Out = getDerived().RebuildPackExpansion(Out, Ellipsis);
if (Out.getArgument().isNull())
return true;
}
Outputs.addArgument(Out);
}

View File

@ -0,0 +1,30 @@
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
// Various tests related to partial ordering of variadic templates.
template<typename ...Types> struct tuple;
template<typename Tuple>
struct X1 {
static const unsigned value = 0;
};
template<typename Head, typename ...Tail>
struct X1<tuple<Head, Tail...> > {
static const unsigned value = 1;
};
template<typename Head, typename ...Tail>
struct X1<tuple<Head, Tail&...> > {
static const unsigned value = 2;
};
template<typename Head, typename ...Tail>
struct X1<tuple<Head&, Tail&...> > {
static const unsigned value = 3;
};
int check0[X1<tuple<>>::value == 0? 1 : -1];
int check1[X1<tuple<int>>::value == 2? 1 : -1];
int check2[X1<tuple<int, int>>::value == 1? 1 : -1];
int check3[X1<tuple<int, int&>>::value == 2? 1 : -1];
int check4[X1<tuple<int&, int&>>::value == 3? 1 : -1];

View File

@ -0,0 +1,27 @@
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
// Note: Partial ordering of function templates containing template
// parameter packs is independent of the number of deduced arguments
// for those template parameter packs.
template<class ...> struct Tuple { };
template<class ... Types> int &g(Tuple<Types ...>); // #1
template<class T1, class ... Types> float &g(Tuple<T1, Types ...>); // #2
template<class T1, class ... Types> double &g(Tuple<T1, Types& ...>); // #3
void test_g() {
int &ir1 = g(Tuple<>());
float &fr1 = g(Tuple<int, float>());
double &dr1 = g(Tuple<int, float&>());
double &dr2 = g(Tuple<int>());
}
template<class ... Types> int &h(int (*)(Types ...)); // #1
template<class T1, class ... Types> float &h(int (*)(T1, Types ...)); // #2
template<class T1, class ... Types> double &h(int (*)(T1, Types& ...)); // #3
void test_h() {
int &ir1 = h((int(*)())0);
float &fr1 = h((int(*)(int, float))0);
double &dr1 = h((int(*)(int, float&))0);
double &dr2 = h((int(*)(int))0);
}

View File

@ -0,0 +1,14 @@
// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
// If the original function parameter associated with A is a function
// parameter pack and the function parameter associated with P is not
// a function parameter pack, then template argument deduction fails.
template<class ... Args> int& f(Args ... args);
template<class T1, class ... Args> float& f(T1 a1, Args ... args);
template<class T1, class T2> double& f(T1 a1, T2 a2);
void test_f() {
int &ir1 = f();
float &fr1 = f(1, 2, 3);
double &dr1 = f(1, 2);
}