]> granicus.if.org Git - clang/commitdiff
Fix deduction of type of pack-expanded non-type template parameter.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 21 Feb 2017 23:49:18 +0000 (23:49 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 21 Feb 2017 23:49:18 +0000 (23:49 +0000)
We need to look through the PackExpansionType in the parameter type when
deducing, and we need to consider the possibility of deducing arguments for
packs that are not lexically mentioned in the pattern (but are nonetheless
deducible) when figuring out which packs are covered by a pack deduction scope.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@295790 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaTemplateDeduction.cpp
test/SemaCXX/cxx1z-class-template-argument-deduction.cpp
test/SemaTemplate/deduction.cpp

index 32e195b5b9707ba31c29501d2291b758d5143f2f..50d7c23c26c612698d18263c56beb1fefc685c0f 100644 (file)
@@ -112,6 +112,15 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
                         SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                         bool NumberOfArgumentsMustMatch);
 
+static void MarkUsedTemplateParameters(ASTContext &Ctx,
+                                       const TemplateArgument &TemplateArg,
+                                       bool OnlyDeduced, unsigned Depth,
+                                       llvm::SmallBitVector &Used);
+
+static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
+                                       bool OnlyDeduced, unsigned Level,
+                                       llvm::SmallBitVector &Deduced);
+
 /// \brief If the given expression is of a form that permits the deduction
 /// of a non-type template parameter, return the declaration of that
 /// non-type template parameter.
@@ -334,12 +343,24 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument(
   if (!S.getLangOpts().CPlusPlus1z)
     return Sema::TDK_Success;
 
+  if (NTTP->isExpandedParameterPack())
+    // FIXME: We may still need to deduce parts of the type here! But we
+    // don't have any way to find which slice of the type to use, and the
+    // type stored on the NTTP itself is nonsense. Perhaps the type of an
+    // expanded NTTP should be a pack expansion type?
+    return Sema::TDK_Success;
+
+  // Get the type of the parameter for deduction.
+  QualType ParamType = NTTP->getType();
+  if (auto *Expansion = dyn_cast<PackExpansionType>(ParamType))
+    ParamType = Expansion->getPattern();
+
   // FIXME: It's not clear how deduction of a parameter of reference
   // type from an argument (of non-reference type) should be performed.
   // For now, we just remove reference types from both sides and let
   // the final check for matching types sort out the mess.
   return DeduceTemplateArgumentsByTypeMatch(
-      S, TemplateParams, NTTP->getType().getNonReferenceType(),
+      S, TemplateParams, ParamType.getNonReferenceType(),
       ValueType.getNonReferenceType(), Info, Deduced, TDF_SkipNonDependent,
       /*PartialOrdering=*/false,
       /*ArrayBound=*/NewDeduced.wasDeducedFromArrayBound());
@@ -617,29 +638,68 @@ public:
                      SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                      TemplateDeductionInfo &Info, TemplateArgument Pattern)
       : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info) {
+    // Dig out the partially-substituted pack, if there is one.
+    const TemplateArgument *PartialPackArgs = nullptr;
+    unsigned NumPartialPackArgs = 0;
+    std::pair<unsigned, unsigned> PartialPackDepthIndex(-1u, -1u);
+    if (auto *Scope = S.CurrentInstantiationScope)
+      if (auto *Partial = Scope->getPartiallySubstitutedPack(
+              &PartialPackArgs, &NumPartialPackArgs))
+        PartialPackDepthIndex = getDepthAndIndex(Partial);
+
     // Compute the set of template parameter indices that correspond to
     // parameter packs expanded by the pack expansion.
     {
       llvm::SmallBitVector SawIndices(TemplateParams->size());
+
+      auto AddPack = [&](unsigned Index) {
+        if (SawIndices[Index])
+          return;
+        SawIndices[Index] = true;
+
+        // Save the deduced template argument for the parameter pack expanded
+        // by this pack expansion, then clear out the deduction.
+        DeducedPack Pack(Index);
+        Pack.Saved = Deduced[Index];
+        Deduced[Index] = TemplateArgument();
+
+        Packs.push_back(Pack);
+      };
+
+      // First look for unexpanded packs in the pattern.
       SmallVector<UnexpandedParameterPack, 2> Unexpanded;
       S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
       for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
         unsigned Depth, Index;
         std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]);
