mizvekov created this revision.
mizvekov requested review of this revision.
Herald added projects: clang, Sanitizers.
Herald added subscribers: Sanitizers, cfe-commits.

Word on the grapevine was that the committee had some discussion that
ended with unanimous agreement on eliminating relational function pointer 
comparisons.

We wanted to be bold and just ban all of them cold turkey.
But then we chickened out at the last second and are going for
eliminating just the spaceship overload candidate instead, for now.

See D104680 <https://reviews.llvm.org/D104680> for reference.

This should be fine and "safe", because the only possible semantic change this
would cause is that overload resoltion could possibly be ambiguous if
there was another viable candidate equally as good.

But to save face a little we are going to:

- Issue an "error" for three-way comparisons on function pointers. But all this 
is doing really is changing one vague error message, from an "invalid operands 
to binary expression" into an "ordered comparison of function pointers", which 
sounds more like we mean business.
- Otherwise "warn" that comparing function pointers like that is totally not 
cool (unless we are told to keep quiet about this).
- Treat them all as errors with '-pedantic-errors'.

Signed-off-by: Matheus Izvekov <mizve...@gmail.com>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D104892

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/CXX/class/class.compare/class.spaceship/p2.cpp
  clang/test/CXX/drs/dr15xx.cpp
  clang/test/CXX/drs/dr3xx.cpp
  clang/test/CXX/expr/expr.const/p2-0x.cpp
  clang/test/FixIt/fixit.cpp
  clang/test/Parser/cxx-template-argument.cpp
  clang/test/Sema/compare.c
  clang/test/SemaCXX/compare-cxx2a.cpp
  clang/test/SemaCXX/compare-function-pointer.cpp
  clang/test/SemaTemplate/resolve-single-template-id.cpp
  compiler-rt/test/asan/TestCases/Posix/coverage-module-unloaded.cpp

Index: compiler-rt/test/asan/TestCases/Posix/coverage-module-unloaded.cpp
===================================================================
--- compiler-rt/test/asan/TestCases/Posix/coverage-module-unloaded.cpp
+++ compiler-rt/test/asan/TestCases/Posix/coverage-module-unloaded.cpp
@@ -13,6 +13,7 @@
 
 #include <assert.h>
 #include <dlfcn.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <unistd.h>
 
@@ -38,10 +39,11 @@
 
   // It matters whether the unloaded module has a higher or lower address range
   // than the remaining one. Make sure to test both cases.
+  bool lt = reinterpret_cast<uintptr_t>(bar1) < reinterpret_cast<uintptr_t>(bar2);
   if (argc < 2)
-    dlclose(bar1 < bar2 ? handle1 : handle2);
+    dlclose(lt ? handle1 : handle2);
   else
-    dlclose(bar1 < bar2 ? handle2 : handle1);
+    dlclose(lt ? handle2 : handle1);
   return 0;
 }
 #endif
Index: clang/test/SemaTemplate/resolve-single-template-id.cpp
===================================================================
--- clang/test/SemaTemplate/resolve-single-template-id.cpp
+++ clang/test/SemaTemplate/resolve-single-template-id.cpp
@@ -65,12 +65,14 @@
   void (*u)(int) = oneT<int>;
 
   b = (void (*)()) twoT<int>;
-  
-  one < one; //expected-warning {{self-comparison always evaluates to false}} \
-             //expected-warning {{relational comparison result unused}}         
 
-  oneT<int> < oneT<int>;  //expected-warning {{self-comparison always evaluates to false}} \
-                          //expected-warning {{relational comparison result unused}}
+  one < one; // expected-warning {{self-comparison always evaluates to false}} \
+             // expected-warning {{relational comparison result unused}}       \
+             // expected-warning {{ordered comparison of function pointers}}
+
+  oneT<int> < oneT<int>; // expected-warning {{self-comparison always evaluates to false}} \
+                         // expected-warning {{relational comparison result unused}}       \
+                         // expected-warning {{ordered comparison of function pointers}}
 
   two < two; //expected-error 2 {{reference to overloaded function could not be resolved; did you mean to call it with no arguments?}} expected-error {{invalid operands to binary expression ('void' and 'void')}}
   twoT<int> < twoT<int>; //expected-error {{reference to overloaded function could not be resolved; did you mean to call it?}} {{cannot resolve overloaded function 'twoT' from context}}
