ychen updated this revision to Diff 465549.
ychen added a comment.

- rebased on D135088 <https://reviews.llvm.org/D135088>


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128750/new/

https://reviews.llvm.org/D128750

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/AST/DeclTemplate.h
  clang/include/clang/Sema/Sema.h
  clang/include/clang/Sema/SemaConcept.h
  clang/lib/AST/DeclTemplate.cpp
  clang/lib/Sema/SemaConcept.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/SemaTemplate.cpp
  clang/lib/Sema/SemaTemplateDeduction.cpp
  clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
  clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -963,7 +963,7 @@
       </tr>
       <tr>
         <td><a href="https://wg21.link/p2113r0";>P2113R0</a></td>
-        <td rowspan="1" class="none" align="center">No</td>
+        <td rowspan="1" class="unreleased" align="center">Clang 16</td>
       </tr>
     <!-- Albuquerque papers -->
     <tr>
Index: clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
===================================================================
--- clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
+++ clang/test/CXX/temp/temp.decls/temp.fct/temp.func.order/p6.cpp
@@ -1,10 +1,18 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
 
-struct A;
-struct B;
-
 template <typename> constexpr bool True = true;
 template <typename T> concept C = True<T>;
+template <typename T> concept D = C<T> && sizeof(T) > 2;
+template <typename T> concept E = D<T> && alignof(T) > 1;
+
+struct A {};
+template <typename, auto, int, A, typename...> struct S {};
+template <typename, auto, int, A, auto...> struct S2 {};
+template <typename T, typename U> struct X {};
+
+namespace p6 {
+
+struct B;
 
 void f(C auto &, auto &) = delete;
 template <C Q> void f(Q &, C auto &);
@@ -13,14 +21,120 @@
   f(*ap, *bp);
 }
 
-template <typename T, typename U> struct X {};
-
+#if 0
+// FIXME: [temp.func.order]p6.2.1 is not implemented, matching GCC.
 template <typename T, C U, typename V> bool operator==(X<T, U>, V) = delete;
 template <C T, C U, C V>               bool operator==(T, X<U, V>);
 
 bool h() {
   return X<void *, int>{} == 0;
 }
