[clang] Stop providing builtin overload candidate for relational function pointer...
authorMatheus Izvekov <mizvekov@gmail.com>
Tue, 22 Jun 2021 01:48:51 +0000 (03:48 +0200)
committerMatheus Izvekov <mizvekov@gmail.com>
Fri, 25 Jun 2021 22:08:02 +0000 (00:08 +0200)
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 for reference.

This should be fine and "safe", because the only possible semantic change this
would cause is that overload resolution 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).

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>
Reviewed By: rsmith

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

16 files changed:
clang/include/clang/Basic/DiagnosticGroups.td
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 [new file with mode: 0644]
clang/test/SemaTemplate/resolve-single-template-id.cpp
compiler-rt/test/asan/TestCases/Posix/coverage-module-unloaded.cpp

index ca8e05f..f35c105 100644 (file)
@@ -525,6 +525,7 @@ def OpenCLUnsupportedRGBA: DiagGroup<"opencl-unsupported-rgba">;
 def UnderalignedExceptionObject : DiagGroup<"underaligned-exception-object">;
 def DeprecatedObjCIsaUsage : DiagGroup<"deprecated-objc-isa-usage">;
 def ExplicitInitializeCall : DiagGroup<"explicit-initialize-call">;
+def OrderedCompareFunctionPointers : DiagGroup<"ordered-compare-function-pointers">;
 def Packed : DiagGroup<"packed">;
 def Padded : DiagGroup<"padded">;
 
index b114cdf..b5b8bc6 100644 (file)
@@ -6794,9 +6794,14 @@ def ext_typecheck_compare_complete_incomplete_pointers : Extension<
   "%0 is %select{|in}2complete and "
   "%1 is %select{|in}3complete">,
   InGroup<C11>;
+def warn_typecheck_ordered_comparison_of_function_pointers : Warning<
+  "ordered comparison of function pointers (%0 and %1)">,
+  InGroup<OrderedCompareFunctionPointers>;
 def ext_typecheck_ordered_comparison_of_function_pointers : ExtWarn<
   "ordered comparison of function pointers (%0 and %1)">,
-  InGroup<DiagGroup<"ordered-compare-function-pointers">>;
+  InGroup<OrderedCompareFunctionPointers>;
+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 +9148,6 @@ def note_defaulted_comparison_cannot_deduce_undeduced_auto : Note<
   "%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 "
index a68a06e..83c9762 100644 (file)
@@ -7869,15 +7869,6 @@ private:
         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 b643356..2d0f314 100644 (file)
@@ -11802,6 +11802,21 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
                                    LHS.get()->getSourceRange());
   }
 
