| // RUN: %clang_cc1 -std=c++2a -verify %s |
| |
| namespace std { |
| class strong_ordering { |
| int n; |
| constexpr strong_ordering(int n) : n(n) {} |
| public: |
| static const strong_ordering less, equal, greater; |
| bool operator!=(int) { return n != 0; } |
| }; |
| constexpr strong_ordering strong_ordering::less{-1}, |
| strong_ordering::equal{0}, strong_ordering::greater{1}; |
| |
| class weak_ordering { |
| int n; |
| constexpr weak_ordering(int n) : n(n) {} |
| public: |
| constexpr weak_ordering(strong_ordering o); |
| static const weak_ordering less, equivalent, greater; |
| bool operator!=(int) { return n != 0; } |
| }; |
| constexpr weak_ordering weak_ordering::less{-1}, |
| weak_ordering::equivalent{0}, weak_ordering::greater{1}; |
| |
| class partial_ordering { |
| int n; |
| constexpr partial_ordering(int n) : n(n) {} |
| public: |
| constexpr partial_ordering(strong_ordering o); |
| constexpr partial_ordering(weak_ordering o); |
| static const partial_ordering less, equivalent, greater, unordered; |
| bool operator!=(int) { return n != 0; } |
| }; |
| constexpr partial_ordering partial_ordering::less{-1}, |
| partial_ordering::equivalent{0}, partial_ordering::greater{1}, |
| partial_ordering::unordered{2}; |
| } |
| |
| namespace DeducedNotCat { |
| struct A { |
| A operator<=>(const A&) const; // expected-note {{selected 'operator<=>' for member 'a' declared here}} |
| }; |
| struct B { |
| A a; // expected-note {{return type 'DeducedNotCat::A' of three-way comparison for member 'a' is not a standard comparison category type}} |
| auto operator<=>(const B&) const = default; // expected-warning {{implicitly deleted}} |
| }; |
| } |
| |
| namespace DeducedVsSynthesized { |
| struct A { |
| bool operator==(const A&) const; |
| bool operator<(const A&) const; |
| }; |
| struct B { |
| A a; // expected-note {{no viable three-way comparison function for member 'a'}} |
| auto operator<=>(const B&) const = default; // expected-warning {{implicitly deleted}} |
| }; |
| } |
| |
| namespace Deduction { |
| template<typename T> struct wrap { |
| T t; |
| friend auto operator<=>(const wrap&, const wrap&) = default; |
| }; |
| |
| using strong = wrap<int>; |
| using strong2 = wrap<int*>; |
| struct weak { |
| friend std::weak_ordering operator<=>(weak, weak); |
| }; |
| using partial = wrap<float>; |
| |
| template<typename ...T> struct A : T... { |
| friend auto operator<=>(const A&, const A&) = default; |
| }; |
| |
| template<typename Expected, typename ...Ts> void f() { |
| using T = Expected; // expected-note {{previous}} |
| using T = decltype(A<Ts...>() <=> A<Ts...>()); // expected-error {{different type}} |
| void(A<Ts...>() <=> A<Ts...>()); // trigger synthesis of body |
| } |
| |
| template void f<std::strong_ordering>(); |
| template void f<std::strong_ordering, strong>(); |
| template void f<std::strong_ordering, strong, strong2>(); |
| |
| template void f<std::weak_ordering, weak>(); |
| template void f<std::weak_ordering, weak, strong>(); |
| template void f<std::weak_ordering, strong, weak>(); |
| |
| template void f<std::partial_ordering, partial>(); |
| template void f<std::partial_ordering, weak, partial>(); |
| template void f<std::partial_ordering, strong, partial>(); |
| template void f<std::partial_ordering, partial, weak>(); |
| template void f<std::partial_ordering, partial, strong>(); |
| template void f<std::partial_ordering, weak, partial, strong>(); |
| |
| // Check that the above mechanism works. |
| template void f<std::strong_ordering, weak>(); // expected-note {{instantiation of}} |
| |
| std::strong_ordering x = A<strong>() <=> A<strong>(); |
| } |
| |
| namespace PR44723 { |
| // Make sure we trigger return type deduction for a callee 'operator<=>' |
| // before inspecting its return type. |
| template<int> struct a { |
| friend constexpr auto operator<=>(a const &lhs, a const &rhs) { |
| return std::strong_ordering::equal; |
| } |
| }; |
| struct b { |
| friend constexpr auto operator<=>(b const &, b const &) = default; |
| a<0> m_value; |
| }; |
| std::strong_ordering cmp_b = b() <=> b(); |
| |
| struct c { |
| auto operator<=>(const c&) const&; // expected-note {{selected 'operator<=>' for base class 'c' declared here}} |
| }; |
| struct d : c { // expected-note {{base class 'c' declared here}} |
| friend auto operator<=>(const d&, const d&) = default; // #d |
| // expected-error@#d {{return type of defaulted 'operator<=>' cannot be deduced because three-way comparison for base class 'c' has a deduced return type and is not yet defined}} |
| // expected-warning@#d {{implicitly deleted}} |
| }; |
| auto c::operator<=>(const c&) const& { // #c |
| return std::strong_ordering::equal; |
| } |
| // expected-error@+1 {{overload resolution selected deleted operator '<=>'}} |
| std::strong_ordering cmp_d = d() <=> d(); |
| // expected-note@#c 2{{candidate}} |
| // expected-note@#d {{candidate function has been implicitly deleted}} |
| } |
| |
| namespace BadDeducedType { |
| struct A { |
| // expected-error@+1 {{deduced return type for defaulted three-way comparison operator must be 'auto', not 'auto &'}} |
| friend auto &operator<=>(const A&, const A&) = default; |
| }; |
| |
| struct B { |
| // expected-error@+1 {{deduced return type for defaulted three-way comparison operator must be 'auto', not 'const auto'}} |
| friend const auto operator<=>(const B&, const B&) = default; |
| }; |
| |
| template<typename T> struct X {}; // expected-note {{here}} |
| struct C { |
| // expected-error@+1 {{deduction not allowed in function return type}} |
| friend X operator<=>(const C&, const C&) = default; |
| }; |
| |
| template<typename T> concept CmpCat = true; |
| struct D { |
| // expected-error@+1 {{deduced return type for defaulted three-way comparison operator must be 'auto', not 'CmpCat auto'}} |
| friend CmpCat auto operator<=>(const D&, const D&) = default; |
| }; |
| } |
| |
| namespace PR48856 { |
| struct A { |
| auto operator<=>(const A &) const = default; // expected-warning {{implicitly deleted}} |
| void (*x)(); // expected-note {{because there is no viable three-way comparison function for member 'x'}} |
| }; |
| |
| struct B { |
| auto operator<=>(const B &) const = default; // expected-warning {{implicitly deleted}} |
| void (B::*x)(); // expected-note {{because there is no viable three-way comparison function for member 'x'}} |
| }; |
| |
| struct C { |
| auto operator<=>(const C &) const = default; // expected-warning {{implicitly deleted}} |
| int C::*x; // expected-note {{because there is no viable three-way comparison function for member 'x'}} |
| }; |
| } |
| |
| namespace PR50591 { |
| struct a1 { |
| operator int() const; |
| }; |
| struct b1 { |
| auto operator<=>(b1 const &) const = default; |
| a1 f; |
| }; |
| std::strong_ordering cmp_b1 = b1() <=> b1(); |
| |
| struct a2 { |
| operator float() const; |
| }; |
| struct b2 { |
| auto operator<=>(b2 const &) const = default; |
| a2 f; |
| }; |
| std::partial_ordering cmp_b2 = b2() <=> b2(); |
| |
| using fp = void (*)(); |
| |
| struct a3 { |
| operator fp() const; |
| }; |
| struct b3 { |
| auto operator<=>(b3 const &) const = default; // expected-warning {{implicitly deleted}} |
| 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(); |
| } |