+#endif
+
+template<C T, C auto M, int W, A S,
+         template<typename, auto, int, A, typename...> class U,
+         typename... Z>
+void foo(T, U<T, M, W, S, Z...>) = delete;
+template<C T, D auto M, int W, A S,
+         template<typename, auto, int, A, typename...> class U,
+         typename... Z>
+void foo(T, U<T, M, W, S, Z...>) = delete;
+template<C T, E auto M, int W, A S,
+         template<typename, auto, int, A, typename...> class U,
+         typename... Z>
+void foo(T, U<T, M, W, S, Z...>);
+
+// check auto template parameter pack.
+template<C T, auto M, int W, A S,
+         template<typename, auto, int, A, auto...> class U,
+         C auto... Z>
+void foo2(T, U<T, M, W, S, Z...>) = delete;
+template<C T, auto M, int W, A S,
+         template<typename, auto, int, A, auto...> class U,
+         D auto... Z>
+void foo2(T, U<T, M, W, S, Z...>) = delete;
+template<C T, auto M, int W, A S,
+         template<typename, auto, int, A, auto...> class U,
+         E auto... Z>
+void foo2(T, U<T, M, W, S, Z...>);
+
+void bar(S<int, 1, 1, A{}, int> s, S2<int, 1, 1, A{}, 0, 0u> s2) {
+  foo(0, s);
+  foo2(0, s2);
+}
+
+template<C auto... T> void bar2();
+template<D auto... T> void bar2() = delete;
+
+} // namespace p6
+
+namespace TestConversionFunction {
+struct Y {
+  template<C        T, typename U> operator X<T, U>(); // expected-note {{candidate function [with T = int, U = int]}}
+  template<typename T, typename U> operator X<U, T>(); // expected-note {{candidate function [with T = int, U = int]}}
+};
+
+X<int,int> f() {
+  return Y{}; // expected-error {{conversion from 'Y' to 'X<int, int>' is ambiguous}}
+}
+}
+
+namespace ClassPartialSpecPartialOrdering {
+template<D T> struct Y { Y()=delete; }; // expected-note {{template is declared here}}
+template<C T> struct Y<T> {}; // expected-error {{class template partial specialization is not more specialized than the primary template}}
+
+template<C T> struct B    { B()=delete; };
+template<D T> struct B<T> { B()=delete; };
+template<E T> struct B<T> {};
+
+template<C T, C auto M, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z>
+struct Some { Some()=delete; };
+template<C T, D auto M, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z>
+struct Some<T, M, W, S, U, Z...> { Some()=delete; };
+template<C T, E auto M, int W, A S, template<typename, auto, int, A, typename...> class U, typename... Z>
+struct Some<T, M, W, S, U, Z...> {};
+
+void f() {
+  B<int> b;
+  Some<int, 1, 1, A{}, S, int> c;
+}
+
+template<C T, C V> struct Z; // expected-note {{template is declared here}}
+template<D T, C V> struct Z<V, T>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
+
+template<C auto T> struct W1;
+template<D auto T> struct W1<T> {};
+
+template<C auto... T> struct W2;
+template<D auto... T> struct W2<T...> {};
+
+template<class T, class U>
+concept C1 = C<T> && C<U>;
+template<class T, class U>
+concept D1 = D<T> && C<U>;
+
+template<C1<A> auto T> struct W3;
+template<D1<A> auto T> struct W3<T> {};
+
+template<C1<A> auto... T> struct W4;
+template<D1<A> auto... T> struct W4<T...> {};
+
+// FIXME: enable once Clang support non-trivial auto on NTTP.
+// template<C auto* T> struct W5;
+// template<D auto* T> struct W5<T> {};
+
+// FIXME: enable once Clang support non-trivial auto on NTTP.
+// template<C auto& T> struct W6;
+// template<D auto& T> struct W6<T> {};
+
+struct W1<0> w1;
+struct W2<0> w2;
+struct W3<0> w3;
+struct W4<0> w4;
+// FIXME: enable once Clang support non-trivial auto on NTTP.
+// struct W5<(int*)nullptr> w5;
+// struct W6<w5> w6;
+}
 
 namespace PR53640 {
 
Index: clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
===================================================================
--- clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
+++ clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp
@@ -98,26 +98,31 @@
   static_assert(is_same_v<decltype(bar<int>()), void>); // expected-error {{call to 'bar' is ambiguous}}
 
   template<typename T>
-  constexpr int goo(int a) requires AtLeast2<int> && true {
+  constexpr int goo(int a) requires AtLeast2<int> && true { // expected-note {{candidate function}}
     return 1;
   }
 
   template<typename T>
-  constexpr int goo(const int b) requires AtLeast2<int> {
+  constexpr int goo(const int b) requires AtLeast2<int> { // expected-note {{candidate function}}
     return 2;
   }
 
-  // Only trailing requires clauses of redeclarations are compared for overload resolution.
+  // [temp.func.order] p5
+  //   Since, in a call context, such type deduction considers only parameters
+  //   for which there are explicit call arguments, some parameters are ignored
+  //   (namely, function parameter packs, parameters with default arguments, and
+  //   ellipsis parameters).
   template<typename T>
-  constexpr int doo(int a, ...) requires AtLeast2<int> && true { // expected-note {{candidate function}}
+  constexpr int doo(int a, ...) requires AtLeast2<int> && true {
     return 1;
   }
 
   template<typename T>
-  constexpr int doo(int b) requires AtLeast2<int> { // expected-note {{candidate function}}
+  constexpr int doo(int b) requires AtLeast2<int> {
     return 2;
   }
 
-  static_assert(goo<int>(1) == 1);
-  static_assert(doo<int>(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
+  // By temp.func.order-6.2.2, this is ambiguous because parameter a and b have different types.
+  static_assert(goo<int>(1) == 1); // expected-error {{call to 'goo' is ambiguous}}
+  static_assert(doo<int>(2) == 1);
 }
Index: clang/lib/Sema/SemaTemplateDeduction.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateDeduction.cpp
+++ clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5167,43 +5167,26 @@
 /// candidate with a reversed parameter order. In this case, the corresponding
 /// P/A pairs between FT1 and FT2 are reversed.
 ///
-/// \param AllowOrderingByConstraints If \c is false, don't check whether one
-/// of the templates is more constrained than the other. Default is true.
-///
 /// \returns the more specialized function template. If neither
 /// template is more specialized, returns NULL.
 FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
     TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-    unsigned NumCallArguments2, bool Reversed,
-    bool AllowOrderingByConstraints) {
-
-  auto JudgeByConstraints = [&]() -> FunctionTemplateDecl * {
-    if (!AllowOrderingByConstraints)
-      return nullptr;
-    llvm::SmallVector<const Expr *, 3> AC1, AC2;
-    FT1->getAssociatedConstraints(AC1);
-    FT2->getAssociatedConstraints(AC2);
-    bool AtLeastAsConstrained1, AtLeastAsConstrained2;
-    if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1))
-      return nullptr;
-    if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2))
-      return nullptr;
-    if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
-      return nullptr;
-    return AtLeastAsConstrained1 ? FT1 : FT2;
-  };
+    unsigned NumCallArguments2, bool Reversed) {
 
   bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
                                           NumCallArguments1, Reversed);
   bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
                                           NumCallArguments2, Reversed);
 
