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
26template<class T, class U>
27constexpr 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
33constexpr bool has_partial_order(...) {
34 return false;
35}
36
37namespace 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
44void 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
64namespace 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
83void 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
105namespace 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
130constexpr 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
187namespace 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
214constexpr 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
243namespace 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
272constexpr 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
312int 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

source code of libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp