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 // A set of routines for testing the comparison operators of a type 9 // 10 // FooOrder<expected-ordering> All seven comparison operators, requires C++20 or newer. 11 // FooComparison All six pre-C++20 comparison operators 12 // FooEquality Equality operators operator== and operator!= 13 // 14 // AssertXAreNoexcept static_asserts that the operations are all noexcept. 15 // AssertXReturnBool static_asserts that the operations return bool. 16 // AssertOrderReturn static_asserts that the pre-C++20 comparison operations 17 // return bool and operator<=> returns the proper type. 18 // AssertXConvertibleToBool static_asserts that the operations return something convertible to bool. 19 // testXValues returns the result of the comparison of all operations. 20 // 21 // AssertOrderConvertibleToBool doesn't exist yet. It will be implemented when needed. 22 23 #ifndef TEST_COMPARISONS_H 24 #define TEST_COMPARISONS_H 25 26 #include <cassert> 27 #include <compare> 28 #include <concepts> 29 #include <limits> 30 #include <type_traits> 31 #include <utility> 32 33 #include "test_macros.h" 34 35 // Test the consistency of the six basic comparison operators for values that are ordered or unordered. 36 template <class T, class U = T> 37 TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool 38 testComparisonsComplete(const T& t1, const U& t2, bool isEqual, bool isLess, bool isGreater) { 39 assert(((isEqual ? 1 : 0) + (isLess ? 1 : 0) + (isGreater ? 1 : 0) <= 1) && 40 "at most one of isEqual, isLess, and isGreater can be true"); 41 if (isEqual) { 42 if (!(t1 == t2)) return false; 43 if (!(t2 == t1)) return false; 44 if ( (t1 != t2)) return false; 45 if ( (t2 != t1)) return false; 46 if ( (t1 < t2)) return false; 47 if ( (t2 < t1)) return false; 48 if (!(t1 <= t2)) return false; 49 if (!(t2 <= t1)) return false; 50 if ( (t1 > t2)) return false; 51 if ( (t2 > t1)) return false; 52 if (!(t1 >= t2)) return false; 53 if (!(t2 >= t1)) return false; 54 } else if (isLess) { 55 if ( (t1 == t2)) return false; 56 if ( (t2 == t1)) return false; 57 if (!(t1 != t2)) return false; 58 if (!(t2 != t1)) return false; 59 if (!(t1 < t2)) return false; 60 if ( (t2 < t1)) return false; 61 if (!(t1 <= t2)) return false; 62 if ( (t2 <= t1)) return false; 63 if ( (t1 > t2)) return false; 64 if (!(t2 > t1)) return false; 65 if ( (t1 >= t2)) return false; 66 if (!(t2 >= t1)) return false; 67 } else if (isGreater) { 68 if ( (t1 == t2)) return false; 69 if ( (t2 == t1)) return false; 70 if (!(t1 != t2)) return false; 71 if (!(t2 != t1)) return false; 72 if ( (t1 < t2)) return false; 73 if (!(t2 < t1)) return false; 74 if ( (t1 <= t2)) return false; 75 if (!(t2 <= t1)) return false; 76 if (!(t1 > t2)) return false; 77 if ( (t2 > t1)) return false; 78 if (!(t1 >= t2)) return false; 79 if ( (t2 >= t1)) return false; 80 } else { // unordered 81 if ( (t1 == t2)) return false; 82 if ( (t2 == t1)) return false; 83 if (!(t1 != t2)) return false; 84 if (!(t2 != t1)) return false; 85 if ( (t1 < t2)) return false; 86 if ( (t2 < t1)) return false; 87 if ( (t1 <= t2)) return false; 88 if ( (t2 <= t1)) return false; 89 if ( (t1 > t2)) return false; 90 if ( (t2 > t1)) return false; 91 if ( (t1 >= t2)) return false; 92 if ( (t2 >= t1)) return false; 93 } 94 95 return true; 96 } 97 98 // Test the six basic comparison operators for ordered values. 99 template <class T, class U = T> 100 TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testComparisons(const T& t1, const U& t2, bool isEqual, bool isLess) { 101 assert(!(isEqual && isLess) && "isEqual and isLess cannot be both true"); 102 bool isGreater = !isEqual && !isLess; 103 return testComparisonsComplete(t1, t2, isEqual, isLess, isGreater); 104 } 105 106 // Easy call when you can init from something already comparable. 107 template <class T, class Param> 108 TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testComparisonsValues(Param val1, Param val2) 109 { 110 const bool isEqual = val1 == val2; 111 const bool isLess = val1 < val2; 112 const bool isGreater = val1 > val2; 113 114 return testComparisonsComplete(T(val1), T(val2), isEqual, isLess, isGreater); 115 } 116 117 template <class T, class U = T> 118 TEST_CONSTEXPR_CXX14 void AssertComparisonsAreNoexcept() { 119 ASSERT_NOEXCEPT(std::declval<const T&>() == std::declval<const U&>()); 120 ASSERT_NOEXCEPT(std::declval<const T&>() != std::declval<const U&>()); 121 ASSERT_NOEXCEPT(std::declval<const T&>() < std::declval<const U&>()); 122 ASSERT_NOEXCEPT(std::declval<const T&>() <= std::declval<const U&>()); 123 ASSERT_NOEXCEPT(std::declval<const T&>() > std::declval<const U&>()); 124 ASSERT_NOEXCEPT(std::declval<const T&>() >= std::declval<const U&>()); 125 } 126 127 template <class T, class U = T> 128 TEST_CONSTEXPR_CXX14 void AssertComparisonsReturnBool() { 129 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool); 130 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool); 131 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() < std::declval<const U&>()), bool); 132 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <= std::declval<const U&>()), bool); 133 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() > std::declval<const U&>()), bool); 134 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() >= std::declval<const U&>()), bool); 135 } 136 137 template <class T, class U = T> 138 void AssertComparisonsConvertibleToBool() 139 { 140 static_assert((std::is_convertible<decltype(std::declval<const T&>() == std::declval<const U&>()), bool>::value), ""); 141 static_assert((std::is_convertible<decltype(std::declval<const T&>() != std::declval<const U&>()), bool>::value), ""); 142 static_assert((std::is_convertible<decltype(std::declval<const T&>() < std::declval<const U&>()), bool>::value), ""); 143 static_assert((std::is_convertible<decltype(std::declval<const T&>() <= std::declval<const U&>()), bool>::value), ""); 144 static_assert((std::is_convertible<decltype(std::declval<const T&>() > std::declval<const U&>()), bool>::value), ""); 145 static_assert((std::is_convertible<decltype(std::declval<const T&>() >= std::declval<const U&>()), bool>::value), ""); 146 } 147 148 #if TEST_STD_VER > 17 149 template <class T, class U = T> 150 constexpr void AssertOrderAreNoexcept() { 151 AssertComparisonsAreNoexcept<T, U>(); 152 ASSERT_NOEXCEPT(std::declval<const T&>() <=> std::declval<const U&>()); 153 } 154 155 template <class Order, class T, class U = T> 156 constexpr void AssertOrderReturn() { 157 AssertComparisonsReturnBool<T, U>(); 158 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <=> std::declval<const U&>()), Order); 159 } 160 161 template <class Order, class T, class U = T> 162 TEST_NODISCARD constexpr bool testOrder(const T& t1, const U& t2, Order order) { 163 bool equal = order == Order::equivalent; 164 bool less = order == Order::less; 165 bool greater = order == Order::greater; 166 167 return (t1 <=> t2 == order) && testComparisonsComplete(t1, t2, equal, less, greater); 168 } 169 170 template <class T, class Param> 171 TEST_NODISCARD constexpr bool testOrderValues(Param val1, Param val2) { 172 return testOrder(T(val1), T(val2), val1 <=> val2); 173 } 174 175 #endif 176 177 // Test all two comparison operations for sanity 178 template <class T, class U = T> 179 TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testEquality(const T& t1, const U& t2, bool isEqual) 180 { 181 if (isEqual) 182 { 183 if (!(t1 == t2)) return false; 184 if (!(t2 == t1)) return false; 185 if ( (t1 != t2)) return false; 186 if ( (t2 != t1)) return false; 187 } 188 else /* not equal */ 189 { 190 if ( (t1 == t2)) return false; 191 if ( (t2 == t1)) return false; 192 if (!(t1 != t2)) return false; 193 if (!(t2 != t1)) return false; 194 } 195 196 return true; 197 } 198 199 // Easy call when you can init from something already comparable. 200 template <class T, class Param> 201 TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testEqualityValues(Param val1, Param val2) 202 { 203 const bool isEqual = val1 == val2; 204 205 return testEquality(T(val1), T(val2), isEqual); 206 } 207 208 template <class T, class U = T> 209 void AssertEqualityAreNoexcept() 210 { 211 ASSERT_NOEXCEPT(std::declval<const T&>() == std::declval<const U&>()); 212 ASSERT_NOEXCEPT(std::declval<const T&>() != std::declval<const U&>()); 213 } 214 215 template <class T, class U = T> 216 void AssertEqualityReturnBool() 217 { 218 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool); 219 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool); 220 } 221 222 223 template <class T, class U = T> 224 void AssertEqualityConvertibleToBool() 225 { 226 static_assert((std::is_convertible<decltype(std::declval<const T&>() == std::declval<const U&>()), bool>::value), ""); 227 static_assert((std::is_convertible<decltype(std::declval<const T&>() != std::declval<const U&>()), bool>::value), ""); 228 } 229 230 struct LessAndEqComp { 231 int value; 232 233 TEST_CONSTEXPR_CXX14 LessAndEqComp(int v) : value(v) {} 234 235 friend TEST_CONSTEXPR_CXX14 bool operator<(const LessAndEqComp& lhs, const LessAndEqComp& rhs) { 236 return lhs.value < rhs.value; 237 } 238 239 friend TEST_CONSTEXPR_CXX14 bool operator==(const LessAndEqComp& lhs, const LessAndEqComp& rhs) { 240 return lhs.value == rhs.value; 241 } 242 }; 243 244 #if TEST_STD_VER >= 20 245 246 struct StrongOrder { 247 int value; 248 constexpr StrongOrder(int v) : value(v) {} 249 friend std::strong_ordering operator<=>(StrongOrder, StrongOrder) = default; 250 }; 251 252 struct WeakOrder { 253 int value; 254 constexpr WeakOrder(int v) : value(v) {} 255 friend std::weak_ordering operator<=>(WeakOrder, WeakOrder) = default; 256 }; 257 258 struct PartialOrder { 259 int value; 260 constexpr PartialOrder(int v) : value(v) {} 261 friend constexpr std::partial_ordering operator<=>(PartialOrder lhs, PartialOrder rhs) { 262 if (lhs.value == std::numeric_limits<int>::min() || rhs.value == std::numeric_limits<int>::min()) 263 return std::partial_ordering::unordered; 264 if (lhs.value == std::numeric_limits<int>::max() || rhs.value == std::numeric_limits<int>::max()) 265 return std::partial_ordering::unordered; 266 return lhs.value <=> rhs.value; 267 } 268 friend constexpr bool operator==(PartialOrder lhs, PartialOrder rhs) { 269 return (lhs <=> rhs) == std::partial_ordering::equivalent; 270 } 271 }; 272 273 #endif 274 275 #endif // TEST_COMPARISONS_H 276