+  // C++ [temp.deduct.partial]p10:
+  //   F is more specialized than G if F is at least as specialized as G and G
+  //   is not at least as specialized as F.
   if (Better1 != Better2) // We have a clear winner
     return Better1 ? FT1 : FT2;
 
   if (!Better1 && !Better2) // Neither is better than the other
-    return JudgeByConstraints();
+    return nullptr;
 
   // C++ [temp.deduct.partial]p11:
   //   ... and if G has a trailing function parameter pack for which F does not
@@ -5255,7 +5238,61 @@
     }
   }
 
-  return JudgeByConstraints();
+  if (!Context.getLangOpts().CPlusPlus20 || isa<CXXDeductionGuideDecl>(FD1) ||
+      isa<CXXDeductionGuideDecl>(FD2))
+    return nullptr;
+
+  // Match GCC on not implementing [temp.func.order]p6.2.1.
+
+  // C++20 [temp.func.order]p6:
+  //   If deduction against the other template succeeds for both transformed
+  //   templates, constraints can be considered as follows:
+
+  // C++20 [temp.func.order]p6.1:
+  //   If their template-parameter-lists (possibly including template-parameters
+  //   invented for an abbreviated function template ([dcl.fct])) or function
+  //   parameter lists differ in length, neither template is more specialized
+  //   than the other.
+  TemplateParameterList *TPL1 = FT1->getTemplateParameters();
+  TemplateParameterList *TPL2 = FT2->getTemplateParameters();
+  if (TPL1->size() != TPL2->size() || NumParams1 != NumParams2)
+    return nullptr;
+
+  // C++20 [temp.func.order]p6.2.2:
+  //   Otherwise, if the corresponding template-parameters of the
+  //   template-parameter-lists are not equivalent ([temp.over.link]) or if the
+  //   function parameters that positionally correspond between the two
+  //   templates are not of the same type, neither template is more specialized
+  //   than the other.
+  if (!TemplateParameterListsAreEqual(
+          TPL1, TPL2, false, Sema::TPL_TemplateMatch, SourceLocation(), true))
+    return nullptr;
+
+  for (unsigned i = 0; i < NumParams1; ++i)
+    if (!Context.hasSameType(FD1->getParamDecl(i)->getType(),
+                             FD2->getParamDecl(i)->getType()))
+      return nullptr;
+
+  // C++20 [temp.func.order]p6.3:
+  //   Otherwise, if the context in which the partial ordering is done is
+  //   that of a call to a conversion function and the return types of the
+  //   templates are not the same, then neither template is more specialized
+  //   than the other.
+  if (TPOC == TPOC_Conversion &&
+      !Context.hasSameType(FD1->getReturnType(), FD2->getReturnType()))
+    return nullptr;
+
+  llvm::SmallVector<const Expr *, 3> AC1, AC2;
+  FT1->getAssociatedConstraints(AC1);
+  FT2->getAssociatedConstraints(AC2);
+  bool AtLeastAsConstrained1, AtLeastAsConstrained2;
+  if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1))
+    return nullptr;
+  if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2))
+    return nullptr;
+  if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
+    return nullptr;
+  return AtLeastAsConstrained1 ? FT1 : FT2;
 }
 
 /// Determine if the two templates are equivalent.
@@ -5433,7 +5470,7 @@
 }
 
 namespace {
-// A dummy pass to return nullptr instead of P2 when performing "more
+// A dummy class to return nullptr instead of P2 when performing "more
 // specialized than primary" check.
 struct GetP2 {
   template <typename T1, typename T2,
@@ -5447,6 +5484,60 @@
     return nullptr;
   }
 };