-        if (Depth == Info.getDeducedDepth() && !SawIndices[Index]) {
-          SawIndices[Index] = true;
-
-          // Save the deduced template argument for the parameter pack expanded
-          // by this pack expansion, then clear out the deduction.
-          DeducedPack Pack(Index);
-          Pack.Saved = Deduced[Index];
-          Deduced[Index] = TemplateArgument();
-
-          Packs.push_back(Pack);
-        }
+        if (Depth == Info.getDeducedDepth())
+          AddPack(Index);
       }
+      assert(!Packs.empty() && "Pack expansion without unexpanded packs?");
+
+      // This pack expansion will have been partially expanded iff the only
+      // unexpanded parameter pack within it is the partially-substituted pack.
+      IsPartiallyExpanded =
+          Packs.size() == 1 &&
+          PartialPackDepthIndex ==
+              std::make_pair(Info.getDeducedDepth(), Packs.front().Index);
+
+      // Skip over the pack elements that were expanded into separate arguments.
+      if (IsPartiallyExpanded)
+        PackElements += NumPartialPackArgs;
+
+      // We can also have deduced template parameters that do not actually
+      // appear in the pattern, but can be deduced by it (the type of a non-type
+      // template parameter pack, in particular). These won't have prevented us
+      // from partially expanding the pack.
+      llvm::SmallBitVector Used(TemplateParams->size());
+      MarkUsedTemplateParameters(S.Context, Pattern, /*OnlyDeduced*/true,
+                                 Info.getDeducedDepth(), Used);
+      for (int Index = Used.find_first(); Index != -1;
+           Index = Used.find_next(Index))
+        if (TemplateParams->getParam(Index)->isParameterPack())
+          AddPack(Index);
     }
-    assert(!Packs.empty() && "Pack expansion without unexpanded packs?");
 
     for (auto &Pack : Packs) {
       if (Info.PendingDeducedPacks.size() > Pack.Index)
@@ -648,18 +708,19 @@ public:
         Info.PendingDeducedPacks.resize(Pack.Index + 1);
       Info.PendingDeducedPacks[Pack.Index] = &Pack;
 
-      if (S.CurrentInstantiationScope) {
-        // If the template argument pack was explicitly specified, add that to
-        // the set of deduced arguments.
-        const TemplateArgument *ExplicitArgs;
-        unsigned NumExplicitArgs;
-        NamedDecl *PartiallySubstitutedPack =
-            S.CurrentInstantiationScope->getPartiallySubstitutedPack(
-                &ExplicitArgs, &NumExplicitArgs);
-        if (PartiallySubstitutedPack &&
-            getDepthAndIndex(PartiallySubstitutedPack) ==
-                std::make_pair(Info.getDeducedDepth(), Pack.Index))
-          Pack.New.append(ExplicitArgs, ExplicitArgs + NumExplicitArgs);
+      if (PartialPackDepthIndex ==
+            std::make_pair(Info.getDeducedDepth(), Pack.Index)) {
+        Pack.New.append(PartialPackArgs, PartialPackArgs + NumPartialPackArgs);
+        // We pre-populate the deduced value of the partially-substituted
+        // pack with the specified value. This is not entirely correct: the
+        // value is supposed to have been substituted, not deduced, but the
+        // cases where this is observable require an exact type match anyway.
+        //
+        // FIXME: If we could represent a "depth i, index j, pack elem k"
+        // parameter, we could substitute the partially-substituted pack
+        // everywhere and avoid this.
+        if (Pack.New.size() > PackElements)
+          Deduced[Pack.Index] = Pack.New[PackElements];
       }
     }
   }
@@ -671,16 +732,7 @@ public:
 
   /// 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);