Index: clang/test/SemaCXX/compare-function-pointer.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/compare-function-pointer.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
+
+using fp0_t = void (*)();
+using fp1_t = int (*)();
+
+extern fp0_t a, b;
+extern fp1_t c;
+
+bool eq0 = a == b;
+bool ne0 = a != b;
+bool lt0 = a < b;   // expected-warning {{ordered comparison of function pointers ('fp0_t' (aka 'void (*)()') and 'fp0_t')}}
+bool le0 = a <= b;  // expected-warning {{ordered comparison of function pointers}}
+bool gt0 = a > b;   // expected-warning {{ordered comparison of function pointers}}
+bool ge0 = a >= b;  // expected-warning {{ordered comparison of function pointers}}
+auto tw0 = a <=> b; // expected-error {{ordered comparison of function pointers}}
+
+bool eq1 = a == c;  // expected-error {{comparison of distinct pointer types}}
+bool ne1 = a != c;  // expected-error {{comparison of distinct pointer types}}
+bool lt1 = a < c;   // expected-warning {{ordered comparison of function pointers ('fp0_t' (aka 'void (*)()') and 'fp1_t' (aka 'int (*)()'))}}
+                    // expected-error@-1 {{comparison of distinct pointer types}}
+bool le1 = a <= c;  // expected-warning {{ordered comparison of function pointers}}
+                    // expected-error@-1 {{comparison of distinct pointer types}}
+bool gt1 = a > c;   // expected-warning {{ordered comparison of function pointers}}
+                    // expected-error@-1 {{comparison of distinct pointer types}}
+bool ge1 = a >= c;  // expected-warning {{ordered comparison of function pointers}}
+                    // expected-error@-1 {{comparison of distinct pointer types}}
+auto tw1 = a <=> c; // expected-error {{ordered comparison of function pointers}}
Index: clang/test/SemaCXX/compare-cxx2a.cpp
===================================================================
--- clang/test/SemaCXX/compare-cxx2a.cpp
+++ clang/test/SemaCXX/compare-cxx2a.cpp
@@ -212,8 +212,6 @@
 struct ClassB : Class {};
 struct Class2 {};
 using FnTy = void(int);
-using FnTy2 = long(int);
-using FnTy3 = void(int) noexcept;
 using MemFnTy = void (Class::*)() const;
 using MemDataTy = long(Class::*);
 
@@ -232,11 +230,6 @@
   (void)(md <=> md); // expected-error {{invalid operands}} expected-warning {{self-comparison}}
 }
 
-void test_compatible_pointer(FnTy *f1, FnTy2 *f2, FnTy3 *f3) {
-  (void)(f1 <=> f2); // expected-error {{distinct pointer types}}
-  (void)(f1 <=> f3); // expected-error {{invalid operands}}
-}
-
 // Test that variable narrowing is deferred for value dependent expressions
 template <int Val>
 auto test_template_overflow() {
Index: clang/test/Sema/compare.c
===================================================================
--- clang/test/Sema/compare.c
+++ clang/test/Sema/compare.c
@@ -220,7 +220,7 @@
 int function_pointers(int (*a)(int), int (*b)(int), void (*c)(int)) {
   return a > b; // expected-warning {{ordered comparison of function pointers}}
   return function_pointers > function_pointers; // expected-warning {{self-comparison always evaluates to false}} expected-warning{{ordered comparison of function pointers}}
-  return a > c; // expected-warning {{comparison of distinct pointer types}}
+  return a > c;                                 // expected-warning {{comparison of distinct pointer types}} expected-warning {{ordered comparison of function pointers}}
   return a == (void *) 0;
   return a == (void *) 1; // expected-warning {{equality comparison between function pointer and void pointer}}
 }
Index: clang/test/Parser/cxx-template-argument.cpp
===================================================================
--- clang/test/Parser/cxx-template-argument.cpp
+++ clang/test/Parser/cxx-template-argument.cpp
@@ -22,15 +22,18 @@
   void f(S<int>=0); // expected-error {{a space is required between a right angle bracket and an equals sign (use '> =')}}
   void f(S<S<int>>=S<int>()); // expected-error {{use '> >'}} expected-error {{use '> ='}}
   template<typename T> void t();
+  struct R {
+    friend void operator==(void(*)(), R) {}
+    friend void operator>=(void(*)(), R) {}
+  };
   void g() {
-    void (*p)() = &t<int>;
-    (void)(&t<int>==p); // expected-error {{use '> ='}}
-    (void)(&t<int>>=p); // expected-error {{use '> >'}}
-    (void)(&t<S<int>>>=p);
+    (void)(&t<int>==R()); // expected-error {{use '> ='}}
+    (void)(&t<int>>=R()); // expected-error {{use '> >'}}
+    (void)(&t<S<int>>>=R());
 #if __cplusplus <= 199711L
     // expected-error@-2 {{use '> >'}}
 #endif
-    (void)(&t<S<int>>==p); // expected-error {{use '> >'}} expected-error {{use '> ='}}
+    (void)(&t<S<int>>==R()); // expected-error {{use '> >'}} expected-error {{use '> ='}}
   }
 }
 