+
+// The assumption is that two template argument lists have the same size.
+struct TemplateArgumentListAreEqual {
+  ASTContext &Ctx;
+  TemplateArgumentListAreEqual(ASTContext &Ctx) : Ctx(Ctx) {}
+
+  bool hasEqualTemplateArgumentList(llvm::ArrayRef<TemplateArgumentLoc> P1,
+                                    llvm::ArrayRef<TemplateArgumentLoc> P2) {
+    if (P1.size() != P2.size())
+      return false;
+
+    for (unsigned I = 0, S = P1.size(); I < S; ++I) {
+      const TemplateArgument &ArgA = P1[I].getArgument();
+      const TemplateArgument &ArgB = P2[I].getArgument();
+
+      llvm::FoldingSetNodeID IDA, IDB;
+      Ctx.getCanonicalTemplateArgument(ArgA).Profile(IDA, Ctx);
+      Ctx.getCanonicalTemplateArgument(ArgB).Profile(IDB, Ctx);
+      if (IDA != IDB)
+        return false;
+    }
+    return true;
+  }
+
+  template <typename T1, typename T2,
+            std::enable_if_t<std::is_same<T1, T2>::value, bool> = true>
+  bool operator()(T1 *PS1, T2 *PS2) {
+    return hasEqualTemplateArgumentList(
+        PS1->getTemplateArgsAsWritten()->arguments(),
+        PS2->getTemplateArgsAsWritten()->arguments());
+  }
+
+  template <typename T1, typename T2,
+            std::enable_if_t<!std::is_same<T1, T2>::value, bool> = true>
+  bool operator()(T1 *Spec, T2 *Primary) {
+    SmallVector<TemplateArgumentLoc, 16> Args;
+    for (const TemplateArgument &TA : Primary->getInjectedTemplateArgs()) {
+      // A injected template argument for a parameter pack is
+      // TemplateArgument::Pack. However, the TemplateArgsAsWritten
+      // represents a template argument for a parameter pack as
+      // TemplateArgument::Type with PackExpansionType. Transform the former
+      // to the same TemplateArgument kind as the latter.
+      if (TA.getKind() == TemplateArgument::Pack) {
+        assert(TA.pack_size() == 1);
+        Args.emplace_back(*TA.pack_begin(), TemplateArgumentLocInfo());
+      } else {
+        Args.emplace_back(TA, TemplateArgumentLocInfo());
+      }
+    }
+
+    return hasEqualTemplateArgumentList(
+        Args, Spec->getTemplateArgsAsWritten()->arguments());
+  }
+};
 } // namespace
 
 /// Returns the more specialized template specialization between T1/P1 and
@@ -5486,52 +5577,81 @@
   if (IsMoreSpecialThanPrimaryCheck && !Better2)
     return P1;
 
+  // C++ [temp.deduct.partial]p10:
+  //   F is more specialized than G if F is at least as specialized as G and G
+  //   is not at least as specialized as F.
+  if (Better1 != Better2) // We have a clear winner
+    return Better1 ? P1 : GetP2()(P1, P2);
+
   if (!Better1 && !Better2)
     return nullptr;
 
-  if (Better1 && Better2) {
-    // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
-    // there is no wording or even resolution for this issue.
-    bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <=
-                            LangOptions::ClangABI::Ver15;
-    if (!ClangABICompat15) {
-      auto *TST1 = cast<TemplateSpecializationType>(T1);
-      auto *TST2 = cast<TemplateSpecializationType>(T2);
-      const TemplateArgument &TA1 = TST1->template_arguments().back();
-      if (TA1.getKind() == TemplateArgument::Pack) {
-        assert(TST1->getNumArgs() == TST2->getNumArgs());
-        const TemplateArgument &TA2 = TST2->template_arguments().back();
-        assert(TA2.getKind() == TemplateArgument::Pack);
-        unsigned PackSize1 = TA1.pack_size();
-        unsigned PackSize2 = TA2.pack_size();
-        bool IsPackExpansion1 =
-            PackSize1 && TA1.pack_elements().back().isPackExpansion();
-        bool IsPackExpansion2 =
-            PackSize2 && TA2.pack_elements().back().isPackExpansion();
-        if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
-          if (PackSize1 > PackSize2 && IsPackExpansion1)
-            return GetP2()(P1, P2);
-          if (PackSize1 < PackSize2 && IsPackExpansion2)
-            return P1;
-        }
+  // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
+  // there is no wording or even resolution for this issue.
+  bool ClangABICompat15 = S.Context.getLangOpts().getClangABICompat() <=
+                          LangOptions::ClangABI::Ver15;
+  if (!ClangABICompat15) {
+    auto *TST1 = cast<TemplateSpecializationType>(T1);
+    auto *TST2 = cast<TemplateSpecializationType>(T2);
+    const TemplateArgument &TA1 = TST1->template_arguments().back();
+    if (TA1.getKind() == TemplateArgument::Pack) {
+      assert(TST1->getNumArgs() == TST2->getNumArgs());
+      const TemplateArgument &TA2 = TST2->template_arguments().back();
+      assert(TA2.getKind() == TemplateArgument::Pack);
+      unsigned PackSize1 = TA1.pack_size();
+      unsigned PackSize2 = TA2.pack_size();
+      bool IsPackExpansion1 =
+          PackSize1 && TA1.pack_elements().back().isPackExpansion();
+      bool IsPackExpansion2 =
+          PackSize2 && TA2.pack_elements().back().isPackExpansion();
+      if (PackSize1 != PackSize2 && IsPackExpansion1 != IsPackExpansion2) {
+        if (PackSize1 > PackSize2 && IsPackExpansion1)
+          return GetP2()(P1, P2);
+        if (PackSize1 < PackSize2 && IsPackExpansion2)
+          return P1;
       }
     }
-
-    llvm::SmallVector<const Expr *, 3> AC1, AC2;
-    P1->getAssociatedConstraints(AC1);
-    P2->getAssociatedConstraints(AC2);
-    bool AtLeastAsConstrained1, AtLeastAsConstrained2;
-    if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) ||
-        (IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1))
-      return nullptr;
-    if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2))
-      return nullptr;
-    if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
-      return nullptr;
-    return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2);
   }
 