+  if (IsOrdered && LHSType->isFunctionPointerType() &&
+      RHSType->isFunctionPointerType()) {
+    // Valid unless a relational comparison of function pointers
+    bool IsError = Opc == BO_Cmp;
+    auto DiagID =
+        IsError ? diag::err_typecheck_ordered_comparison_of_function_pointers
+        : getLangOpts().CPlusPlus
+            ? diag::warn_typecheck_ordered_comparison_of_function_pointers
+            : diag::ext_typecheck_ordered_comparison_of_function_pointers;
+    Diag(Loc, DiagID) << 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 +11884,6 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
               << 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 606cc56..8092f0b 100644 (file)
@@ -8466,7 +8466,7 @@ public:
   //        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 @@ public:
         // 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 @@ public:
   //
   //   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 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
   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 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
   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 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
       //      operator '->', the built-in candidates set is empty.
       break;
 
-    OpBuilder.addBinaryBitwiseArithmeticOverloads(Op);
+    OpBuilder.addBinaryBitwiseArithmeticOverloads();
     break;
 
   case OO_Tilde:
index 01fff69..61d03e0 100644 (file)
@@ -159,7 +159,7 @@ namespace BadDeducedType {
 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 @@ namespace PR50591 {
   };
   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 8bfa29a..e4f1105 100644 (file)
@@ -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 @@ namespace dr1512 { // dr1512: 4
   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-warning 2 {{ordered comparison of function pointers}}
+    typedef __typeof(val<A>() <= val<B>()) cmp; // cxx17-warning 2 {{ordered comparison of function pointers}}
+    typedef __typeof(val<A>() > val<B>()) cmp;  // cxx17-warning 2 {{ordered comparison of function pointers}}
+    typedef __typeof(val<A>() >= val<B>()) cmp; // cxx17-warning 2 {{ordered comparison of function pointers}}
     typedef bool cmp;
   }
 
@@ -79,8 +79,8 @@ namespace dr1512 { // dr1512: 4
     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 5c4c755..9c8fe2d 100644 (file)
@@ -18,9 +18,9 @@ namespace dr301 { // dr301: yes
   void operator-(S, S);
 
   void f() {
-    bool a = (void(*)(S, S))operator+<S> <
+    bool a = (void(*)(S, S))operator+<S> < // expected-warning {{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-warning {{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 82c92b7..81efbb0 100644 (file)
@@ -515,6 +515,7 @@ namespace UnspecifiedRelations {
 
   constexpr void (*pf)() = &f, (*pg)() = &g;
   constexpr bool u13 = pf < pg; // expected-error {{constant expression}} expected-note {{comparison has unspecified value}}
+                                // expected-warning@-1 {{ordered comparison of function pointers}}
   constexpr bool u14 = pf == pg;
 
   // If two pointers point to non-static data members of the same object with
index bfff20e..8a3500c 100644 (file)
@@ -1,12 +1,12 @@
-// RUN: %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++98 %s
+// RUN: %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++98 -Wno-c++14-extensions %s
 // RUN: cp %s %t-98
-// RUN: not %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++98 %t-98
-// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Wno-unused-but-set-variable -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++98 %t-98
-// RUN: not %clang_cc1 -fsyntax-only -pedantic -fdiagnostics-parseable-fixits -x c++ -std=c++11 %s 2>&1 | FileCheck %s
-// RUN: %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++11 %s
+// RUN: not %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++98 -Wno-c++14-extensions %t-98
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Wno-unused-but-set-variable -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++98 -Wno-c++14-extensions %t-98
+// RUN: not %clang_cc1 -fsyntax-only -pedantic -fdiagnostics-parseable-fixits -x c++ -std=c++11 -Wno-c++14-extensions %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -verify -fcxx-exceptions -x c++ -std=c++11 -Wno-c++14-extensions %s
 // RUN: cp %s %t-11
-// RUN: not %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++11 %t-11
-// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Wno-unused-but-set-variable -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++11 %t-11
+// RUN: not %clang_cc1 -pedantic -Wall -Wno-unused-but-set-variable -Wno-comment -fcxx-exceptions -fixit -x c++ -std=c++11 -Wno-c++14-extensions %t-11
+// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Wno-unused-but-set-variable -Werror -Wno-comment -fcxx-exceptions -x c++ -std=c++11 -Wno-c++14-extensions %t-11
 
 /* This is a test of the various code modification hints that are
    provided as part of warning or extension diagnostics. All of the
@@ -292,21 +292,21 @@ namespace greatergreater {
 
   template<template<typename>> struct TemplateTemplateParam; // expected-error {{requires 'class'}}
 
-  template<typename T> void t();
+  template <typename T> int t = 0;
   void g() {
-    void (*p)() = &t<int>;
-    (void)(&t<int>==p); // expected-error {{use '> ='}}
-    (void)(&t<int>>=p); // expected-error {{use '> >'}}
+    int p = 0;
+    (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 '> >'}}
+    (void)(t<S<int>>>=p); // expected-error {{use '> >'}}
+    (Shr)t<S<int>>>>=p; // expected-error {{use '> >'}}
 #endif
 
-    // FIXME: We correct this to '&t<int> > >= p;' not '&t<int> >>= p;'
-    //(Shr)&t<int>>>=p;
+    // FIXME: We correct this to 't<int> > >= p;' not 't<int> >>= p;'
+    //(Shr)t<int>>>=p;
 
     // FIXME: The fix-its here overlap.
-    //(void)(&t<S<int>>==p);
+    //(void)(t<S<int>>==p);
   }
 }
 
index 0e72cbf..a42a473 100644 (file)
@@ -22,15 +22,18 @@ namespace greatergreater {
   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 85dcffc..6f25eb2 100644 (file)
@@ -220,7 +220,7 @@ int pointers(int *a) {
 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 a459554..fdeb931 100644 (file)
@@ -212,8 +212,6 @@ struct Class {};
 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 test_memptr(MemFnTy mf, MemDataTy md) {
   (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() {
diff --git a/clang/test/SemaCXX/compare-function-pointer.cpp b/clang/test/SemaCXX/compare-function-pointer.cpp
new file mode 100644 (file)
index 0000000..0b185ce
--- /dev/null
@@ -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 9562845..8d7bf08 100644 (file)
@@ -65,12 +65,14 @@ int main()
   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 322a1ba..d301bb5 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <assert.h>
 #include <dlfcn.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <unistd.h>
 
@@ -38,10 +39,11 @@ int main(int argc, char **argv) {
 
   // 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