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">;
"%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<
"%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 "
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);
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
<< 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())) {
// 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, [...]
// 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);
//
// where LR is the result of the usual arithmetic conversions
// between types L and R.
- void addBinaryBitwiseArithmeticOverloads(OverloadedOperatorKind Op) {
+ void addBinaryBitwiseArithmeticOverloads() {
if (!HasArithmeticOrEnumeralCandidateType)
return;
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;
case OO_Pipe:
case OO_LessLess:
case OO_GreaterGreater:
- OpBuilder.addBinaryBitwiseArithmeticOverloads(Op);
+ OpBuilder.addBinaryBitwiseArithmeticOverloads();
break;
case OO_Amp: // '&' is either unary or binary
// operator '->', the built-in candidates set is empty.
break;
- OpBuilder.addBinaryBitwiseArithmeticOverloads(Op);
+ OpBuilder.addBinaryBitwiseArithmeticOverloads();
break;
case OO_Tilde:
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 {
};
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();
}
-// 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) {
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;
}
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::*)()'.
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 '>'}}
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
-// 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
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);
}
}
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 '> ='}}
}
}
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}}
}
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::*);
(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() {
--- /dev/null
+// 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}}
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}}
#include <assert.h>
#include <dlfcn.h>
+#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
// 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