-  return Better1 ? P1 : GetP2()(P1, P2);
+  if (!S.Context.getLangOpts().CPlusPlus20)
+    return nullptr;
+
+  // Match GCC on not implementing [temp.func.order]p6.2.1.
+
+  // C++20 [temp.func.order]p6:
+  //   If deduction against the other template succeeds for both transformed
+  //   templates, constraints can be considered as follows:
+
+  TemplateParameterList *TPL1 = P1->getTemplateParameters();
+  TemplateParameterList *TPL2 = P2->getTemplateParameters();
+  if (TPL1->size() != TPL2->size())
+    return nullptr;
+
+  // C++20 [temp.func.order]p6.2.2:
+  // Otherwise, if the corresponding template-parameters of the
+  // template-parameter-lists are not equivalent ([temp.over.link]) or if the
+  // function parameters that positionally correspond between the two
+  // templates are not of the same type, neither template is more specialized
+  // than the other.
+  if (!S.TemplateParameterListsAreEqual(
+          TPL1, TPL2, false, Sema::TPL_TemplateMatch, SourceLocation(), true))
+    return nullptr;
+
+  if (!TemplateArgumentListAreEqual(S.getASTContext())(P1, P2))
+    return nullptr;
+
+  llvm::SmallVector<const Expr *, 3> AC1, AC2;
+  P1->getAssociatedConstraints(AC1);
+  P2->getAssociatedConstraints(AC2);
+  bool AtLeastAsConstrained1, AtLeastAsConstrained2;
+  if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) ||
+      (IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1))
+    return nullptr;
+  if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2))
+    return nullptr;
+  if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
+    return nullptr;
+  return AtLeastAsConstrained1 ? P1 : GetP2()(P1, P2);
 }
 
 /// Returns the more specialized class template partial specialization
@@ -5591,17 +5711,11 @@
 
 bool Sema::isMoreSpecializedThanPrimary(
     VarTemplatePartialSpecializationDecl *Spec, TemplateDeductionInfo &Info) {
-  TemplateDecl *Primary = Spec->getSpecializedTemplate();
-  // FIXME: Cache the injected template arguments rather than recomputing
-  // them for each partial specialization.
-  SmallVector<TemplateArgument, 8> PrimaryArgs;
-  Context.getInjectedTemplateArgs(Primary->getTemplateParameters(),
-                                  PrimaryArgs);
-
+  VarTemplateDecl *Primary = Spec->getSpecializedTemplate();
   TemplateName CanonTemplate =
       Context.getCanonicalTemplateName(TemplateName(Primary));
   QualType PrimaryT = Context.getTemplateSpecializationType(
-      CanonTemplate, PrimaryArgs);
+      CanonTemplate, Primary->getInjectedTemplateArgs());
   QualType PartialT = Context.getTemplateSpecializationType(
       CanonTemplate, Spec->getTemplateArgs().asArray());
 