Index: clang/test/FixIt/fixit.cpp
===================================================================
--- clang/test/FixIt/fixit.cpp
+++ clang/test/FixIt/fixit.cpp
@@ -296,9 +296,7 @@
   void g() {
     void (*p)() = &t<int>;
     (void)(&t<int>==p); // expected-error {{use '> ='}}
-    (void)(&t<int>>=p); // expected-error {{use '> >'}}
 #if __cplusplus < 201103L
-    (void)(&t<S<int>>>=p); // expected-error {{use '> >'}}
     (Shr)&t<S<int>>>>=p; // expected-error {{use '> >'}}
 #endif
 
Index: clang/test/CXX/expr/expr.const/p2-0x.cpp
===================================================================
--- clang/test/CXX/expr/expr.const/p2-0x.cpp
+++ clang/test/CXX/expr/expr.const/p2-0x.cpp
@@ -514,8 +514,7 @@
   void f(), g();
 
   constexpr void (*pf)() = &f, (*pg)() = &g;
-  constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}}
-  constexpr bool u14 = pf == pg;
+  constexpr bool u13 = pf == pg;
 
   // If two pointers point to non-static data members of the same object with
   // different access control, the result is unspecified.
Index: clang/test/CXX/drs/dr3xx.cpp
===================================================================
--- clang/test/CXX/drs/dr3xx.cpp
+++ clang/test/CXX/drs/dr3xx.cpp
@@ -18,9 +18,9 @@
   void operator-(S, S);
 
   void f() {
-    bool a = (void(*)(S, S))operator+<S> <
+    bool a = (void(*)(S, S))operator+<S> < // expected-error {{ordered comparison of function pointers}}
              (void(*)(S, S))operator+<S>;
-    bool b = (void(*)(S, S))operator- < // cxx20_2b-note {{to match this '<'}}
+    bool b = (void(*)(S, S))operator- < // cxx20_2b-note {{to match this '<'}} cxx98_17-error {{ordered comparison of function pointers}}
              (void(*)(S, S))operator-; // cxx20_2b-error {{expected '>'}}
     bool c = (void(*)(S, S))operator+ < // expected-note {{to match this '<'}}
              (void(*)(S, S))operator-; // expected-error {{expected '>'}}
Index: clang/test/CXX/drs/dr15xx.cpp
===================================================================
--- clang/test/CXX/drs/dr15xx.cpp
+++ clang/test/CXX/drs/dr15xx.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,cxx14_17 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
 
 namespace dr1512 { // dr1512: 4
   void f(char *p) {
@@ -28,10 +28,10 @@
   template<typename A, typename B, typename C> void composite_pointer_type_is_ord() {
     composite_pointer_type_is_base<A, B, C>();
 
-    typedef __typeof(val<A>() < val<B>()) cmp;
-    typedef __typeof(val<A>() <= val<B>()) cmp;
-    typedef __typeof(val<A>() > val<B>()) cmp;
-    typedef __typeof(val<A>() >= val<B>()) cmp;
+    typedef __typeof(val<A>() < val<B>()) cmp;  // cxx17-error 2 {{ordered comparison of function pointers}}
+    typedef __typeof(val<A>() <= val<B>()) cmp; // cxx17-error 2 {{ordered comparison of function pointers}}
+    typedef __typeof(val<A>() > val<B>()) cmp;  // cxx17-error 2 {{ordered comparison of function pointers}}
+    typedef __typeof(val<A>() >= val<B>()) cmp; // cxx17-error 2 {{ordered comparison of function pointers}}
     typedef bool cmp;
   }
 
@@ -79,8 +79,8 @@
     no_composite_pointer_type<const int (A::*)(), volatile int (C::*)()>();
 
 #if __cplusplus > 201402
-    composite_pointer_type_is_ord<int (*)() noexcept, int (*)(), int (*)()>();
-    composite_pointer_type_is_ord<int (*)(), int (*)() noexcept, int (*)()>();
+    composite_pointer_type_is_ord<int (*)() noexcept, int (*)(), int (*)()>(); // expected-note {{requested here}}
+    composite_pointer_type_is_ord<int (*)(), int (*)() noexcept, int (*)()>(); // expected-note {{requested here}}
     composite_pointer_type_is_unord<int (A::*)() noexcept, int (A::*)(), int (A::*)()>();
     composite_pointer_type_is_unord<int (A::*)(), int (A::*)() noexcept, int (A::*)()>();
     // FIXME: This looks like a standard defect; these should probably all have type 'int (B::*)()'.
Index: clang/test/CXX/class/class.compare/class.spaceship/p2.cpp
===================================================================
--- clang/test/CXX/class/class.compare/class.spaceship/p2.cpp
+++ clang/test/CXX/class/class.compare/class.spaceship/p2.cpp
@@ -159,7 +159,7 @@
 namespace PR48856 {
   struct A {
     auto operator<=>(const A &) const = default; // expected-warning {{implicitly deleted}}
-    void (*x)();                                 // expected-note {{does not support relational comparisons}}
+    void (*x)();                                 // expected-note {{because there is no viable three-way comparison function for member 'x'}}
   };
 
   struct B {
@@ -192,12 +192,23 @@
   };
   std::partial_ordering cmp_b2 = b2() <=> b2();
 
+  using fp = void (*)();
+
   struct a3 {
-    using fp = void (*)();
     operator fp() const;
   };
   struct b3 {
     auto operator<=>(b3 const &) const = default; // expected-warning {{implicitly deleted}}
-    a3 f; // expected-note {{would compare member 'f' as 'void (*)()', which does not support relational comparisons}}
+    a3 f;                                         // expected-note {{because there is no viable three-way comparison function}}
+  };
+
+  struct a4 { // Test that function pointer conversion operator here is ignored for this overload resolution.
+    operator int() const;
+    operator fp() const;
+  };
+  struct b4 {
+    auto operator<=>(b4 const &) const = default;
+    a4 f;
   };
+  std::strong_ordering cmp_b4 = b4() <=> b4();
 }
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -8466,7 +8466,7 @@
   //        bool       operator==(T, T);
   //        bool       operator!=(T, T);
   //           R       operator<=>(T, T)
-  void addGenericBinaryPointerOrEnumeralOverloads() {
+  void addGenericBinaryPointerOrEnumeralOverloads(bool IsSpaceship) {
     // C++ [over.match.oper]p3:
     //   [...]the built-in candidates include all of the candidate operator
     //   functions defined in 13.6 that, compared to the given operator, [...]
@@ -8525,6 +8525,8 @@
         // Don't add the same builtin candidate twice.
         if (!AddedTypes.insert(S.Context.getCanonicalType(PtrTy)).second)
           continue;
+        if (IsSpaceship && PtrTy->isFunctionPointerType())
+          continue;
 
         QualType ParamTypes[2] = {PtrTy, PtrTy};
         S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
@@ -8715,7 +8717,7 @@
   //
   //   where LR is the result of the usual arithmetic conversions
   //   between types L and R.
-  void addBinaryBitwiseArithmeticOverloads(OverloadedOperatorKind Op) {
+  void addBinaryBitwiseArithmeticOverloads() {
     if (!HasArithmeticOrEnumeralCandidateType)
       return;
 
@@ -9221,18 +9223,20 @@
   case OO_EqualEqual:
   case OO_ExclaimEqual:
     OpBuilder.addEqualEqualOrNotEqualMemberPointerOrNullptrOverloads();
-    LLVM_FALLTHROUGH;
+    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(/*IsSpaceship=*/false);
+    OpBuilder.addGenericBinaryArithmeticOverloads();
+    break;
 
   case OO_Less:
   case OO_Greater:
   case OO_LessEqual:
   case OO_GreaterEqual:
-    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads();
+    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(/*IsSpaceship=*/false);
     OpBuilder.addGenericBinaryArithmeticOverloads();
     break;
 
   case OO_Spaceship:
-    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads();
+    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(/*IsSpaceship=*/true);
     OpBuilder.addThreeWayArithmeticOverloads();
     break;
 
@@ -9241,7 +9245,7 @@
   case OO_Pipe:
   case OO_LessLess:
   case OO_GreaterGreater:
-    OpBuilder.addBinaryBitwiseArithmeticOverloads(Op);
+    OpBuilder.addBinaryBitwiseArithmeticOverloads();
     break;
 
   case OO_Amp: // '&' is either unary or binary
@@ -9251,7 +9255,7 @@
       //      operator '->', the built-in candidates set is empty.
       break;
 
-    OpBuilder.addBinaryBitwiseArithmeticOverloads(Op);
+    OpBuilder.addBinaryBitwiseArithmeticOverloads();
     break;
 
   case OO_Tilde:
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -11802,6 +11802,19 @@
                                    LHS.get()->getSourceRange());
   }
 
+  if (IsOrdered && LHSType->isFunctionPointerType() &&
+      RHSType->isFunctionPointerType()) {
+    // Valid unless a relational comparison of function pointers
+    bool IsError = Opc == BO_Cmp;
+    Diag(Loc, IsError
+                  ? diag::err_typecheck_ordered_comparison_of_function_pointers
+                  : diag::ext_typecheck_ordered_comparison_of_function_pointers)
+        << LHSType << RHSType << LHS.get()->getSourceRange()
+        << RHS.get()->getSourceRange();
+    if (IsError)
+      return QualType();
+  }
+
   if ((LHSType->isIntegerType() && !LHSIsNull) ||
       (RHSType->isIntegerType() && !RHSIsNull)) {
     // Skip normal pointer conversion checks in this case; we have better
@@ -11869,12 +11882,6 @@
               << LHSType << RHSType << LCanPointeeTy->isIncompleteType()
               << RCanPointeeTy->isIncompleteType();
         }
-        if (LCanPointeeTy->isFunctionType()) {
-          // Valid unless a relational comparison of function pointers
-          Diag(Loc, diag::ext_typecheck_ordered_comparison_of_function_pointers)
-              << LHSType << RHSType << LHS.get()->getSourceRange()
-              << RHS.get()->getSourceRange();
-        }
       }
     } else if (!IsRelational &&
                (LCanPointeeTy->isVoidType() || RCanPointeeTy->isVoidType())) {
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -7866,15 +7866,6 @@
         assert(Best->BuiltinParamTypes[2].isNull() &&
                "invalid builtin comparison");
 
-        // The builtin operator for relational comparisons on function
-        // pointers is the only known case which cannot be used.
-        if (OO != OO_EqualEqual && T->isFunctionPointerType()) {
-          if (Diagnose == ExplainDeleted)
-            S.Diag(Subobj.Loc, diag::note_defaulted_comparison_selected_invalid)
-                << Subobj.Kind << Subobj.Decl << T;
-          return Result::deleted();
-        }
-
         if (NeedsDeducing) {
           Optional<ComparisonCategoryType> Cat =
               getComparisonCategoryForBuiltinCmp(T);
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6797,6 +6797,8 @@
 def ext_typecheck_ordered_comparison_of_function_pointers : ExtWarn<
   "ordered comparison of function pointers (%0 and %1)">,
   InGroup<DiagGroup<"ordered-compare-function-pointers">>;
+def err_typecheck_ordered_comparison_of_function_pointers : Error<
+  "ordered comparison of function pointers (%0 and %1)">;
 def ext_typecheck_comparison_of_fptr_to_void : Extension<
   "equality comparison between function pointer and void pointer (%0 and %1)">;
 def err_typecheck_comparison_of_fptr_to_void : Error<
@@ -9143,9 +9145,6 @@
   "%select{|member|base class}0 %1 declared here">;
 def note_defaulted_comparison_cannot_deduce_callee : Note<
   "selected 'operator<=>' for %select{|member|base class}0 %1 declared here">;
-def note_defaulted_comparison_selected_invalid : Note<
-  "would compare %select{|member|base class}0 %1 "
-  "as %2, which does not support relational comparisons">;
 def err_incorrect_defaulted_comparison_constexpr : Error<
   "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
   "three-way comparison operator}0 "
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D104892: ... Matheus Izvekov via Phabricator via cfe-commits

Reply via email to