When performing template argument deduction to select a partial
authorRichard Smith <richard@metafoo.co.uk>
Wed, 5 May 2021 21:44:49 +0000 (14:44 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Wed, 5 May 2021 21:47:18 +0000 (14:47 -0700)
specialization while substituting a partial template parameter pack,
don't try to extend the existing deduction.

This caused us to select the wrong partial specialization in some rare
cases. A recent change to libc++ caused this to happen in practice for
code using std::conjunction.

clang/lib/Sema/SemaTemplateDeduction.cpp
clang/test/SemaTemplate/partial-spec-instantiate.cpp

index 7d548e4..3c66c8e 100644 (file)
@@ -3079,6 +3079,10 @@ Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial,
       *this, Sema::ExpressionEvaluationContext::Unevaluated);
   SFINAETrap Trap(*this);
 
+  // This deduction has no relation to any outer instantiation we might be
+  // performing.
+  LocalInstantiationScope InstantiationScope(*this);
+
   SmallVector<DeducedTemplateArgument, 4> Deduced;
   Deduced.resize(Partial->getTemplateParameters()->size());
   if (TemplateDeductionResult Result
@@ -3127,6 +3131,10 @@ Sema::DeduceTemplateArguments(VarTemplatePartialSpecializationDecl *Partial,
       *this, Sema::ExpressionEvaluationContext::Unevaluated);
   SFINAETrap Trap(*this);
 
+  // This deduction has no relation to any outer instantiation we might be
+  // performing.
+  LocalInstantiationScope InstantiationScope(*this);
+
   SmallVector<DeducedTemplateArgument, 4> Deduced;
   Deduced.resize(Partial->getTemplateParameters()->size());
   if (TemplateDeductionResult Result = ::DeduceTemplateArguments(
index 3b7cee8..369ff69 100644 (file)
@@ -112,3 +112,25 @@ namespace InstantiationDependent {
   _Static_assert(!A<X>::specialized, "");
   _Static_assert(A<Y>::specialized, "");
 }
+
+namespace IgnorePartialSubstitution {
+  template <typename... T> struct tuple {}; // expected-warning 0-1{{extension}}
+  template <typename> struct IsTuple {
+    enum { value = false };
+  };
+  template <typename... Us> struct IsTuple<tuple<Us...> > { // expected-warning 0-1{{extension}}
+    enum { value = true };
+  };
+
+  template <bool...> using ignore = void; // expected-warning 0-2{{extension}}
+  template <class... Pred> ignore<Pred::value...> helper(); // expected-warning 0-1{{extension}}
+
+  using S = IsTuple<tuple<int> >; // expected-warning 0-1{{extension}}
+
+  // This used to pick the primary template, because we got confused and
+  // thought that template parameter 0 was the current partially-substituted
+  // pack (from `helper`) during the deduction for the partial specialization.
+  void f() { helper<S>(); }
+
+  _Static_assert(S::value, "");
+}