Fix PR24694 (CWG1591): Deducing array bound and element type from initializer list

https://llvm.org/bugs/show_bug.cgi?id=24694
http://wg21.link/cwg1591

Teach DeduceFromInitializerList in SemaTemplateDeduction.cpp to deduce against array (constant and dependent sized) parameters (really, reference to arrays since they don't decay to pointers), by checking if the template parameter is either one of those kinds of arrays, and if so, deducing each initializer list element against the element type, and then deducing the array bound if needed.

In brief, this patch enables the following code:
template<class T, int N> int *f(T (&&)[N]);
int *ip = f({1, 2, 3});

llvm-svn: 255221
This commit is contained in:
Faisal Vali 2015-12-10 05:36:39 +00:00
parent f170ba08af
commit f6dfdb361f
2 changed files with 103 additions and 13 deletions

View File

@ -3212,24 +3212,63 @@ DeduceFromInitializerList(Sema &S, TemplateParameterList *TemplateParams,
TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
unsigned TDF, Sema::TemplateDeductionResult &Result) {
// If the argument is an initializer list then the parameter is an undeduced
// context, unless the parameter type is (reference to cv)
// std::initializer_list<P'>, in which case deduction is done for each element
// of the initializer list as-if it were an argument in a function call, and
// the result is the deduced type if it's the same for all elements.
QualType X;
if (!S.isStdInitializerList(AdjustedParamType, &X))
// [temp.deduct.call] p1 (post CWG-1591)
// If removing references and cv-qualifiers from P gives
// std::initializer_list<P0> or P0[N] for some P0 and N and the argument is a
// non-empty initializer list (8.5.4), then deduction is performed instead for
// each element of the initializer list, taking P0 as a function template
// parameter type and the initializer element as its argument, and in the
// P0[N] case, if N is a non-type template parameter, N is deduced from the
// length of the initializer list. Otherwise, an initializer list argument
// causes the parameter to be considered a non-deduced context
const bool IsConstSizedArray = AdjustedParamType->isConstantArrayType();
const bool IsDependentSizedArray =
!IsConstSizedArray && AdjustedParamType->isDependentSizedArrayType();
QualType ElTy; // The type of the std::initializer_list or the array element.
const bool IsSTDList = !IsConstSizedArray && !IsDependentSizedArray &&
S.isStdInitializerList(AdjustedParamType, &ElTy);
if (!IsConstSizedArray && !IsDependentSizedArray && !IsSTDList)
return false;
Result = Sema::TDK_Success;
// Recurse down into the init list.
for (unsigned i = 0, e = ILE->getNumInits(); i < e; ++i) {
if ((Result = DeduceTemplateArgumentByListElement(
S, TemplateParams, X, ILE->getInit(i), Info, Deduced, TDF)))
// If we are not deducing against the 'T' in a std::initializer_list<T> then
// deduce against the 'T' in T[N].
if (ElTy.isNull()) {
assert(!IsSTDList);
ElTy = S.Context.getAsArrayType(AdjustedParamType)->getElementType();
}
// Deduction only needs to be done for dependent types.
if (ElTy->isDependentType()) {
for (Expr *E : ILE->inits()) {
if (Result = DeduceTemplateArgumentByListElement(S, TemplateParams, ElTy,
E, Info, Deduced, TDF))
return true;
}
}
if (IsDependentSizedArray) {
const DependentSizedArrayType *ArrTy =
S.Context.getAsDependentSizedArrayType(AdjustedParamType);
// Determine the array bound is something we can deduce.
if (NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(ArrTy->getSizeExpr())) {
// We can perform template argument deduction for the given non-type
// template parameter.
assert(NTTP->getDepth() == 0 &&
"Cannot deduce non-type template argument at depth > 0");
llvm::APInt Size(S.Context.getIntWidth(NTTP->getType()),
ILE->getNumInits());
Result = DeduceNonTypeTemplateArgument(
S, NTTP, llvm::APSInt(Size), NTTP->getType(),
/*ArrayBound=*/true, Info, Deduced);
}
}
return true;
}

View File

@ -101,4 +101,55 @@ namespace dr1589 { // dr1589: 3.7 c++11
}
} // dr1589
namespace dr1591 { //dr1591. Deducing array bound and element type from initializer list
template<class T, int N> int h(T const(&)[N]);
int X = h({1,2,3}); // T deduced to int, N deduced to 3
template<class T> int j(T const(&)[3]);
int Y = j({42}); // T deduced to int, array bound not considered
struct Aggr { int i; int j; };
template<int N> int k(Aggr const(&)[N]); //expected-note{{not viable}}
int Y0 = k({1,2,3}); //expected-error{{no matching function}}
int Z = k({{1},{2},{3}}); // OK, N deduced to 3
template<int M, int N> int m(int const(&)[M][N]);
int X0 = m({{1,2},{3,4}}); // M and N both deduced to 2
template<class T, int N> int n(T const(&)[N], T);
int X1 = n({{1},{2},{3}},Aggr()); // OK, T is Aggr, N is 3
namespace check_multi_dim_arrays {
template<class T, int N, int M, int O> int ***f(const T (&a)[N][M][O]); //expected-note{{deduced conflicting values}}
template<class T, int N, int M> int **f(const T (&a)[N][M]); //expected-note{{couldn't infer}}
template<class T, int N> int *f(const T (&a)[N]); //expected-note{{couldn't infer}}
int ***p3 = f({ { {1,2}, {3, 4} }, { {5,6}, {7, 8} }, { {9,10}, {11, 12} } });
int ***p33 = f({ { {1,2}, {3, 4} }, { {5,6}, {7, 8} }, { {9,10}, {11, 12, 13} } }); //expected-error{{no matching}}
int **p2 = f({ {1,2,3}, {3, 4, 5} });
int **p22 = f({ {1,2}, {3, 4} });
int *p1 = f({1, 2, 3});
}
namespace check_multi_dim_arrays_rref {
template<class T, int N, int M, int O> int ***f(T (&&a)[N][M][O]); //expected-note{{deduced conflicting values}}
template<class T, int N, int M> int **f(T (&&a)[N][M]); //expected-note{{couldn't infer}}
template<class T, int N> int *f(T (&&a)[N]); //expected-note{{couldn't infer}}
int ***p3 = f({ { {1,2}, {3, 4} }, { {5,6}, {7, 8} }, { {9,10}, {11, 12} } });
int ***p33 = f({ { {1,2}, {3, 4} }, { {5,6}, {7, 8} }, { {9,10}, {11, 12, 13} } }); //expected-error{{no matching}}
int **p2 = f({ {1,2,3}, {3, 4, 5} });
int **p22 = f({ {1,2}, {3, 4} });
int *p1 = f({1, 2, 3});
}
namespace check_arrays_of_init_list {
template<class T, int N> float *f(const std::initializer_list<T> (&)[N]);
template<class T, int N> double *f(const T(&)[N]);
double *p = f({1, 2, 3});
float *fp = f({{1}, {1, 2}, {1, 2, 3}});
}
} // dr1591
#endif