r336962 - PR38136: improve handling of template argument deduction of non-trailing

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 12 16:32:39 PDT 2018


Author: rsmith
Date: Thu Jul 12 16:32:39 2018
New Revision: 336962

URL: http://llvm.org/viewvc/llvm-project?rev=336962&view=rev
Log:
PR38136: improve handling of template argument deduction of non-trailing
function parameter packs.

This makes our handling of non-trailing function parameter packs
consistent between the case of deduction at the top level in a function
call and other cases where deduction encounters a non-trailing function
parameter pack.

Instead of treating a non-trailing pack and all later parameters as
being non-deduced, we treat a non-trailing pack as exactly matching
any explicitly-specified template arguments (or being an empty pack
if there are no such arguments). This corresponds to the "never deduced"
rule in [temp.deduct.call]p1, but generalized to all deduction contexts.

Non-trailing template argument packs still result in the entire
template argument list being treated as non-deduced, as specified in
[temp.deduct.type]p9.

Modified:
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp
    cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=336962&r1=336961&r2=336962&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Thu Jul 12 16:32:39 2018
@@ -941,12 +941,6 @@ DeduceTemplateArguments(Sema &S,
                         SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                         unsigned TDF,
                         bool PartialOrdering = false) {
-  // Fast-path check to see if we have too many/too few arguments.
-  if (NumParams != NumArgs &&
-      !(NumParams && isa<PackExpansionType>(Params[NumParams - 1])) &&
-      !(NumArgs && isa<PackExpansionType>(Args[NumArgs - 1])))
-    return Sema::TDK_MiscellaneousDeductionFailure;
-
   // C++0x [temp.deduct.type]p10:
   //   Similarly, if P has a form that contains (T), then each parameter type
   //   Pi of the respective parameter-type- list of P is compared with the
@@ -983,13 +977,6 @@ DeduceTemplateArguments(Sema &S,
       continue;
     }
 
-    // C++0x [temp.deduct.type]p5:
-    //   The non-deduced contexts are:
-    //     - A function parameter pack that does not occur at the end of the
-    //       parameter-declaration-clause.
-    if (ParamIdx + 1 < NumParams)
-      return Sema::TDK_Success;
-
     // C++0x [temp.deduct.type]p10:
     //   If the parameter-declaration corresponding to Pi is a function
     //   parameter pack, then the type of its declarator- id is compared with
@@ -1000,15 +987,41 @@ DeduceTemplateArguments(Sema &S,
     QualType Pattern = Expansion->getPattern();
     PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern);
 
-    for (; ArgIdx < NumArgs; ++ArgIdx) {
-      // Deduce template arguments from the pattern.
-      if (Sema::TemplateDeductionResult Result
-            = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern,
-                                                 Args[ArgIdx], Info, Deduced,
-                                                 TDF, PartialOrdering))
-        return Result;
+    if (ParamIdx + 1 == NumParams) {
+      for (; ArgIdx < NumArgs; ++ArgIdx) {
+        // Deduce template arguments from the pattern.
+        if (Sema::TemplateDeductionResult Result
+              = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern,
+                                                   Args[ArgIdx], Info, Deduced,
+                                                   TDF, PartialOrdering))
+          return Result;
 
-      PackScope.nextPackElement();
+        PackScope.nextPackElement();
+      }
+    } else {
+      // C++0x [temp.deduct.type]p5:
+      //   The non-deduced contexts are:
+      //     - A function parameter pack that does not occur at the end of the
+      //       parameter-declaration-clause.
+      //
+      // FIXME: There is no wording to say what we should do in this case. We
+      // choose to resolve this by applying the same rule that is applied for a
+      // function call: that is, deduce all contained packs to their
+      // explicitly-specified values (or to <> if there is no such value).
+      //
+      // This is seemingly-arbitrarily different from the case of a template-id
+      // with a non-trailing pack-expansion in its arguments, which renders the
+      // entire template-argument-list a non-deduced context.
+
+      // 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 = Expansion->getNumExpansions();
+      if (NumExpansions && !PackScope.isPartiallyExpanded()) {
+        for (unsigned I = 0; I != *NumExpansions && ArgIdx < NumArgs;
+             ++I, ++ArgIdx)
+          PackScope.nextPackElement();
+      }
     }
 
     // Build argument packs for each of the parameter packs expanded by this
@@ -2195,10 +2208,6 @@ DeduceTemplateArguments(Sema &S, Templat
     //   template parameter packs expanded by Pi.
     TemplateArgument Pattern = Params[ParamIdx].getPackExpansionPattern();
 
-    // FIXME: If there are no remaining arguments, we can bail out early
-    // and set any deduced parameter packs to an empty argument pack.
-    // The latter part of this is a (minor) correctness issue.
-
     // Prepare to deduce the packs within the pattern.
     PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern);
 
