[clang] e18c2c5 - [Clang] use non-instantiated function declaration for constraints partial ordering

Yuanfang Chen via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 30 22:40:26 PDT 2022


Author: Yuanfang Chen
Date: 2022-10-30T22:39:47-07:00
New Revision: e18c2c5548f6864def4a110239395a18afc195d6

URL: https://github.com/llvm/llvm-project/commit/e18c2c5548f6864def4a110239395a18afc195d6
DIFF: https://github.com/llvm/llvm-project/commit/e18c2c5548f6864def4a110239395a18afc195d6.diff

LOG: [Clang] use non-instantiated function declaration for constraints partial ordering

Per wordings in
- https://eel.is/c++draft/over.match#best.general-2.6
- https://eel.is/c++draft/temp.constr.order
- https://eel.is/c++draft/temp.constr#atomic-1

constraints partial ordering should use the unsubstituted template
parameters of the constrained entity, not the instantiated entity.

Fix #56154

Reviewed By: erichkeane, royjacobson, mizvekov

Differential Revision: https://reviews.llvm.org/D136545

Added: 
    clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaConcept.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/test/CXX/over/over.match/over.match.best/p2.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bd4c0bf7fa0c0..641f75eeeb64c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -264,6 +264,8 @@ Bug Fixes
   constraint, which re-evaluated the same constraint.
   `Issue 53213 <https://github.com/llvm/llvm-project/issues/53213>`_
   `Issue 45736 <https://github.com/llvm/llvm-project/issues/45736>`_