Index: clang/lib/Sema/SemaTemplate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplate.cpp
+++ clang/lib/Sema/SemaTemplate.cpp
@@ -7641,10 +7641,10 @@
 
     if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
                                                           Arg.getLocation())) {
-      // C++2a[temp.func.order]p2
+      // P2113
+      // C++20[temp.func.order]p2
       //   [...] If both deductions succeed, the partial ordering selects the
-      //   more constrained template as described by the rules in
-      //   [temp.constr.order].
+      // more constrained template (if one exists) as determined below.
       SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
       Params->getAssociatedConstraints(ParamsAC);
       // C++2a[temp.arg.template]p3
@@ -7853,7 +7853,8 @@
 static bool MatchTemplateParameterKind(
     Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old,
     const NamedDecl *OldInstFrom, bool Complain,
-    Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
+    Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
+    bool PartialOrdering) {
   // Check the actual kind (type, non-type, template).
   if (Old->getKind() != New->getKind()) {
     if (Complain) {
@@ -7941,11 +7942,11 @@
             (Kind == Sema::TPL_TemplateMatch
                  ? Sema::TPL_TemplateTemplateParmMatch
                  : Kind),
-            TemplateArgLoc))
+            TemplateArgLoc, PartialOrdering))
       return false;
   }
 
-  if (Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
+  if (!PartialOrdering && Kind != Sema::TPL_TemplateTemplateArgumentMatch &&
       !isa<TemplateTemplateParmDecl>(Old)) {
     const Expr *NewC = nullptr, *OldC = nullptr;
 
@@ -8038,7 +8039,8 @@
 bool Sema::TemplateParameterListsAreEqual(
     const NamedDecl *NewInstFrom, TemplateParameterList *New,
     const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
-    TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) {
+    TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc,
+    bool PartialOrdering) {
   if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) {
     if (Complain)
       DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind,
@@ -8070,7 +8072,7 @@
 
       if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
                                       OldInstFrom, Complain, Kind,
-                                      TemplateArgLoc))
+                                      TemplateArgLoc, PartialOrdering))
         return false;
 
       ++NewParm;
@@ -8087,7 +8089,7 @@
     for (; NewParm != NewParmEnd; ++NewParm) {
       if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm,
                                       OldInstFrom, Complain, Kind,
-                                      TemplateArgLoc))
+                                      TemplateArgLoc, PartialOrdering))
         return false;
     }
   }
@@ -8101,7 +8103,7 @@
     return false;
   }
 
-  if (Kind != TPL_TemplateTemplateArgumentMatch) {
+  if (!PartialOrdering && Kind != TPL_TemplateTemplateArgumentMatch) {
     const Expr *NewRC = New->getRequiresClause();
     const Expr *OldRC = Old->getRequiresClause();
 
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -9690,19 +9690,13 @@
 
 /// We're allowed to use constraints partial ordering only if the candidates
 /// have the same parameter types:
-/// [temp.func.order]p6.2.2 [...] or if the function parameters that
-/// positionally correspond between the two templates are not of the same type,
-/// neither template is more specialized than the other.
 /// [over.match.best]p2.6
 /// F1 and F2 are non-template functions with the same parameter-type-lists,
 /// and F1 is more constrained than F2 [...]
-static bool canCompareFunctionConstraints(Sema &S,
+static bool sameFunctionParameterTypeLists(Sema &S,
                                           const OverloadCandidate &Cand1,
                                           const OverloadCandidate &Cand2) {
-  // FIXME: Per P2113R0 we also need to compare the template parameter lists
-  // when comparing template functions. 
-  if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() &&
-      Cand2.Function->hasPrototype()) {
+  if (Cand1.Function && Cand2.Function) {
     auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
     auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType());
     if (PT1->getNumParams() == PT2->getNumParams() &&
@@ -9945,15 +9939,14 @@
             isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
                                                    : TPOC_Call,
             Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
-            Cand1.isReversed() ^ Cand2.isReversed(),
-            canCompareFunctionConstraints(S, Cand1, Cand2)))
+            Cand1.isReversed() ^ Cand2.isReversed()))
       return BetterTemplate == Cand1.Function->getPrimaryTemplate();
   }
 
   //   -— F1 and F2 are non-template functions with the same
   //      parameter-type-lists, and F1 is more constrained than F2 [...],
   if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