@@ -5280,9 +5289,24 @@ MarkUsedTemplateParameters(ASTContext &C
     const FunctionProtoType *Proto = cast<FunctionProtoType>(T);
     MarkUsedTemplateParameters(Ctx, Proto->getReturnType(), OnlyDeduced, Depth,
                                Used);
-    for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I)
-      MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced,
-                                 Depth, Used);
+    for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I) {
+      // C++17 [temp.deduct.type]p5:
+      //   The non-deduced contexts are: [...]
+      //   -- A function parameter pack that does not occur at the end of the
+      //      parameter-declaration-list.
+      if (!OnlyDeduced || I + 1 == N ||
+          !Proto->getParamType(I)->getAs<PackExpansionType>()) {
+        MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced,
+                                   Depth, Used);
+      } else {
+        // FIXME: C++17 [temp.deduct.call]p1:
+        //   When a function parameter pack appears in a non-deduced context,
+        //   the type of that pack is never deduced.
+        //
+        // We should also track a set of "never deduced" parameters, and
+        // subtract that from the list of deduced parameters after marking.
+      }
+    }
     if (auto *E = Proto->getNoexceptExpr())
       MarkUsedTemplateParameters(Ctx, E, OnlyDeduced, Depth, Used);
     break;

Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp?rev=336962&r1=336961&r2=336962&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp Thu Jul 12 16:32:39 2018
@@ -1,5 +1,4 @@
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
-// expected-no-diagnostics
 
 // FIXME: More bullets to go!
 
@@ -16,8 +15,23 @@ struct has_nondeduced_pack_test<R(FirstT
 
 // - A function parameter pack that does not occur at the end of the
 //   parameter-declaration-clause.
+//
+// We interpret [temp.deduct.call]p1's
+//
+//   "When a function parameter pack appears in a non-deduced context
+//   (12.9.2.5), the type of that pack is never deduced."
+//
+// as applying in all deduction contexts, not just [temp.deduct.call],
+// so we do *not* deduce Types from the second argument here. (More
+// precisely, we deduce it as <> when processing the first argument,
+// and then fail because 'int' doesn't match 'double, int'.)
 int check_nondeduced_pack_test0[
                    has_nondeduced_pack_test<int(float, double, int),
-                                            int(float, double)>::value? 1 : -1];
+                                            int(float, double)>::value? -1 : 1];
 
+template<typename ...T> void has_non_trailing_pack(T ..., int);
+void (*ptr_has_non_trailing_pack)(char, int) = has_non_trailing_pack<char>;
 
+template<typename ...T, typename U> void has_non_trailing_pack_and_more(T ..., U); // expected-note {{failed}}
+void (*ptr_has_non_trailing_pack_and_more_1)(float, double, int) = &has_non_trailing_pack_and_more<float, double>;
+void (*ptr_has_non_trailing_pack_and_more_2)(float, double, int) = &has_non_trailing_pack_and_more<float>; // expected-error {{does not match}}

Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp?rev=336962&r1=336961&r2=336962&view=diff
==============================================================================
--- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp (original)
+++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp Thu Jul 12 16:32:39 2018
@@ -35,6 +35,44 @@ namespace PackExpansionNotAtEnd {
   struct UselessPartialSpec<Types..., Tail>; // expected-error{{class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used}}
 }
 
+// When a pack expansion occurs within a template argument list, the entire
+// list is a non-deduced context. For the corresponding case in a function
+// parameter list, only that parameter is non-deduced.
+//
+// FIXME: It's not clear that this difference is intended, but the wording is
+// explicit.
+namespace PackExpansionNotAtEndFunctionVersusTemplate {
+  template<typename ...T> struct X {};
+  template<typename ...T, typename U> void f1(void(T..., U));
+  // expected-note at +1 {{couldn't infer template argument 'U'}}
+  template<typename ...T, typename U> void f2(X<T..., U>); // FIXME: ill-formed, U is not deducible
+
+  void g(int, int, int);
+  X<int, int, int> h;
+  void test() {
+    // This is deducible: the T... parameter is a non-deduced context, but
+    // that's OK because we don't need to deduce it.
+    f1<int, int>(g);
+    // This is not deducible: the T... parameter renders the entire
+    // template-argument-list a non-deduced context, so U is not deducible.
+    f2<int, int>(h); // expected-error {{no matching function}}
+  }
+
+  template<typename T> struct Y;
+  template<typename ...T, // expected-note {{non-deducible template parameter 'T'}}
+           typename U>
+    struct Y<void(T..., U)> {}; // expected-error {{cannot be deduced}}
+  template<typename ...T, // expected-note {{non-deducible template parameter 'T'}}
+           typename U> // expected-note {{non-deducible template parameter 'U'}}
+    struct Y<X<T..., U>>; // expected-error {{cannot be deduced}}
+  // FIXME: T is not deducible here, due to [temp.deduct.call]p1:
+  //   "When a function parameter pack appears in a non-deduced context,
+  //   the type of that pack is never deduced."
+  template<typename ...T,
+           typename U>
+    struct Y<void(T..., U, T...)> {};
+}
+
 namespace DeduceNonTypeTemplateArgsInArray {
   template<typename ...ArrayTypes>
   struct split_arrays;




More information about the cfe-commits mailing list