+- Fix an issue when performing constraints partial ordering on non-template
+  functions. `Issue 56154 <https://github.com/llvm/llvm-project/issues/56154>`_
 
 Improvements to Clang's diagnostics
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index d3d87c8354aea..54d8a746fa247 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1300,6 +1300,18 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
                                   NamedDecl *D2,
                                   MutableArrayRef<const Expr *> AC2,
                                   bool &Result) {
+  if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) {
+    auto IsExpectedEntity = [](const FunctionDecl *FD) {
+      FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind();
+      return Kind == FunctionDecl::TK_NonTemplate ||
+             Kind == FunctionDecl::TK_FunctionTemplate;
+    };
+    const auto *FD2 = dyn_cast<FunctionDecl>(D2);
+    assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
+           "use non-instantiated function declaration for constraints partial "
+           "ordering");
+  }
+
   if (AC1.empty()) {
     Result = AC2.empty();
     return false;

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 686a126968e8a..a164d9af8f34a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18218,13 +18218,20 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
     if (!SatisfactionStatus[i])
       continue;
     CXXMethodDecl *Method = Methods[i];
-    const Expr *Constraints = Method->getTrailingRequiresClause();
+    CXXMethodDecl *OrigMethod = Method;
+    if (FunctionDecl *MF = OrigMethod->getInstantiatedFromMemberFunction())
+      OrigMethod = cast<CXXMethodDecl>(MF);
+
+    const Expr *Constraints = OrigMethod->getTrailingRequiresClause();
     bool AnotherMethodIsMoreConstrained = false;
     for (size_t j = 0; j < Methods.size(); j++) {
       if (i == j || !SatisfactionStatus[j])
         continue;
       CXXMethodDecl *OtherMethod = Methods[j];
-      if (!AreSpecialMemberFunctionsSameKind(S.Context, Method, OtherMethod,
+      if (FunctionDecl *MF = OtherMethod->getInstantiatedFromMemberFunction())
+        OtherMethod = cast<CXXMethodDecl>(MF);
+
+      if (!AreSpecialMemberFunctionsSameKind(S.Context, OrigMethod, OtherMethod,
                                              CSM))
         continue;
 
@@ -18235,7 +18242,7 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
         AnotherMethodIsMoreConstrained = true;
         break;
       }
-      if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, Method,
+      if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, OrigMethod,
                                    {Constraints},
                                    AnotherMethodIsMoreConstrained)) {
         // There was an error with the constraints comparison. Exit the loop

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d21de7db3f53b..45bca3a31d3a7 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10039,13 +10039,20 @@ bool clang::isBetterOverloadCandidate(
   //      parameter-type-lists, and F1 is more constrained than F2 [...],
   if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
       sameFunctionParameterTypeLists(S, Cand1, Cand2)) {
-    const Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
-    const Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
+    FunctionDecl *Function1 = Cand1.Function;
+    FunctionDecl *Function2 = Cand2.Function;
+    if (FunctionDecl *MF = Function1->getInstantiatedFromMemberFunction())
+      Function1 = MF;
+    if (FunctionDecl *MF = Function2->getInstantiatedFromMemberFunction())
+      Function2 = MF;
+
+    const Expr *RC1 = Function1->getTrailingRequiresClause();
+    const Expr *RC2 = Function2->getTrailingRequiresClause();
     if (RC1 && RC2) {
       bool AtLeastAsConstrained1, AtLeastAsConstrained2;
-      if (S.IsAtLeastAsConstrained(Cand1.Function, RC1, Cand2.Function, RC2,
+      if (S.IsAtLeastAsConstrained(Function1, RC1, Function2, RC2,
                                    AtLeastAsConstrained1) ||
-          S.IsAtLeastAsConstrained(Cand2.Function, RC2, Cand1.Function, RC1,
+          S.IsAtLeastAsConstrained(Function2, RC2, Function1, RC1,
                                    AtLeastAsConstrained2))
         return false;
       if (AtLeastAsConstrained1 != AtLeastAsConstrained2)
@@ -12630,20 +12637,24 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
   DeclAccessPair DAP;
   SmallVector<FunctionDecl *, 2> AmbiguousDecls;
 
-  auto CheckMoreConstrained =
-      [&] (FunctionDecl *FD1, FunctionDecl *FD2) -> Optional<bool> {
-        SmallVector<const Expr *, 1> AC1, AC2;
-        FD1->getAssociatedConstraints(AC1);
-        FD2->getAssociatedConstraints(AC2);
-        bool AtLeastAsConstrained1, AtLeastAsConstrained2;
-        if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
-          return None;
-        if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
-          return None;
-        if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
-          return None;
-        return AtLeastAsConstrained1;
-      };
+  auto CheckMoreConstrained = [&](FunctionDecl *FD1,
+                                  FunctionDecl *FD2) -> Optional<bool> {
+    if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction())
+      FD1 = MF;
+    if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction())
+      FD2 = MF;
+    SmallVector<const Expr *, 1> AC1, AC2;
+    FD1->getAssociatedConstraints(AC1);
+    FD2->getAssociatedConstraints(AC2);
+    bool AtLeastAsConstrained1, AtLeastAsConstrained2;
+    if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
+      return None;
+    if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
+      return None;
+    if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
+      return None;
+    return AtLeastAsConstrained1;
+  };
 
   // Don't use the AddressOfResolver because we're specifically looking for
   // cases where we have one overload candidate that lacks

diff  --git a/clang/test/CXX/over/over.match/over.match.best/p2.cpp b/clang/test/CXX/over/over.match/over.match.best/p2.cpp
index 3a44436655767..e0f7dfb6a4065 100644
--- a/clang/test/CXX/over/over.match/over.match.best/p2.cpp
+++ b/clang/test/CXX/over/over.match/over.match.best/p2.cpp
@@ -7,7 +7,11 @@ namespace PR44761 {
     bool operator<(const A&) const & requires X<T>; // #1
     int operator<=>(const A&) const & requires X<T> && X<int> = delete; // #2
   };
-  bool k1 = A<int>() < A<int>(); // not ordered by constraints: prefer non-rewritten form
+  bool k1 = A<int>() < A<int>(); // prefer more-constrained 'operator<=>'
+  // expected-error at -1 {{deleted}}
+  // expected-note@#1 {{candidate}}
+  // expected-note@#2 {{candidate function has been explicitly deleted}}
+  // expected-note@#2 {{candidate function (with reversed parameter order) has been explicitly deleted}}
   bool k2 = A<float>() < A<float>(); // prefer more-constrained 'operator<=>'
   // expected-error at -1 {{deleted}}
   // expected-note@#1 {{candidate}}

diff  --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp
new file mode 100644
index 0000000000000..4ef8403774428
--- /dev/null
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s
+// expected-no-diagnostics
+
+template<typename T, typename U>
+constexpr static bool is_same_v = false;
+
+template<typename T>
+constexpr static bool is_same_v<T, T> = true;
+
+namespace PR56154 {
+  template <int N> concept C0 = (N == 0);
+  template <int N, int N2> concept C0x = C0<N>;
+  template <int N1, int N2> concept C00 = C0x<N1, N2> && C0<N2>;
+
+  template<int N1, int N2>
+  struct A {
+    void f() requires C00<N1, N2>;
+    void f() requires C0x<N1, N2> = delete;
+
+    static short g() requires C00<N1, N2>;
+    static int g() requires C0x<N1, N2>;
+  };
+  void h(A<0, 0> a) {
+    a.f();
+    static_assert(is_same_v<decltype(&A<0, 0>::g), short(*)()>);
+  }
+}


        


More information about the cfe-commits mailing list