-      canCompareFunctionConstraints(S, Cand1, Cand2)) {
+      sameFunctionParameterTypeLists(S, Cand1, Cand2)) {
     Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
     Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
     if (RC1 && RC2) {
Index: clang/lib/Sema/SemaConcept.cpp
===================================================================
--- clang/lib/Sema/SemaConcept.cpp
+++ clang/lib/Sema/SemaConcept.cpp
@@ -1103,6 +1103,18 @@
   // - The normal form of an expression (E) is the normal form of E.
   // [...]
   E = E->IgnoreParenImpCasts();
+
+  // C++2a [temp.param]p4:
+  //     [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
+  //
+  // Using the pattern suffices because the partial ordering rules guarantee
+  // the template paramaters are equivalent.
+  if (auto *FoldE = dyn_cast<const CXXFoldExpr>(E)) {
+    assert(FoldE->isRightFold() && FoldE->getOperator() == BO_LAnd);
+    assert(E->IgnoreParenImpCasts() == E);
+    E = FoldE->getPattern();
+  }
+
   if (LogicalBinOp BO = E) {
     auto LHS = fromConstraintExpr(S, D, BO.getLHS());
     if (!LHS)
Index: clang/lib/AST/DeclTemplate.cpp
===================================================================
--- clang/lib/AST/DeclTemplate.cpp
+++ clang/lib/AST/DeclTemplate.cpp
@@ -353,6 +353,22 @@
                                       SETraits::getDecl(Entry));
 }
 
+ArrayRef<TemplateArgument> RedeclarableTemplateDecl::getInjectedTemplateArgs() {
+  TemplateParameterList *Params = getTemplateParameters();
+  auto *CommonPtr = getCommonPtr();
+  if (!CommonPtr->InjectedArgs) {
+    auto &Context = getASTContext();
+    SmallVector<TemplateArgument, 16> TemplateArgs;
+    Context.getInjectedTemplateArgs(Params, TemplateArgs);
+    CommonPtr->InjectedArgs =
+        new (Context) TemplateArgument[TemplateArgs.size()];
+    std::copy(TemplateArgs.begin(), TemplateArgs.end(),
+              CommonPtr->InjectedArgs);
+  }
+
+  return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size());
+}
+
 //===----------------------------------------------------------------------===//
 // FunctionTemplateDecl Implementation
 //===----------------------------------------------------------------------===//
@@ -403,22 +419,6 @@
                                               InsertPos);
 }
 
-ArrayRef<TemplateArgument> FunctionTemplateDecl::getInjectedTemplateArgs() {
-  TemplateParameterList *Params = getTemplateParameters();
-  Common *CommonPtr = getCommonPtr();
-  if (!CommonPtr->InjectedArgs) {
-    auto &Context = getASTContext();
-    SmallVector<TemplateArgument, 16> TemplateArgs;
-    Context.getInjectedTemplateArgs(Params, TemplateArgs);
-    CommonPtr->InjectedArgs =
-        new (Context) TemplateArgument[TemplateArgs.size()];
-    std::copy(TemplateArgs.begin(), TemplateArgs.end(),
-              CommonPtr->InjectedArgs);
-  }
-
-  return llvm::makeArrayRef(CommonPtr->InjectedArgs, Params->size());
-}
-
 void FunctionTemplateDecl::mergePrevDecl(FunctionTemplateDecl *Prev) {
   using Base = RedeclarableTemplateDecl;
 
Index: clang/include/clang/Sema/SemaConcept.h
===================================================================
--- clang/include/clang/Sema/SemaConcept.h
+++ clang/include/clang/Sema/SemaConcept.h
@@ -43,11 +43,28 @@
       return false;
 
     for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
+      const TemplateArgument &ArgA = (*ParameterMapping)[I].getArgument();
+      const TemplateArgument &ArgB = (*Other.ParameterMapping)[I].getArgument();
+
+      if (ArgA.getKind() == TemplateArgument::Type &&
+          ArgB.getKind() == TemplateArgument::Type)
+        if (const auto *SubstA =
+                ArgA.getAsType()->getAs<SubstTemplateTypeParmType>())
+          if (const auto *SubstB =
+                  ArgB.getAsType()->getAs<SubstTemplateTypeParmType>()) {
+            QualType ReplacementA = SubstA->getReplacementType();
+            QualType ReplacementB = SubstB->getReplacementType();
+            if (ReplacementA->isDecltypeType() &&
+                ReplacementB->isDecltypeType()) {
+              assert(ReplacementA->isDependentType() &&
+                     ReplacementB->isDependentType());
+              continue;
+            }
+          }
+
       llvm::FoldingSetNodeID IDA, IDB;
-      C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
-          .Profile(IDA, C);
-      C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
-          .Profile(IDB, C);
+      C.getCanonicalTemplateArgument(ArgA).Profile(IDA, C);
+      C.getCanonicalTemplateArgument(ArgB).Profile(IDB, C);
       if (IDA != IDB)
         return false;
     }
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -8309,14 +8309,17 @@
       const NamedDecl *NewInstFrom, TemplateParameterList *New,
       const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
       TemplateParameterListEqualKind Kind,
