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