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