xref: /llvm-project/libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp (revision 557f7e1398e13c0957c7a0cbc013c1468f47a237)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // <compare>
12 
13 // template<class T> constexpr partial_ordering compare_partial_order_fallback(const T& a, const T& b);
14 
15 #include <compare>
16 
17 #include <cassert>
18 #include <cmath>
19 #include <iterator> // std::size
20 #include <limits>
21 #include <type_traits>
22 #include <utility>
23 
24 #include "test_macros.h"
25 
26 template<class T, class U>
27 constexpr auto has_partial_order(T&& t, U&& u)
28     -> decltype(std::compare_partial_order_fallback(static_cast<T&&>(t), static_cast<U&&>(u)), true)
29 {
30     return true;
31 }
32 
33 constexpr bool has_partial_order(...) {
34     return false;
35 }
36 
37 namespace N11 {
38     struct A {};
39     struct B {};
40     std::strong_ordering partial_order(const A&, const A&) { return std::strong_ordering::less; }
41     std::strong_ordering partial_order(const A&, const B&);
42 }
43 
44 void test_1_1()
45 {
46     // If the decayed types of E and F differ, partial_order(E, F) is ill-formed.
47 
48     static_assert( has_partial_order(1, 2));
49     static_assert(!has_partial_order(1, (short)2));
50     static_assert(!has_partial_order(1, 2.0));
51     static_assert(!has_partial_order(1.0f, 2.0));
52 
53     static_assert( has_partial_order((int*)nullptr, (int*)nullptr));
54     static_assert(!has_partial_order((int*)nullptr, (const int*)nullptr));
55     static_assert(!has_partial_order((const int*)nullptr, (int*)nullptr));
56     static_assert( has_partial_order((const int*)nullptr, (const int*)nullptr));
57 
58     N11::A a;
59     N11::B b;
60     static_assert( has_partial_order(a, a));
61     static_assert(!has_partial_order(a, b));
62 }
63 
64 namespace N12 {
65     struct A {};
66     std::strong_ordering partial_order(A&, A&&) { return std::strong_ordering::less; }
67     std::weak_ordering partial_order(A&&, A&&) { return std::weak_ordering::equivalent; }
68     std::strong_ordering partial_order(const A&, const A&);
69 
70     struct B {
71         friend int partial_order(B, B);
72     };
73 
74     struct PartialOrder {
75         explicit operator std::partial_ordering() const { return std::partial_ordering::less; }
76     };
77     struct C {
78         bool touched = false;
79         friend PartialOrder partial_order(C& lhs, C&) { lhs.touched = true; return PartialOrder(); }
80     };
81 }
82 
83 void test_1_2()
84 {
85     // Otherwise, partial_ordering(partial_order(E, F))
86     // if it is a well-formed expression with overload resolution performed
87     // in a context that does not include a declaration of std::partial_order.
88 
89     // Test that partial_order does not const-qualify the forwarded arguments.
90     N12::A a;
91     assert(std::compare_partial_order_fallback(a, std::move(a)) == std::partial_ordering::less);
92     assert(std::compare_partial_order_fallback(std::move(a), std::move(a)) == std::partial_ordering::equivalent);
93 
94     // The type of partial_order(e,f) must be explicitly convertible to partial_ordering.
95     N12::B b;
96     static_assert(!has_partial_order(b, b));
97 
98     N12::C c1, c2;
99     ASSERT_SAME_TYPE(decltype(std::compare_partial_order_fallback(c1, c2)), std::partial_ordering);
100     assert(std::partial_order(c1, c2) == std::partial_ordering::less);
101     assert(c1.touched);
102     assert(!c2.touched);
103 }
104 
105 namespace N13 {
106     // Compare to N12::A.
107     struct A {};
108     bool operator==(const A&, const A&);
109     constexpr std::partial_ordering operator<=>(A&, A&&) { return std::partial_ordering::less; }
110     constexpr std::partial_ordering operator<=>(A&&, A&&) { return std::partial_ordering::equivalent; }
111     std::partial_ordering operator<=>(const A&, const A&);
112     static_assert(std::three_way_comparable<A>);
113 
114     struct B {
115         std::partial_ordering operator<=>(const B&) const;  // lacks operator==
116     };
117     static_assert(!std::three_way_comparable<B>);
118 
119     struct C {
120         bool *touched;
121         bool operator==(const C&) const;
122         constexpr std::partial_ordering operator<=>(const C& rhs) const {
123             *rhs.touched = true;
124             return std::partial_ordering::equivalent;
125         }
126     };
127     static_assert(std::three_way_comparable<C>);
128 }
129 
130 constexpr bool test_1_3()
131 {
132     // Otherwise, partial_ordering(compare_three_way()(E, F)) if it is a well-formed expression.
133 
134     // Test neither partial_order nor compare_three_way const-qualify the forwarded arguments.
135     N13::A a;
136     assert(std::compare_partial_order_fallback(a, std::move(a)) == std::partial_ordering::less);
137     assert(std::compare_partial_order_fallback(std::move(a), std::move(a)) == std::partial_ordering::equivalent);
138 
139     N13::B b;
140     static_assert(!has_partial_order(b, b));
141 
142     // Test that the arguments are passed to <=> in the correct order.
143     bool c1_touched = false;
144     bool c2_touched = false;
145     N13::C c1 = {&c1_touched};
146     N13::C c2 = {&c2_touched};
147     assert(std::compare_partial_order_fallback(c1, c2) == std::partial_ordering::equivalent);
148     assert(!c1_touched);
149     assert(c2_touched);
150 
151     // For partial_order, this bullet point takes care of floating-point types;
152     // they receive their natural partial order.
153     {
154         using F = float;
155         F nan = std::numeric_limits<F>::quiet_NaN();
156         assert(std::compare_partial_order_fallback(F(1), F(2)) == std::partial_ordering::less);
157         assert(std::compare_partial_order_fallback(F(0), -F(0)) == std::partial_ordering::equivalent);
158 #ifndef TEST_COMPILER_GCC  // GCC can't compare NaN to non-NaN in a constant-expression
159         assert(std::compare_partial_order_fallback(nan, F(1)) == std::partial_ordering::unordered);
160 #endif
161         assert(std::compare_partial_order_fallback(nan, nan) == std::partial_ordering::unordered);
162     }
163     {
164         using F = double;
165         F nan = std::numeric_limits<F>::quiet_NaN();
166         assert(std::compare_partial_order_fallback(F(1), F(2)) == std::partial_ordering::less);
167         assert(std::compare_partial_order_fallback(F(0), -F(0)) == std::partial_ordering::equivalent);
168 #ifndef TEST_COMPILER_GCC
169         assert(std::compare_partial_order_fallback(nan, F(1)) == std::partial_ordering::unordered);
170 #endif
171         assert(std::compare_partial_order_fallback(nan, nan) == std::partial_ordering::unordered);
172     }
173     {
174         using F = long double;
175         F nan = std::numeric_limits<F>::quiet_NaN();
176         assert(std::compare_partial_order_fallback(F(1), F(2)) == std::partial_ordering::less);
177         assert(std::compare_partial_order_fallback(F(0), -F(0)) == std::partial_ordering::equivalent);
178 #ifndef TEST_COMPILER_GCC
179         assert(std::compare_partial_order_fallback(nan, F(1)) == std::partial_ordering::unordered);
180 #endif
181         assert(std::compare_partial_order_fallback(nan, nan) == std::partial_ordering::unordered);
182     }
183 
184     return true;
185 }
186 
187 namespace N14 {
188     struct A {};
189     constexpr std::strong_ordering weak_order(A&, A&&) { return std::strong_ordering::less; }
190     constexpr std::strong_ordering weak_order(A&&, A&&) { return std::strong_ordering::equal; }
191     std::strong_ordering weak_order(const A&, const A&);
192 
193     struct B {
194         friend std::partial_ordering weak_order(B, B);
195     };
196 
197     struct StrongOrder {
198         operator std::strong_ordering() const { return std::strong_ordering::less; }
199     };
200     struct C {
201         friend StrongOrder weak_order(C& lhs, C&);
202     };
203 
204     struct WeakOrder {
205         constexpr explicit operator std::weak_ordering() const { return std::weak_ordering::less; }
206         operator std::partial_ordering() const = delete;
207     };
208     struct D {
209         bool touched = false;
210         friend constexpr WeakOrder weak_order(D& lhs, D&) { lhs.touched = true; return WeakOrder(); }
211     };
212 }
213 
214 constexpr bool test_1_4()
215 {
216     // Otherwise, partial_ordering(weak_order(E, F)) [that is, std::weak_order]
217     // if it is a well-formed expression.
218 
219     // Test that partial_order and weak_order do not const-qualify the forwarded arguments.
220     N14::A a;
221     assert(std::compare_partial_order_fallback(a, std::move(a)) == std::partial_ordering::less);
222     assert(std::compare_partial_order_fallback(std::move(a), std::move(a)) == std::partial_ordering::equivalent);
223 
224     // The type of ADL weak_order(e,f) must be explicitly convertible to weak_ordering
225     // (not just to partial_ordering), or else std::weak_order(e,f) won't exist.
226     N14::B b;
227     static_assert(!has_partial_order(b, b));
228 
229     // The type of ADL weak_order(e,f) must be explicitly convertible to weak_ordering
230     // (not just to strong_ordering), or else std::weak_order(e,f) won't exist.
231     N14::C c;
232     static_assert(!has_partial_order(c, c));
233 
234     N14::D d1, d2;
235     ASSERT_SAME_TYPE(decltype(std::compare_partial_order_fallback(d1, d2)), std::partial_ordering);
236     assert(std::compare_partial_order_fallback(d1, d2) == std::partial_ordering::less);
237     assert(d1.touched);
238     assert(!d2.touched);
239 
240     return true;
241 }
242 
243 namespace N2 {
244     struct Stats {
245         int eq = 0;
246         int lt = 0;
247     };
248     struct A {
249         Stats *stats_;
250         double value_;
251         constexpr explicit A(Stats *stats, double value) : stats_(stats), value_(value) {}
252         friend constexpr bool operator==(A a, A b) { a.stats_->eq += 1; return a.value_ == b.value_; }
253         friend constexpr bool operator<(A a, A b) { a.stats_->lt += 1; return a.value_ < b.value_; }
254     };
255     struct NoEquality {
256         friend bool operator<(NoEquality, NoEquality);
257     };
258     struct VC1 {
259         // Deliberately asymmetric `const` qualifiers here.
260         friend bool operator==(const VC1&, VC1&);
261         friend bool operator<(const VC1&, VC1&);
262     };
263     struct VC2 {
264         // Deliberately asymmetric `const` qualifiers here.
265         friend bool operator==(const VC2&, VC2&);
266         friend bool operator==(VC2&, const VC2&) = delete;
267         friend bool operator<(const VC2&, VC2&);
268         friend bool operator<(VC2&, const VC2&);
269     };
270 
271     enum class comparison_result_kind : bool {
272       convertible_bool,
273       boolean_testable,
274     };
275 
276     template <comparison_result_kind K>
277     struct comparison_result {
278       bool value;
279 
280       constexpr operator bool() const noexcept { return value; }
281 
282       constexpr auto operator!() const noexcept {
283         if constexpr (K == comparison_result_kind::boolean_testable) {
284           return comparison_result{!value};
285         }
286       }
287     };
288 
289     template <comparison_result_kind EqKind, comparison_result_kind LeKind>
290     struct boolean_tested_type {
291       friend constexpr comparison_result<EqKind> operator==(boolean_tested_type, boolean_tested_type) noexcept {
292         return comparison_result<EqKind>{true};
293       }
294 
295       friend constexpr comparison_result<LeKind> operator<(boolean_tested_type, boolean_tested_type) noexcept {
296         return comparison_result<LeKind>{false};
297       }
298     };
299 
300     using test_only_convertible =
301         boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::convertible_bool>;
302     using test_eq_boolean_testable =
303         boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::convertible_bool>;
304     using test_le_boolean_testable =
305         boolean_tested_type<comparison_result_kind::convertible_bool, comparison_result_kind::boolean_testable>;
306     using test_boolean_testable =
307         boolean_tested_type<comparison_result_kind::boolean_testable, comparison_result_kind::boolean_testable>;
308 }
309 
310 constexpr bool test_2()
311 {
312     {
313         N2::Stats stats;
314         N2::Stats bstats;
315         assert(std::compare_partial_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 1)) == std::partial_ordering::equivalent);
316         assert(stats.eq == 1 && stats.lt == 0);
317         stats = {};
318         assert(std::compare_partial_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 2)) == std::partial_ordering::less);
319         assert(stats.eq == 1 && stats.lt == 1);
320         stats = {};
321         assert(std::compare_partial_order_fallback(N2::A(&stats, 2), N2::A(&bstats, 1)) == std::partial_ordering::greater);
322         assert(stats.eq == 1 && stats.lt == 1 && bstats.lt == 1);
323         stats = {};
324         bstats = {};
325         double nan = std::numeric_limits<double>::quiet_NaN();
326         assert(std::compare_partial_order_fallback(N2::A(&stats, nan), N2::A(&bstats, nan)) == std::partial_ordering::unordered);
327         assert(stats.eq == 1 && stats.lt == 1 && bstats.lt == 1);
328     }
329     {
330         N2::NoEquality ne;
331         assert(!has_partial_order(ne, ne));
332     }
333     {
334         // LWG3465: (cvc < vc) is well-formed, (vc < cvc) is not. Substitution failure.
335         N2::VC1 vc;
336         const N2::VC1 cvc;
337         assert(!has_partial_order(cvc, vc));
338         assert(!has_partial_order(vc, cvc));
339     }
340     {
341         // LWG3465: (cvc == vc) is well-formed, (vc == cvc) is not. That's fine.
342         N2::VC2 vc;
343         const N2::VC2 cvc;
344         assert( has_partial_order(cvc, vc));
345         assert(!has_partial_order(vc, cvc));
346     }
347     {
348       // P2167R3 as modified by the intent of LWG3465:
349       //   All of decltype(e == f), decltype(e < f), and decltype(f < e) need to be well-formed and boolean-testable.
350       N2::test_only_convertible tc;
351       N2::test_eq_boolean_testable teq;
352       N2::test_le_boolean_testable tle;
353       N2::test_boolean_testable tbt;
354 
355       assert(!has_partial_order(tc, tc));
356       assert(!has_partial_order(teq, teq));
357       assert(!has_partial_order(tle, tle));
358       assert(has_partial_order(tbt, tbt));
359 
360       assert(std::compare_partial_order_fallback(tbt, tbt) == std::partial_ordering::equivalent);
361     }
362     return true;
363 }
364 
365 int main(int, char**)
366 {
367     test_1_1();
368     test_1_2();
369     test_1_3();
370     test_1_4();
371     test_2();
372 
373     static_assert(test_1_3());
374     static_assert(test_1_4());
375     static_assert(test_2());
376 
377     return 0;
378 }
379