r291425 - Implement DR1388 (wg21.link/cwg1388).

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 8 23:14:41 PST 2017


Author: rsmith
Date: Mon Jan  9 01:14:40 2017
New Revision: 291425

URL: http://llvm.org/viewvc/llvm-project?rev=291425&view=rev
Log:
Implement DR1388 (wg21.link/cwg1388).

This issue clarifies how deduction proceeds past a non-trailing function
parameter pack. Essentially, the pack itself is skipped and consumes no
arguments (except for those implied by an explicitly-specified template
arguments), and nothing is deduced from it. As a small fix to the standard's
rule, we do not allow subsequent deduction to change the length of the function
parameter pack (by preventing extension of the explicitly-specified pack if
present, and otherwise deducing all contained packs to empty packs).

Modified:
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/CXX/drs/dr13xx.cpp
    cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp
    cfe/trunk/test/SemaTemplate/deduction.cpp
    cfe/trunk/www/cxx_dr_status.html

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=291425&r1=291424&r2=291425&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Mon Jan  9 01:14:40 2017
@@ -669,6 +669,19 @@ public:
       Info.PendingDeducedPacks[Pack.Index] = Pack.Outer;
   }
 
+  /// Determine whether this pack has already been partially expanded into a
+  /// sequence of (prior) function parameters / template arguments.
+  bool isPartiallyExpanded() {
+    if (Packs.size() != 1 || !S.CurrentInstantiationScope)
+      return false;
+
+    auto *PartiallySubstitutedPack =
+        S.CurrentInstantiationScope->getPartiallySubstitutedPack();
+    return PartiallySubstitutedPack &&
+           getDepthAndIndex(PartiallySubstitutedPack) ==
+               std::make_pair(Info.getDeducedDepth(), Packs.front().Index);
+  }
+
   /// Move to deducing the next element in each pack that is being deduced.
   void nextPackElement() {
     // Capture the deduced template arguments for each parameter pack expanded
@@ -2552,6 +2565,12 @@ static bool isSimpleTemplateIdType(QualT
   return false;
 }
 
+static void
+MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
+                           bool OnlyDeduced,
+                           unsigned Level,
+                           llvm::SmallBitVector &Deduced);
+
 /// \brief Substitute the explicitly-provided template arguments into the
 /// given function template according to C++ [temp.arg.explicit].
 ///
@@ -2613,7 +2632,7 @@ Sema::SubstituteExplicitTemplateArgument
   // Enter a new template instantiation context where we check the
   // explicitly-specified template arguments against this function template,
   // and then substitute them into the function parameter types.
-  SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
+  SmallVector<TemplateArgument, 4> DeducedArgs;
   InstantiatingTemplate Inst(*this, Info.getLocation(), FunctionTemplate,
                              DeducedArgs,
            ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution,
@@ -3389,7 +3408,6 @@ Sema::TemplateDeductionResult Sema::Dedu
   //   Template argument deduction is done by comparing each function template
   //   parameter type (call it P) with the type of the corresponding argument
   //   of the call (call it A) as described below.
-  unsigned CheckArgs = Args.size();
   if (Args.size() < Function->getMinRequiredArguments() && !PartialOverloading)
     return TDK_TooFewArguments;
   else if (TooManyArguments(NumParams, Args.size(), PartialOverloading)) {
@@ -3397,9 +3415,7 @@ Sema::TemplateDeductionResult Sema::Dedu
       = Function->getType()->getAs<FunctionProtoType>();
     if (Proto->isTemplateVariadic())
       /* Do nothing */;
-    else if (Proto->isVariadic())
-      CheckArgs = NumParams;
-    else
+    else if (!Proto->isVariadic())
       return TDK_TooManyArguments;
   }
 
@@ -3456,7 +3472,7 @@ Sema::TemplateDeductionResult Sema::Dedu
         dyn_cast<PackExpansionType>(ParamType);
     if (!ParamExpansion) {
       // Simple case: matching a function parameter to a function argument.
-      if (ArgIdx >= CheckArgs)
+      if (ArgIdx >= Args.size())
         break;
 
       if (auto Result = DeduceCallArgument(ParamType, ArgIdx++))
@@ -3465,36 +3481,47 @@ Sema::TemplateDeductionResult Sema::Dedu
       continue;
     }
 
+    QualType ParamPattern = ParamExpansion->getPattern();
+    PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info,
+                                 ParamPattern);
+
     // C++0x [temp.deduct.call]p1:
     //   For a function parameter pack that occurs at the end of the
     //   parameter-declaration-list, the type A of each remaining argument of
     //   the call is compared with the type P of the declarator-id of the
     //   function parameter pack. Each comparison deduces template arguments
     //   for subsequent positions in the template parameter packs expanded by
