From: Faisal Vali Date: Thu, 10 Dec 2015 05:36:39 +0000 (+0000) Subject: Fix PR24694 (CWG1591): Deducing array bound and element type from initializer list X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3648de7a637e6f4d99c1cc091d70158d27644d69;p=clang 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 int *f(T (&&)[N]); int *ip = f({1, 2, 3}); git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@255221 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 4b811c7e25..3a3958c5da 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3212,24 +3212,63 @@ DeduceFromInitializerList(Sema &S, TemplateParameterList *TemplateParams, TemplateDeductionInfo &Info, SmallVectorImpl &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, 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 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))) - return true; + // If we are not deducing against the 'T' in a std::initializer_list 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; } diff --git a/test/CXX/drs/dr15xx.cpp b/test/CXX/drs/dr15xx.cpp index d35583ff9f..29887120c2 100644 --- a/test/CXX/drs/dr15xx.cpp +++ b/test/CXX/drs/dr15xx.cpp @@ -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 int h(T const(&)[N]); + int X = h({1,2,3}); // T deduced to int, N deduced to 3 + + template 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 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 const(&)[M][N]); + int X0 = m({{1,2},{3,4}}); // M and N both deduced to 2 + + template 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 int ***f(const T (&a)[N][M][O]); //expected-note{{deduced conflicting values}} + template int **f(const T (&a)[N][M]); //expected-note{{couldn't infer}} + + template 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 int ***f(T (&&a)[N][M][O]); //expected-note{{deduced conflicting values}} + template int **f(T (&&a)[N][M]); //expected-note{{couldn't infer}} + + template 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 float *f(const std::initializer_list (&)[N]); + template double *f(const T(&)[N]); + double *p = f({1, 2, 3}); + float *fp = f({{1}, {1, 2}, {1, 2, 3}}); + } +} // dr1591 + #endif