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, libcpp-no-concepts 10 // ADDITIONAL_COMPILE_FLAGS: -Wno-sign-compare 11 12 // constexpr auto synth-three-way = ...; 13 // via std::tuple<T>(t) <=> std::tuple<U>(u), which exposes its behavior most directly 14 15 #include <cassert> 16 #include <compare> 17 #include <limits> // quiet_NaN 18 #include <tuple> 19 #include <utility> // declval 20 21 #include "test_macros.h" 22 23 template <typename T, typename U = T> 24 concept can_synth_three_way = requires(T t, U u) { std::tuple<T>(t) <=> std::tuple<U>(u); }; 25 26 template <typename T, typename U> 27 constexpr auto synth_three_way(const T& t, const U& u) { 28 return std::tuple<T>(t) <=> std::tuple<U>(u); 29 } 30 31 template <typename T, typename U> 32 using synth_three_way_result = decltype(std::declval<std::tuple<T>>() <=> std::declval<std::tuple<U>>()); 33 34 // A custom three-way result type 35 struct CustomEquality { 36 friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; } 37 friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; } 38 friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; } 39 }; 40 41 constexpr bool test() { 42 { 43 assert(synth_three_way(1, 1) == std::strong_ordering::equal); 44 assert(synth_three_way(2, 1) == std::strong_ordering::greater); 45 assert(synth_three_way(1, 2) == std::strong_ordering::less); 46 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<int, int>); 47 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<short, long long int>); 48 } 49 { 50 constexpr double nan = std::numeric_limits<double>::quiet_NaN(); 51 assert(synth_three_way(1.0, 1.0) == std::partial_ordering::equivalent); 52 assert(synth_three_way(2.0, 1.0) == std::partial_ordering::greater); 53 assert(synth_three_way(1.0, 2.0) == std::partial_ordering::less); 54 assert(synth_three_way(nan, nan) == std::partial_ordering::unordered); 55 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, double>); 56 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, float>); 57 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, int>); 58 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<float, short>); 59 } 60 { 61 struct StrongSpaceship { 62 int value; 63 constexpr bool operator==(const StrongSpaceship&) const = default; 64 constexpr std::strong_ordering operator<=>(const StrongSpaceship& other) const { return value <=> other.value; } 65 }; 66 assert(synth_three_way(StrongSpaceship{1}, StrongSpaceship{1}) == std::strong_ordering::equal); 67 assert(synth_three_way(StrongSpaceship{2}, StrongSpaceship{1}) == std::strong_ordering::greater); 68 assert(synth_three_way(StrongSpaceship{1}, StrongSpaceship{2}) == std::strong_ordering::less); 69 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<StrongSpaceship, StrongSpaceship>); 70 } 71 { 72 struct WeakSpaceship { 73 int value; 74 constexpr bool operator==(const WeakSpaceship&) const = default; 75 constexpr std::weak_ordering operator<=>(const WeakSpaceship& other) const { 76 return value <=> other.value; 77 } 78 }; 79 assert(synth_three_way(WeakSpaceship{1}, WeakSpaceship{1}) == std::weak_ordering::equivalent); 80 assert(synth_three_way(WeakSpaceship{2}, WeakSpaceship{1}) == std::weak_ordering::greater); 81 assert(synth_three_way(WeakSpaceship{1}, WeakSpaceship{2}) == std::weak_ordering::less); 82 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<WeakSpaceship, WeakSpaceship>); 83 } 84 { 85 struct PartialSpaceship { 86 double value; 87 constexpr bool operator==(const PartialSpaceship&) const = default; 88 constexpr std::partial_ordering operator<=>(const PartialSpaceship& other) const { 89 return value <=> other.value; 90 } 91 }; 92 constexpr double nan = std::numeric_limits<double>::quiet_NaN(); 93 assert(synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{1.0}) == std::partial_ordering::equivalent); 94 assert(synth_three_way(PartialSpaceship{2.0}, PartialSpaceship{1.0}) == std::partial_ordering::greater); 95 assert(synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{2.0}) == std::partial_ordering::less); 96 assert(synth_three_way(PartialSpaceship{nan}, PartialSpaceship{nan}) == std::partial_ordering::unordered); 97 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<PartialSpaceship, PartialSpaceship>); 98 } 99 { 100 struct NoSpaceship { 101 int value; 102 constexpr bool operator==(const NoSpaceship&) const = default; 103 constexpr bool operator<(const NoSpaceship& other) const { return value < other.value; } 104 }; 105 assert(synth_three_way(NoSpaceship{1}, NoSpaceship{1}) == std::weak_ordering::equivalent); 106 assert(synth_three_way(NoSpaceship{2}, NoSpaceship{1}) == std::weak_ordering::greater); 107 assert(synth_three_way(NoSpaceship{1}, NoSpaceship{2}) == std::weak_ordering::less); 108 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<NoSpaceship, NoSpaceship>); 109 } 110 { 111 // Types with operator<=> but no operator== are not three_way_comparable and will fall back to operator< and 112 // compare as weakly ordered. 113 struct SpaceshipNoEquals { 114 constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const { 115 return std::strong_ordering::equivalent; 116 } 117 }; 118 assert(synth_three_way(SpaceshipNoEquals{}, SpaceshipNoEquals{}) == std::weak_ordering::equivalent); 119 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<SpaceshipNoEquals, SpaceshipNoEquals>); 120 } 121 { 122 // Custom three-way-comparison result types cannot satisfy standard concepts (and therefore synth-three-way) 123 // because they are not understood by std::common_comparison_category, but they can still be used in 124 // the same way as standard orderings to do comparisons, and thus can be used by synth-three-way to yield a 125 // weakly-ordered result. 126 struct CustomSpaceship { 127 constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); } 128 }; 129 assert((CustomSpaceship{} <=> CustomSpaceship{}) == 0); 130 assert(!(CustomSpaceship{} < CustomSpaceship{})); 131 assert(synth_three_way(CustomSpaceship{}, CustomSpaceship{}) == std::weak_ordering::equivalent); 132 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<CustomSpaceship, CustomSpaceship>); 133 } 134 // SFINAE tests demonstrating synth-three-way needs three_way_comparable or operator<. 135 { 136 struct NoRelative { 137 constexpr bool operator==(const NoRelative&) const; 138 }; 139 static_assert(!can_synth_three_way<NoRelative>); 140 } 141 { 142 struct NoLessThan { 143 constexpr bool operator==(const NoLessThan&) const; 144 constexpr bool operator>(const NoLessThan&) const; 145 constexpr bool operator>=(const NoLessThan&) const; 146 constexpr bool operator<=(const NoLessThan&) const; 147 }; 148 static_assert(!can_synth_three_way<NoLessThan>); 149 } 150 { 151 assert(synth_three_way(1, 1U) == std::weak_ordering::equivalent); 152 assert(synth_three_way(-1, 0U) == std::weak_ordering::greater); 153 // Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator 154 // between signed and unsigned types, so we should end up with a synthesized weak ordering. 155 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<int, unsigned int>); 156 // When an unsigned type can be narrowed to a larger signed type, <=> should be defined and we 157 // should get a strong ordering. (This probably does not raise a warning due to safe narrowing.) 158 assert(synth_three_way(static_cast<long long int>(-1), static_cast<unsigned char>(0)) == std::strong_ordering::less); 159 assert(synth_three_way(static_cast<long long int>(-1), static_cast<unsigned char>(0)) == std::strong_ordering::less); 160 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<long long int, unsigned char>); 161 } 162 #ifdef TEST_COMPILER_GCC 163 // GCC cannot evaluate NaN @ non-NaN constexpr, so test that runtime-only. 164 if (!std::is_constant_evaluated()) 165 #endif 166 { 167 constexpr double nan = std::numeric_limits<double>::quiet_NaN(); 168 assert(synth_three_way(nan, 1.0) == std::partial_ordering::unordered); 169 } 170 171 return true; 172 } 173 174 int main(int, char**) { 175 test(); 176 static_assert(test()); 177 178 return 0; 179 } 180