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


_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to