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 = {.touched: &c1_touched}; |
146 | N13::C c2 = {.touched: &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 | |
272 | constexpr bool test_2() |
273 | { |
274 | { |
275 | N2::Stats stats; |
276 | N2::Stats bstats; |
277 | assert(std::compare_partial_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 1)) == std::partial_ordering::equivalent); |
278 | assert(stats.eq == 1 && stats.lt == 0); |
279 | stats = {}; |
280 | assert(std::compare_partial_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 2)) == std::partial_ordering::less); |
281 | assert(stats.eq == 1 && stats.lt == 1); |
282 | stats = {}; |
283 | assert(std::compare_partial_order_fallback(N2::A(&stats, 2), N2::A(&bstats, 1)) == std::partial_ordering::greater); |
284 | assert(stats.eq == 1 && stats.lt == 1 && bstats.lt == 1); |
285 | stats = {}; |
286 | bstats = {}; |
287 | double nan = std::numeric_limits<double>::quiet_NaN(); |
288 | assert(std::compare_partial_order_fallback(N2::A(&stats, nan), N2::A(&bstats, nan)) == std::partial_ordering::unordered); |
289 | assert(stats.eq == 1 && stats.lt == 1 && bstats.lt == 1); |
290 | } |
291 | { |
292 | N2::NoEquality ne; |
293 | assert(!has_partial_order(ne, ne)); |
294 | } |
295 | { |
296 | // LWG3465: (cvc < vc) is well-formed, (vc < cvc) is not. Substitution failure. |
297 | N2::VC1 vc; |
298 | const N2::VC1 cvc; |
299 | assert(!has_partial_order(cvc, vc)); |
300 | assert(!has_partial_order(vc, cvc)); |
301 | } |
302 | { |
303 | // LWG3465: (cvc == vc) is well-formed, (vc == cvc) is not. That's fine. |
304 | N2::VC2 vc; |
305 | const N2::VC2 cvc; |
306 | assert( has_partial_order(cvc, vc)); |
307 | assert(!has_partial_order(vc, cvc)); |
308 | } |
309 | return true; |
310 | } |
311 | |
312 | int main(int, char**) |
313 | { |
314 | test_1_1(); |
315 | test_1_2(); |
316 | test_1_3(); |
317 | test_1_4(); |
318 | test_2(); |
319 | |
320 | static_assert(test_1_3()); |
321 | static_assert(test_1_4()); |
322 | static_assert(test_2()); |
323 | |
324 | return 0; |
325 | } |
326 | |