-      SourceLocation TemplateArgLoc = SourceLocation());
+      SourceLocation TemplateArgLoc = SourceLocation(),
+      bool PartialOrdering = false);
 
   bool TemplateParameterListsAreEqual(
       TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
       TemplateParameterListEqualKind Kind,
-      SourceLocation TemplateArgLoc = SourceLocation()) {
+      SourceLocation TemplateArgLoc = SourceLocation(),
+      bool PartialOrdering = false) {
     return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
-                                          Kind, TemplateArgLoc);
+                                          Kind, TemplateArgLoc,
+                                          PartialOrdering);
   }
 
   bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
@@ -9001,8 +9004,7 @@
   FunctionTemplateDecl *getMoreSpecializedTemplate(
       FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
       TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-      unsigned NumCallArguments2, bool Reversed = false,
-      bool AllowOrderingByConstraints = true);
+      unsigned NumCallArguments2, bool Reversed = false);
   UnresolvedSetIterator
   getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
                      TemplateSpecCandidateSet &FailedCandidates,
Index: clang/include/clang/AST/DeclTemplate.h
===================================================================
--- clang/include/clang/AST/DeclTemplate.h
+++ clang/include/clang/AST/DeclTemplate.h
@@ -848,6 +848,15 @@
     /// The first value in the array is the number of specializations/partial
     /// specializations that follow.
     uint32_t *LazySpecializations = nullptr;
+
+    /// The set of "injected" template arguments used within this
+    /// template.
+    ///
+    /// This pointer refers to the template arguments (there are as
+    /// many template arguments as template parameaters) for the
+    /// template, and is allocated lazily, since most templates do not
+    /// require the use of this information.
+    TemplateArgument *InjectedArgs = nullptr;
   };
 
   /// Pointer to the common data shared by all declarations of this
@@ -955,6 +964,14 @@
     getCommonPtr()->InstantiatedFromMember.setPointer(TD);
   }
 
+  /// Retrieve the "injected" template arguments that correspond to the
+  /// template parameters of this template.
+  ///
+  /// Although the C++ standard has no notion of the "injected" template
+  /// arguments for a template, the notion is convenient when
+  /// we need to perform substitutions inside the definition of a template.
+  ArrayRef<TemplateArgument> getInjectedTemplateArgs();
+
   using redecl_range = redeclarable_base::redecl_range;
   using redecl_iterator = redeclarable_base::redecl_iterator;
 
@@ -999,15 +1016,6 @@
     /// template, including explicit specializations and instantiations.
     llvm::FoldingSetVector<FunctionTemplateSpecializationInfo> Specializations;
 
-    /// The set of "injected" template arguments used within this
-    /// function template.
-    ///
-    /// This pointer refers to the template arguments (there are as
-    /// many template arguments as template parameaters) for the function
-    /// template, and is allocated lazily, since most function templates do not
-    /// require the use of this information.
-    TemplateArgument *InjectedArgs = nullptr;
-
     Common() = default;
   };
 
@@ -1107,15 +1115,6 @@
     return makeSpecIterator(getSpecializations(), true);
   }
 
-  /// Retrieve the "injected" template arguments that correspond to the
-  /// template parameters of this function template.
-  ///
-  /// Although the C++ standard has no notion of the "injected" template
-  /// arguments for a function template, the notion is convenient when
-  /// we need to perform substitutions inside the definition of a function
-  /// template.
-  ArrayRef<TemplateArgument> getInjectedTemplateArgs();
-
   /// Return whether this function template is an abbreviated function template,
   /// e.g. `void foo(auto x)` or `template<typename T> void foo(auto x)`
   bool isAbbreviated() const {
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -416,6 +416,9 @@
 - Implemented `P0634r3 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html>`_,
   which removes the requirement for the ``typename`` keyword in certain contexts.
 
+- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 <https://wg21.link/P2113R0>`_
+  ([temp.func.order]p6.2.1 is not implemented, matching GCC).
+
 C++2b Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to