1c17628fbSMarshall Clow //===----------------------------------------------------------------------===//
2c17628fbSMarshall Clow //
357b08b09SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
457b08b09SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
557b08b09SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c17628fbSMarshall Clow //
7c17628fbSMarshall Clow //===----------------------------------------------------------------------===//
8c17628fbSMarshall Clow // A set of routines for testing the comparison operators of a type
9c17628fbSMarshall Clow //
10984f5f3fSMark de Wever // FooOrder<expected-ordering> All seven comparison operators, requires C++20 or newer.
11984f5f3fSMark de Wever // FooComparison All six pre-C++20 comparison operators
12984f5f3fSMark de Wever // FooEquality Equality operators operator== and operator!=
13c17628fbSMarshall Clow //
14984f5f3fSMark de Wever // AssertXAreNoexcept static_asserts that the operations are all noexcept.
15984f5f3fSMark de Wever // AssertXReturnBool static_asserts that the operations return bool.
16984f5f3fSMark de Wever // AssertOrderReturn static_asserts that the pre-C++20 comparison operations
17984f5f3fSMark de Wever // return bool and operator<=> returns the proper type.
18984f5f3fSMark de Wever // AssertXConvertibleToBool static_asserts that the operations return something convertible to bool.
19984f5f3fSMark de Wever // testXValues returns the result of the comparison of all operations.
20984f5f3fSMark de Wever //
21984f5f3fSMark de Wever // AssertOrderConvertibleToBool doesn't exist yet. It will be implemented when needed.
22c17628fbSMarshall Clow
23c17628fbSMarshall Clow #ifndef TEST_COMPARISONS_H
24c17628fbSMarshall Clow #define TEST_COMPARISONS_H
25c17628fbSMarshall Clow
26c87a4a46SKonstantin Boyarinov #include <cassert>
272a06757aSAdrian Vogelsgesang #include <compare>
283818b4dfSMark de Wever #include <concepts>
292a06757aSAdrian Vogelsgesang #include <limits>
30c4566cacSKent Ross #include <type_traits>
31c4566cacSKent Ross #include <utility>
32c4566cacSKent Ross
33c17628fbSMarshall Clow #include "test_macros.h"
34c17628fbSMarshall Clow
35c4566cacSKent Ross // Test the consistency of the six basic comparison operators for values that are ordered or unordered.
36f3126c8bSMarshall Clow template <class T, class U = T>
37c4566cacSKent Ross TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool
testComparisonsComplete(const T & t1,const U & t2,bool isEqual,bool isLess,bool isGreater)38c4566cacSKent Ross testComparisonsComplete(const T& t1, const U& t2, bool isEqual, bool isLess, bool isGreater) {
39c4566cacSKent Ross assert(((isEqual ? 1 : 0) + (isLess ? 1 : 0) + (isGreater ? 1 : 0) <= 1) &&
40c4566cacSKent Ross "at most one of isEqual, isLess, and isGreater can be true");
41c4566cacSKent Ross if (isEqual) {
42c17628fbSMarshall Clow if (!(t1 == t2)) return false;
43c17628fbSMarshall Clow if (!(t2 == t1)) return false;
44c17628fbSMarshall Clow if ( (t1 != t2)) return false;
45c17628fbSMarshall Clow if ( (t2 != t1)) return false;
46c17628fbSMarshall Clow if ( (t1 < t2)) return false;
47c17628fbSMarshall Clow if ( (t2 < t1)) return false;
48c17628fbSMarshall Clow if (!(t1 <= t2)) return false;
49c17628fbSMarshall Clow if (!(t2 <= t1)) return false;
50c17628fbSMarshall Clow if ( (t1 > t2)) return false;
51c17628fbSMarshall Clow if ( (t2 > t1)) return false;
52c17628fbSMarshall Clow if (!(t1 >= t2)) return false;
53c17628fbSMarshall Clow if (!(t2 >= t1)) return false;
54c4566cacSKent Ross } else if (isLess) {
55c17628fbSMarshall Clow if ( (t1 == t2)) return false;
56c17628fbSMarshall Clow if ( (t2 == t1)) return false;
57c17628fbSMarshall Clow if (!(t1 != t2)) return false;
58c17628fbSMarshall Clow if (!(t2 != t1)) return false;
59c17628fbSMarshall Clow if (!(t1 < t2)) return false;
60c17628fbSMarshall Clow if ( (t2 < t1)) return false;
61c17628fbSMarshall Clow if (!(t1 <= t2)) return false;
62c17628fbSMarshall Clow if ( (t2 <= t1)) return false;
63c17628fbSMarshall Clow if ( (t1 > t2)) return false;
64c17628fbSMarshall Clow if (!(t2 > t1)) return false;
65c17628fbSMarshall Clow if ( (t1 >= t2)) return false;
66c17628fbSMarshall Clow if (!(t2 >= t1)) return false;
67c4566cacSKent Ross } else if (isGreater) {
68c17628fbSMarshall Clow if ( (t1 == t2)) return false;
69c17628fbSMarshall Clow if ( (t2 == t1)) return false;
70c17628fbSMarshall Clow if (!(t1 != t2)) return false;
71c17628fbSMarshall Clow if (!(t2 != t1)) return false;
72c17628fbSMarshall Clow if ( (t1 < t2)) return false;
73c17628fbSMarshall Clow if (!(t2 < t1)) return false;
74c17628fbSMarshall Clow if ( (t1 <= t2)) return false;
75c17628fbSMarshall Clow if (!(t2 <= t1)) return false;
76c17628fbSMarshall Clow if (!(t1 > t2)) return false;
77c17628fbSMarshall Clow if ( (t2 > t1)) return false;
78c17628fbSMarshall Clow if (!(t1 >= t2)) return false;
79c17628fbSMarshall Clow if ( (t2 >= t1)) return false;
80c4566cacSKent Ross } else { // unordered
81c4566cacSKent Ross if ( (t1 == t2)) return false;
82c4566cacSKent Ross if ( (t2 == t1)) return false;
83c4566cacSKent Ross if (!(t1 != t2)) return false;
84c4566cacSKent Ross if (!(t2 != t1)) return false;
85c4566cacSKent Ross if ( (t1 < t2)) return false;
86c4566cacSKent Ross if ( (t2 < t1)) return false;
87c4566cacSKent Ross if ( (t1 <= t2)) return false;
88c4566cacSKent Ross if ( (t2 <= t1)) return false;
89c4566cacSKent Ross if ( (t1 > t2)) return false;
90c4566cacSKent Ross if ( (t2 > t1)) return false;
91c4566cacSKent Ross if ( (t1 >= t2)) return false;
92c4566cacSKent Ross if ( (t2 >= t1)) return false;
93c17628fbSMarshall Clow }
94c17628fbSMarshall Clow
95c17628fbSMarshall Clow return true;
96c17628fbSMarshall Clow }
97c17628fbSMarshall Clow
98c4566cacSKent Ross // Test the six basic comparison operators for ordered values.
99c4566cacSKent Ross template <class T, class U = T>
testComparisons(const T & t1,const U & t2,bool isEqual,bool isLess)100c4566cacSKent Ross TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testComparisons(const T& t1, const U& t2, bool isEqual, bool isLess) {
101c4566cacSKent Ross assert(!(isEqual && isLess) && "isEqual and isLess cannot be both true");
102c4566cacSKent Ross bool isGreater = !isEqual && !isLess;
103c4566cacSKent Ross return testComparisonsComplete(t1, t2, isEqual, isLess, isGreater);
104c4566cacSKent Ross }
105c4566cacSKent Ross
106c17628fbSMarshall Clow // Easy call when you can init from something already comparable.
107c17628fbSMarshall Clow template <class T, class Param>
testComparisonsValues(Param val1,Param val2)108d7e0cec6SAdrian Vogelsgesang TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testComparisonsValues(Param val1, Param val2)
109c17628fbSMarshall Clow {
110c17628fbSMarshall Clow const bool isEqual = val1 == val2;
111c17628fbSMarshall Clow const bool isLess = val1 < val2;
112c4566cacSKent Ross const bool isGreater = val1 > val2;
113c17628fbSMarshall Clow
114c4566cacSKent Ross return testComparisonsComplete(T(val1), T(val2), isEqual, isLess, isGreater);
115c17628fbSMarshall Clow }
116c17628fbSMarshall Clow
117f3126c8bSMarshall Clow template <class T, class U = T>
AssertComparisonsAreNoexcept()1183818b4dfSMark de Wever TEST_CONSTEXPR_CXX14 void AssertComparisonsAreNoexcept() {
119f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() == std::declval<const U&>());
120f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() != std::declval<const U&>());
121f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() < std::declval<const U&>());
122f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() <= std::declval<const U&>());
123f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() > std::declval<const U&>());
124f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() >= std::declval<const U&>());
125c17628fbSMarshall Clow }
126c17628fbSMarshall Clow
127f3126c8bSMarshall Clow template <class T, class U = T>
AssertComparisonsReturnBool()1283818b4dfSMark de Wever TEST_CONSTEXPR_CXX14 void AssertComparisonsReturnBool() {
129f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool);
130f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool);
131f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() < std::declval<const U&>()), bool);
132f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <= std::declval<const U&>()), bool);
133f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() > std::declval<const U&>()), bool);
134f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() >= std::declval<const U&>()), bool);
135c17628fbSMarshall Clow }
136c17628fbSMarshall Clow
137f3126c8bSMarshall Clow template <class T, class U = T>
AssertComparisonsConvertibleToBool()138984f5f3fSMark de Wever void AssertComparisonsConvertibleToBool()
139c17628fbSMarshall Clow {
140f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() == std::declval<const U&>()), bool>::value), "");
141f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() != std::declval<const U&>()), bool>::value), "");
142f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() < std::declval<const U&>()), bool>::value), "");
143f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() <= std::declval<const U&>()), bool>::value), "");
144f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() > std::declval<const U&>()), bool>::value), "");
145f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() >= std::declval<const U&>()), bool>::value), "");
146c17628fbSMarshall Clow }
147c17628fbSMarshall Clow
148984f5f3fSMark de Wever #if TEST_STD_VER > 17
149984f5f3fSMark de Wever template <class T, class U = T>
AssertOrderAreNoexcept()1503818b4dfSMark de Wever constexpr void AssertOrderAreNoexcept() {
151984f5f3fSMark de Wever AssertComparisonsAreNoexcept<T, U>();
152984f5f3fSMark de Wever ASSERT_NOEXCEPT(std::declval<const T&>() <=> std::declval<const U&>());
153984f5f3fSMark de Wever }
154984f5f3fSMark de Wever
155984f5f3fSMark de Wever template <class Order, class T, class U = T>
AssertOrderReturn()1563818b4dfSMark de Wever constexpr void AssertOrderReturn() {
157984f5f3fSMark de Wever AssertComparisonsReturnBool<T, U>();
158984f5f3fSMark de Wever ASSERT_SAME_TYPE(decltype(std::declval<const T&>() <=> std::declval<const U&>()), Order);
159984f5f3fSMark de Wever }
160984f5f3fSMark de Wever
161984f5f3fSMark de Wever template <class Order, class T, class U = T>
testOrder(const T & t1,const U & t2,Order order)162d7e0cec6SAdrian Vogelsgesang TEST_NODISCARD constexpr bool testOrder(const T& t1, const U& t2, Order order) {
1633818b4dfSMark de Wever bool equal = order == Order::equivalent;
1643818b4dfSMark de Wever bool less = order == Order::less;
165c4566cacSKent Ross bool greater = order == Order::greater;
1663818b4dfSMark de Wever
167c4566cacSKent Ross return (t1 <=> t2 == order) && testComparisonsComplete(t1, t2, equal, less, greater);
168984f5f3fSMark de Wever }
169984f5f3fSMark de Wever
170984f5f3fSMark de Wever template <class T, class Param>
testOrderValues(Param val1,Param val2)171d7e0cec6SAdrian Vogelsgesang TEST_NODISCARD constexpr bool testOrderValues(Param val1, Param val2) {
172984f5f3fSMark de Wever return testOrder(T(val1), T(val2), val1 <=> val2);
173984f5f3fSMark de Wever }
174984f5f3fSMark de Wever
175984f5f3fSMark de Wever #endif
176984f5f3fSMark de Wever
177f3126c8bSMarshall Clow // Test all two comparison operations for sanity
178f3126c8bSMarshall Clow template <class T, class U = T>
testEquality(const T & t1,const U & t2,bool isEqual)179d7e0cec6SAdrian Vogelsgesang TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testEquality(const T& t1, const U& t2, bool isEqual)
180c17628fbSMarshall Clow {
181c17628fbSMarshall Clow if (isEqual)
182c17628fbSMarshall Clow {
183c17628fbSMarshall Clow if (!(t1 == t2)) return false;
184c17628fbSMarshall Clow if (!(t2 == t1)) return false;
185c17628fbSMarshall Clow if ( (t1 != t2)) return false;
186c17628fbSMarshall Clow if ( (t2 != t1)) return false;
187c17628fbSMarshall Clow }
188f3126c8bSMarshall Clow else /* not equal */
189c17628fbSMarshall Clow {
190c17628fbSMarshall Clow if ( (t1 == t2)) return false;
191c17628fbSMarshall Clow if ( (t2 == t1)) return false;
192c17628fbSMarshall Clow if (!(t1 != t2)) return false;
193c17628fbSMarshall Clow if (!(t2 != t1)) return false;
194c17628fbSMarshall Clow }
195c17628fbSMarshall Clow
196c17628fbSMarshall Clow return true;
197c17628fbSMarshall Clow }
198c17628fbSMarshall Clow
199c17628fbSMarshall Clow // Easy call when you can init from something already comparable.
200c17628fbSMarshall Clow template <class T, class Param>
testEqualityValues(Param val1,Param val2)201d7e0cec6SAdrian Vogelsgesang TEST_NODISCARD TEST_CONSTEXPR_CXX14 bool testEqualityValues(Param val1, Param val2)
202c17628fbSMarshall Clow {
203c17628fbSMarshall Clow const bool isEqual = val1 == val2;
204c17628fbSMarshall Clow
205984f5f3fSMark de Wever return testEquality(T(val1), T(val2), isEqual);
206c17628fbSMarshall Clow }
207c17628fbSMarshall Clow
208f3126c8bSMarshall Clow template <class T, class U = T>
AssertEqualityAreNoexcept()209984f5f3fSMark de Wever void AssertEqualityAreNoexcept()
210c17628fbSMarshall Clow {
211f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() == std::declval<const U&>());
212f3126c8bSMarshall Clow ASSERT_NOEXCEPT(std::declval<const T&>() != std::declval<const U&>());
213c17628fbSMarshall Clow }
214c17628fbSMarshall Clow
215f3126c8bSMarshall Clow template <class T, class U = T>
AssertEqualityReturnBool()216*0b46606cSJakub Mazurkiewicz TEST_CONSTEXPR_CXX14 void AssertEqualityReturnBool() {
217f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() == std::declval<const U&>()), bool);
218f3126c8bSMarshall Clow ASSERT_SAME_TYPE(decltype(std::declval<const T&>() != std::declval<const U&>()), bool);
219c17628fbSMarshall Clow }
220c17628fbSMarshall Clow
221f3126c8bSMarshall Clow template <class T, class U = T>
AssertEqualityConvertibleToBool()222984f5f3fSMark de Wever void AssertEqualityConvertibleToBool()
223c17628fbSMarshall Clow {
224f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() == std::declval<const U&>()), bool>::value), "");
225f3126c8bSMarshall Clow static_assert((std::is_convertible<decltype(std::declval<const T&>() != std::declval<const U&>()), bool>::value), "");
226c17628fbSMarshall Clow }
227c17628fbSMarshall Clow
228c87a4a46SKonstantin Boyarinov struct LessAndEqComp {
229c87a4a46SKonstantin Boyarinov int value;
230c87a4a46SKonstantin Boyarinov
LessAndEqCompLessAndEqComp231d7ac595fSKonstantin Boyarinov TEST_CONSTEXPR_CXX14 LessAndEqComp(int v) : value(v) {}
232c87a4a46SKonstantin Boyarinov
233d7ac595fSKonstantin Boyarinov friend TEST_CONSTEXPR_CXX14 bool operator<(const LessAndEqComp& lhs, const LessAndEqComp& rhs) {
234c87a4a46SKonstantin Boyarinov return lhs.value < rhs.value;
235c87a4a46SKonstantin Boyarinov }
236c87a4a46SKonstantin Boyarinov
237d7ac595fSKonstantin Boyarinov friend TEST_CONSTEXPR_CXX14 bool operator==(const LessAndEqComp& lhs, const LessAndEqComp& rhs) {
238c87a4a46SKonstantin Boyarinov return lhs.value == rhs.value;
239c87a4a46SKonstantin Boyarinov }
240c87a4a46SKonstantin Boyarinov };
2412a06757aSAdrian Vogelsgesang
242f8b5ac34SHristo Hristov #if TEST_STD_VER >= 20
243f8b5ac34SHristo Hristov
2442a06757aSAdrian Vogelsgesang struct StrongOrder {
2452a06757aSAdrian Vogelsgesang int value;
StrongOrderStrongOrder2462a06757aSAdrian Vogelsgesang constexpr StrongOrder(int v) : value(v) {}
2472a06757aSAdrian Vogelsgesang friend std::strong_ordering operator<=>(StrongOrder, StrongOrder) = default;
2482a06757aSAdrian Vogelsgesang };
2492a06757aSAdrian Vogelsgesang
2502a06757aSAdrian Vogelsgesang struct WeakOrder {
2512a06757aSAdrian Vogelsgesang int value;
WeakOrderWeakOrder2522a06757aSAdrian Vogelsgesang constexpr WeakOrder(int v) : value(v) {}
2532a06757aSAdrian Vogelsgesang friend std::weak_ordering operator<=>(WeakOrder, WeakOrder) = default;
2542a06757aSAdrian Vogelsgesang };
2552a06757aSAdrian Vogelsgesang
2562a06757aSAdrian Vogelsgesang struct PartialOrder {
2572a06757aSAdrian Vogelsgesang int value;
PartialOrderPartialOrder2582a06757aSAdrian Vogelsgesang constexpr PartialOrder(int v) : value(v) {}
2592a06757aSAdrian Vogelsgesang friend constexpr std::partial_ordering operator<=>(PartialOrder lhs, PartialOrder rhs) {
2602a06757aSAdrian Vogelsgesang if (lhs.value == std::numeric_limits<int>::min() || rhs.value == std::numeric_limits<int>::min())
2612a06757aSAdrian Vogelsgesang return std::partial_ordering::unordered;
262f8b5ac34SHristo Hristov if (lhs.value == std::numeric_limits<int>::max() || rhs.value == std::numeric_limits<int>::max())
263f8b5ac34SHristo Hristov return std::partial_ordering::unordered;
2642a06757aSAdrian Vogelsgesang return lhs.value <=> rhs.value;
2652a06757aSAdrian Vogelsgesang }
2662a06757aSAdrian Vogelsgesang friend constexpr bool operator==(PartialOrder lhs, PartialOrder rhs) {
2672a06757aSAdrian Vogelsgesang return (lhs <=> rhs) == std::partial_ordering::equivalent;
2682a06757aSAdrian Vogelsgesang }
2692a06757aSAdrian Vogelsgesang };
2702a06757aSAdrian Vogelsgesang
2712a06757aSAdrian Vogelsgesang #endif
2722a06757aSAdrian Vogelsgesang
273c17628fbSMarshall Clow #endif // TEST_COMPARISONS_H
274