-  }
+  bool isPartiallyExpanded() { return IsPartiallyExpanded; }
 
   /// Move to deducing the next element in each pack that is being deduced.
   void nextPackElement() {
@@ -692,8 +744,13 @@ public:
       if (!Pack.New.empty() || !DeducedArg.isNull()) {
         while (Pack.New.size() < PackElements)
           Pack.New.push_back(DeducedTemplateArgument());
-        Pack.New.push_back(DeducedArg);
-        DeducedArg = DeducedTemplateArgument();
+        if (Pack.New.size() == PackElements)
+          Pack.New.push_back(DeducedArg);
+        else
+          Pack.New[PackElements] = DeducedArg;
+        DeducedArg = Pack.New.size() > PackElements + 1
+                         ? Pack.New[PackElements + 1]
+                         : DeducedTemplateArgument();
       }
     }
     ++PackElements;
@@ -784,6 +841,7 @@ private:
   SmallVectorImpl<DeducedTemplateArgument> &Deduced;
   TemplateDeductionInfo &Info;
   unsigned PackElements = 0;
+  bool IsPartiallyExpanded = false;
 
   SmallVector<DeducedPack, 2> Packs;
 };
@@ -2591,12 +2649,6 @@ static bool isSimpleTemplateIdType(QualType T) {
   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].
 ///
@@ -4894,13 +4946,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
   return isAtLeastAsSpecializedAs(*this, PType, AType, AArg, Info);
 }
 
-static void
-MarkUsedTemplateParameters(ASTContext &Ctx,
-                           const TemplateArgument &TemplateArg,
-                           bool OnlyDeduced,
-                           unsigned Depth,
-                           llvm::SmallBitVector &Used);
-
 /// \brief Mark the template parameters that are used by the given
 /// expression.
 static void
index 750a0892917870a6d5b8a704210c8b3c99e83970..5eee34e8097eecb94c3237adcf992c9ffb637968 100644 (file)
@@ -203,13 +203,19 @@ namespace transform_params {
   };
   B b({1, 2, 3}, "foo", {'x', 'y', 'z', 'w'}); // ok
 
+  // This should be accepted once -std=c++1z implies
+  // -frelaxed-template-template-args. Without that, a template template
+  // parameter 'template<int, int, int> typename' cannot bind to a template
+  // template argument 'template<int...> typename'.
   template<typename ...T> struct C { // expected-note {{candidate}}
     template<T ...V, template<T...> typename X>
-      C(X<V...>); // expected-note {{substitution failure [with T = <>, V = <0, 1, 2>]}}
+      C(X<V...>); // expected-note {{substitution failure [with T = <int, int, int>, V = <0, 1, 2>]}}
   };
   template<int...> struct Y {};
-  // FIXME: This incorrectly deduces T = <>, rather than deducing
-  // T = <int, int, int> from the types of the elements of V.
-  // (This failure is not related to class template argument deduction.)
   C c(Y<0, 1, 2>{}); // expected-error {{no viable constructor or deduction guide}}
+
+  template<typename ...T> struct D {
+    template<T ...V> D(Y<V...>);
+  };
+  D d(Y<0, 1, 2>{});
 }
index 216b8347be77cf1b061a6473600cc1c24fba435c..8ac65aacd5b4e013633c42231f8c00c63e6780b4 100644 (file)
@@ -526,13 +526,17 @@ namespace dependent_list_deduction {
     static_assert(is_same<X<T...>, X<int, char, char>>::value, "");
     static_assert(is_same<Y<V...>, Y<3, 2, 4>>::value, "");
   }
-  template<typename ...T, T ...V> void g(const T (&...p)[V]) { // expected-note {{deduced incomplete pack}}
+  template<typename ...T, T ...V> void g(const T (&...p)[V]) {
     static_assert(is_same<X<T...>, X<int, decltype(sizeof(0))>>::value, "");
     static_assert(is_same<Y<V...>, Y<2, 3>>::value, "");
   }
   void h() {
     f({1, 2, 3}, {'a', 'b'}, "foo");
-    // FIXME: Deduction in this case should succeed.
-    g({1, 2}, {{}, {}, {}}); // expected-error {{no match}}
+    g({1, 2}, {{}, {}, {}});
+#if __cplusplus <= 201402
+    // expected-error@-2 {{no match}}
+    // expected-note@-9 {{deduced incomplete pack}}
+    // We deduce V$1 = (size_t)3, which in C++1z also deduces T$1 = size_t.
+#endif
   }
 }