-    //   the function parameter pack. For a function parameter pack that does
-    //   not occur at the end of the parameter-declaration-list, the type of
-    //   the parameter pack is a non-deduced context.
-    // FIXME: This does not say that subsequent parameters are also non-deduced.
-    // See also DR1388 / DR1399, which effectively says we should keep deducing
-    // after the pack.
-    if (ParamIdx + 1 < NumParamTypes)
-      break;
-
-    QualType ParamPattern = ParamExpansion->getPattern();
-    PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info,
-                                 ParamPattern);
-
-    for (; ArgIdx < Args.size(); PackScope.nextPackElement(), ++ArgIdx)
-      if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx))
-        return Result;
+    //   the function parameter pack. When a function parameter pack appears
+    //   in a non-deduced context [not at the end of the list], the type of
+    //   that parameter pack is never deduced.
+    //
+    // FIXME: The above rule allows the size of the parameter pack to change
+    // after we skip it (in the non-deduced case). That makes no sense, so
+    // we instead notionally deduce the pack against N arguments, where N is
+    // the length of the explicitly-specified pack if it's expanded by the
+    // parameter pack and 0 otherwise, and we treat each deduction as a
+    // non-deduced context.
+    if (ParamIdx + 1 == NumParamTypes) {
+      for (; ArgIdx < Args.size(); PackScope.nextPackElement(), ++ArgIdx)
+        if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx))
+          return Result;
+    } else {
+      // If the parameter type contains an explicitly-specified pack that we
+      // could not expand, skip the number of parameters notionally created
+      // by the expansion.
+      Optional<unsigned> NumExpansions = ParamExpansion->getNumExpansions();
+      if (NumExpansions && !PackScope.isPartiallyExpanded())
+        for (unsigned I = 0; I != *NumExpansions && ArgIdx < Args.size();
+             ++I, ++ArgIdx)
+          // FIXME: Should we add OriginalCallArgs for these? What if the
+          // corresponding argument is a list?
+          PackScope.nextPackElement();
+    }
 
     // Build argument packs for each of the parameter packs expanded by this
     // pack expansion.
     if (auto Result = PackScope.finish())
       return Result;
-
-    // After we've matching against a parameter pack, we're done.
-    break;
   }
 
   return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced,
@@ -4230,12 +4257,6 @@ bool Sema::DeduceReturnType(FunctionDecl
   return StillUndeduced;
 }
 
-static void
-MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
-                           bool OnlyDeduced,
-                           unsigned Level,
-                           llvm::SmallBitVector &Deduced);
-
 /// \brief If this is a non-static member function,
 static void
 AddImplicitObjectParameterType(ASTContext &Context,

Modified: cfe/trunk/test/CXX/drs/dr13xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr13xx.cpp?rev=291425&r1=291424&r2=291425&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr13xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr13xx.cpp Mon Jan  9 01:14:40 2017
@@ -174,3 +174,55 @@ namespace dr1359 { // dr1359: 3.5
   constexpr Y y = Y(); // expected-error {{no matching}}
 #endif
 }
+
+namespace dr1388 { // dr1388: 4.0
+  template<typename A, typename ...T> void f(T..., A); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}}
+  template<typename ...T> void g(T..., int); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}}
+  template<typename ...T, typename A> void h(T..., A); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}}
+
+  void test_f() { 
+    f(0); // ok, trailing parameter pack deduced to empty
+    f(0, 0); // expected-error {{no matching}}
+    f<int>(0);
+    f<int>(0, 0); // expected-error {{no matching}}
+    f<int, int>(0, 0);
+    f<int, int, int>(0, 0); // expected-error {{no matching}}
+
+    g(0);
+    g(0, 0); // expected-error {{no matching}}
+    g<>(0);
+    g<int>(0); // expected-error {{no matching}}
+    g<int>(0, 0);
+
+    h(0);
+    h(0, 0); // expected-error {{no matching}}
+    h<int>(0, 0);
+    h<int, int>(0, 0); // expected-error {{no matching}}
+  }
+
+  // A non-trailing parameter pack is still a non-deduced context, even though
+  // we know exactly how many arguments correspond to it.
+  template<typename T, typename U> struct pair {};
+  template<typename ...T> struct tuple { typedef char type; }; // expected-error 0-2{{C++11}}
+  template<typename ...T, typename ...U> void f_pair_1(pair<T, U>..., int); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 0)}}
+  template<typename ...T, typename U> void f_pair_2(pair<T, char>..., U); // expected-error 0-2{{C++11}}
+  template<typename ...T, typename ...U> void f_pair_3(pair<T, U>..., tuple<U...>); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 1)}}
+  template<typename ...T> void f_pair_4(pair<T, char>..., T...); // expected-error 0-2{{C++11}} expected-note {{<int, long> vs. <int, long, const char *>}}
+  void g(pair<int, char> a, pair<long, char> b, tuple<char, char> c) {
+    f_pair_1<int, long>(a, b, 0); // expected-error {{no match}}
+    f_pair_2<int, long>(a, b, 0);
+    f_pair_3<int, long>(a, b, c);
+    f_pair_3<int, long>(a, b, tuple<char>()); // expected-error {{no match}}
+    f_pair_4<int, long>(a, b, 0, 0L);
+    f_pair_4<int, long>(a, b, 0, 0L, "foo"); // expected-error {{no match}}
+  }
+}
+
+namespace dr1399 { // dr1399: dup 1388
+  template<typename ...T> void f(T..., int, T...) {} // expected-note {{candidate}} expected-error 0-1{{C++11}}
+  void g() {
+    f(0);
+    f<int>(0, 0, 0);
+    f(0, 0, 0); // expected-error {{no match}}
+  }
+}

Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp?rev=291425&r1=291424&r2=291425&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp Mon Jan  9 01:14:40 2017
@@ -76,14 +76,17 @@ void test_pair_deduction(int *ip, float
   first_arg_pair(make_pair(ip, 17), 16); // expected-error{{no matching function for call to 'first_arg_pair'}}
 }
 
