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

Faisal Vali via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 9 21:36:40 PST 2015


Author: faisalv
Date: Wed Dec  9 23:36:39 2015
New Revision: 255221

URL: http://llvm.org/viewvc/llvm-project?rev=255221&view=rev
Log:
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});



Modified:
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/CXX/drs/dr15xx.cpp

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=255221&r1=255220&r2=255221&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Wed Dec  9 23:36:39 2015
@@ -3212,24 +3212,63 @@ DeduceFromInitializerList(Sema &S, Templ
                           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)))
-      return true;
+  // 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;
 }
 

Modified: cfe/trunk/test/CXX/drs/dr15xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr15xx.cpp?rev=255221&r1=255220&r2=255221&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr15xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr15xx.cpp Wed Dec  9 23:36:39 2015
@@ -101,4 +101,55 @@ namespace dr1589 {   // dr1589: 3.7 c++1
   }
 
 } // 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




More information about the cfe-commits mailing list