diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/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>; +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 " diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/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 Cat = getComparisonCategoryForBuiltinCmp(T); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/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 = getLangOpts().CPlusPlus; + 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())) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/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 IsOrdered) { // 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 (IsOrdered && 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(false); + OpBuilder.addGenericBinaryArithmeticOverloads(); + break; case OO_Less: case OO_Greater: case OO_LessEqual: case OO_GreaterEqual: - OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(); + OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(true); OpBuilder.addGenericBinaryArithmeticOverloads(); break; case OO_Spaceship: - OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(); + OpBuilder.addGenericBinaryPointerOrEnumeralOverloads(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: diff --git a/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp b/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp --- a/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp +++ b/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 { @@ -198,6 +198,6 @@ }; 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 for member 'f'}} }; } diff --git a/clang/test/CXX/drs/dr15xx.cpp b/clang/test/CXX/drs/dr15xx.cpp --- a/clang/test/CXX/drs/dr15xx.cpp +++ b/clang/test/CXX/drs/dr15xx.cpp @@ -79,8 +79,8 @@ no_composite_pointer_type(); #if __cplusplus > 201402 - composite_pointer_type_is_ord(); - composite_pointer_type_is_ord(); + composite_pointer_type_is_unord(); + composite_pointer_type_is_unord(); composite_pointer_type_is_unord(); composite_pointer_type_is_unord(); // FIXME: This looks like a standard defect; these should probably all have type 'int (B::*)()'. diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -18,9 +18,9 @@ void operator-(S, S); void f() { - bool a = (void(*)(S, S))operator+ < + bool a = (void(*)(S, S))operator+ < // expected-error {{ordered comparison of function pointers}} (void(*)(S, S))operator+; - 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 '>'}} diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/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. diff --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp --- a/clang/test/SemaCXX/compare-cxx2a.cpp +++ b/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 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 --- /dev/null +++ b/clang/test/SemaCXX/compare-function-pointer.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s + +using fp_t = void (*)(); +extern fp_t a, b; + +bool eq = a == b; +bool ne = a != b; +bool lt = a < b; // expected-error {{ordered comparison of function pointers ('fp_t' (aka 'void (*)()') and 'fp_t')}} +bool le = a <= b; // expected-error {{ordered comparison of function pointers}} +bool gt = a > b; // expected-error {{ordered comparison of function pointers}} +bool ge = a >= b; // expected-error {{ordered comparison of function pointers}} +bool tw = a <=> b; // expected-error {{ordered comparison of function pointers}}