-// For a function parameter pack that does not occur at the end of the
-// parameter-declaration-list, the type of the parameter pack is a
-// non-deduced context.
+// A function parameter pack not at the end of the parameter list is never
+// deduced. We interpret this as meaning the types within it are never
+// deduced, and thus must match explicitly-specified values.
 template<typename ...Types> struct tuple { };
 
 template<typename ...Types>
-void pack_not_at_end(tuple<Types...>, Types... values, int);
+void pack_not_at_end(tuple<Types...>, Types... values, int); // expected-note {{<int *, double *> vs. <>}}
 
 void test_pack_not_at_end(tuple<int*, double*> t2) {
-  pack_not_at_end(t2, 0, 0, 0);
+  pack_not_at_end(t2, 0, 0, 0); // expected-error {{no match}}
+  // FIXME: Should the "original argument type must match deduced parameter
+  // type" rule apply here?
+  pack_not_at_end<int*, double*>(t2, 0, 0, 0); // ok
 }

Modified: cfe/trunk/test/SemaTemplate/deduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/deduction.cpp?rev=291425&r1=291424&r2=291425&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/deduction.cpp (original)
+++ cfe/trunk/test/SemaTemplate/deduction.cpp Mon Jan  9 01:14:40 2017
@@ -350,17 +350,35 @@ namespace deduction_substitution_failure
 }
 
 namespace deduction_after_explicit_pack {
-  template<typename ...T, typename U> int *f(T ...t, int &r, U *u) { // expected-note {{couldn't infer template argument 'U'}}
+  template<typename ...T, typename U> int *f(T ...t, int &r, U *u) {
     return u;
   }
   template<typename U, typename ...T> int *g(T ...t, int &r, U *u) {
     return u;
   }
   void h(float a, double b, int c) {
-    // FIXME: Under DR1388, this appears to be valid.
-    f<float&, double&>(a, b, c, &c); // expected-error {{no matching}}
+    f<float&, double&>(a, b, c, &c); // ok
     g<int, float&, double&>(a, b, c, &c); // ok
   }
+
+  template <typename... T> void i(T..., int, T..., ...); // expected-note 5{{deduced conflicting}}
+  void j() {
+    i(0);
+    i(0, 1); // expected-error {{no match}}
+    i(0, 1, 2); // expected-error {{no match}}
+    i<>(0);
+    i<>(0, 1); // expected-error {{no match}}
+    i<>(0, 1, 2); // expected-error {{no match}}
+    i<int, int>(0, 1, 2, 3, 4);
+    i<int, int>(0, 1, 2, 3, 4, 5); // expected-error {{no match}}
+  }
+
+  // GCC alarmingly accepts this by deducing T={int} by matching the second
+  // parameter against the first argument, then passing the first argument
+  // through the first parameter.
+  template<typename... T> struct X { X(int); operator int(); };
+  template<typename... T> void p(T..., X<T...>, ...); // expected-note {{deduced conflicting}}
+  void q() { p(X<int>(0), 0); } // expected-error {{no match}}
 }
 
 namespace overload_vs_pack {

Modified: cfe/trunk/www/cxx_dr_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_status.html?rev=291425&r1=291424&r2=291425&view=diff
==============================================================================
--- cfe/trunk/www/cxx_dr_status.html (original)
+++ cfe/trunk/www/cxx_dr_status.html Mon Jan  9 01:14:40 2017
@@ -8143,7 +8143,7 @@ and <I>POD class</I></td>
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1388">1388</a></td>
     <td>CD3</td>
     <td>Missing non-deduced context following a function parameter pack</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="svn" align="center">SVN</td>
   </tr>
   <tr id="1389">
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1389">1389</a></td>
@@ -8209,7 +8209,7 @@ and <I>POD class</I></td>
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1399">1399</a></td>
     <td>CD3</td>
     <td>Deduction with multiple function parameter packs</td>
-    <td class="none" align="center">Unknown</td>
+    <td class="svn" align="center">Duplicate of <a href="#1388">1388</a></td>
   </tr>
   <tr id="1400">
     <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1400">1400</a></td>




More information about the cfe-commits mailing list