338 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8// A set of routines for testing the comparison operators of a type9//10// FooOrder<expected-ordering> All seven comparison operators, requires C++20 or newer.11// FooComparison All six pre-C++20 comparison operators12// 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 operations17// 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_H24#define TEST_COMPARISONS_H25 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.36template <class T, class U = T>37TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool38testComparisonsComplete(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 { // unordered81 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.99template <class T, class U = T>100TEST_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.107template <class T, class Param>108TEST_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 117template <class T, class U = T>118TEST_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 127template <class T, class U = T>128TEST_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 137template <class T, class U = T>138void 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 > 17149template <class T, class U = T>150constexpr void AssertOrderAreNoexcept() {151 AssertComparisonsAreNoexcept<T, U>();152 ASSERT_NOEXCEPT(std::declval<const T&>() <=> std::declval<const U&>());153}154 155template <class Order, class T, class U = T>156constexpr void AssertOrderReturn() {157 AssertComparisonsReturnBool<T, U>();158 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <=> std::declval<const U&>()), Order);159}160 161template <class Order, class T, class U = T>162TEST_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 170template <class T, class Param>171TEST_NODISCARD constexpr bool testOrderValues(Param val1, Param val2) {172 return testOrder(T(val1), T(val2), val1 <=> val2);173}174 175#endif176 177// Test all two comparison operations for sanity178template <class T, class U = T>179TEST_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.200template <class T, class Param>201TEST_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 208template <class T, class U = T>209void 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 215template <class T, class U = T>216TEST_CONSTEXPR_CXX14 void AssertEqualityReturnBool() {217 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool);218 ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool);219}220 221template <class T, class U = T>222void AssertEqualityConvertibleToBool()223{224 static_assert((std::is_convertible<decltype(std::declval<const T&>() == std::declval<const U&>()), bool>::value), "");225 static_assert((std::is_convertible<decltype(std::declval<const T&>() != std::declval<const U&>()), bool>::value), "");226}227 228struct LessAndEqComp {229 int value;230 231 TEST_CONSTEXPR_CXX14 LessAndEqComp(int v) : value(v) {}232 233 friend TEST_CONSTEXPR_CXX14 bool operator<(const LessAndEqComp& lhs, const LessAndEqComp& rhs) {234 return lhs.value < rhs.value;235 }236 237 friend TEST_CONSTEXPR_CXX14 bool operator==(const LessAndEqComp& lhs, const LessAndEqComp& rhs) {238 return lhs.value == rhs.value;239 }240};241 242#if TEST_STD_VER >= 20243 244struct StrongOrder {245 int value;246 constexpr StrongOrder(int v) : value(v) {}247 friend std::strong_ordering operator<=>(StrongOrder, StrongOrder) = default;248};249 250struct WeakOrder {251 int value;252 constexpr WeakOrder(int v) : value(v) {}253 friend std::weak_ordering operator<=>(WeakOrder, WeakOrder) = default;254};255 256struct PartialOrder {257 int value;258 constexpr PartialOrder(int v) : value(v) {}259 friend constexpr std::partial_ordering operator<=>(PartialOrder lhs, PartialOrder rhs) {260 if (lhs.value == std::numeric_limits<int>::min() || rhs.value == std::numeric_limits<int>::min())261 return std::partial_ordering::unordered;262 if (lhs.value == std::numeric_limits<int>::max() || rhs.value == std::numeric_limits<int>::max())263 return std::partial_ordering::unordered;264 return lhs.value <=> rhs.value;265 }266 friend constexpr bool operator==(PartialOrder lhs, PartialOrder rhs) {267 return (lhs <=> rhs) == std::partial_ordering::equivalent;268 }269};270 271template <typename T1, typename T2 = T1>272concept HasOperatorEqual = requires(T1 t1, T2 t2) { t1 == t2; };273 274template <typename T1, typename T2 = T1>275concept HasOperatorGreaterThan = requires(T1 t1, T2 t2) { t1 > t2; };276 277template <typename T1, typename T2 = T1>278concept HasOperatorGreaterThanEqual = requires(T1 t1, T2 t2) { t1 >= t2; };279template <typename T1, typename T2 = T1>280concept HasOperatorLessThan = requires(T1 t1, T2 t2) { t1 < t2; };281 282template <typename T1, typename T2 = T1>283concept HasOperatorLessThanEqual = requires(T1 t1, T2 t2) { t1 <= t2; };284 285template <typename T1, typename T2 = T1>286concept HasOperatorNotEqual = requires(T1 t1, T2 t2) { t1 != t2; };287 288template <typename T1, typename T2 = T1>289concept HasOperatorSpaceship = requires(T1 t1, T2 t2) { t1 <=> t2; };290 291struct NonComparable {};292static_assert(!std::equality_comparable<NonComparable>);293static_assert(!HasOperatorEqual<NonComparable>);294static_assert(!HasOperatorGreaterThan<NonComparable>);295static_assert(!HasOperatorGreaterThanEqual<NonComparable>);296static_assert(!HasOperatorLessThan<NonComparable>);297static_assert(!HasOperatorLessThanEqual<NonComparable>);298static_assert(!HasOperatorNotEqual<NonComparable>);299static_assert(!HasOperatorSpaceship<NonComparable>);300 301class EqualityComparable {302public:303 constexpr EqualityComparable(int value) : value_{value} {};304 305 friend constexpr bool operator==(const EqualityComparable&, const EqualityComparable&) noexcept = default;306 307private:308 int value_;309};310static_assert(std::equality_comparable<EqualityComparable>);311static_assert(HasOperatorEqual<EqualityComparable>);312static_assert(HasOperatorNotEqual<EqualityComparable>);313 314class ThreeWayComparable {315public:316 constexpr ThreeWayComparable(int value) : value_{value} {};317 318 friend constexpr bool operator==(const ThreeWayComparable&, const ThreeWayComparable&) noexcept = default;319 friend constexpr std::strong_ordering320 operator<=>(const ThreeWayComparable&, const ThreeWayComparable&) noexcept = default;321 322private:323 int value_;324};325static_assert(std::equality_comparable<ThreeWayComparable>);326static_assert(std::three_way_comparable<ThreeWayComparable>);327static_assert(HasOperatorEqual<ThreeWayComparable>);328static_assert(HasOperatorGreaterThan<ThreeWayComparable>);329static_assert(HasOperatorGreaterThanEqual<ThreeWayComparable>);330static_assert(HasOperatorLessThan<ThreeWayComparable>);331static_assert(HasOperatorLessThanEqual<ThreeWayComparable>);332static_assert(HasOperatorNotEqual<ThreeWayComparable>);333static_assert(HasOperatorSpaceship<ThreeWayComparable>);334 335#endif // TEST_STD_VER >= 20336 337#endif // TEST_COMPARISONS_H338