//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // A set of routines for testing the comparison operators of a type // // FooOrder All seven comparison operators, requires C++20 or newer. // FooComparison All six pre-C++20 comparison operators // FooEquality Equality operators operator== and operator!= // // AssertXAreNoexcept static_asserts that the operations are all noexcept. // AssertXReturnBool static_asserts that the operations return bool. // AssertOrderReturn static_asserts that the pre-C++20 comparison operations // return bool and operator<=> returns the proper type. // AssertXConvertibleToBool static_asserts that the operations return something convertible to bool. // testXValues returns the result of the comparison of all operations. // // AssertOrderConvertibleToBool doesn't exist yet. It will be implemented when needed. #ifndef TEST_COMPARISONS_H #define TEST_COMPARISONS_H #include #if _LIBCUDACXX_HAS_SPACESHIP_OPERATOR() # include #endif // _LIBCUDACXX_HAS_SPACESHIP_OPERATOR() #include #include #include #include #include "test_macros.h" // Test the consistency of the six basic comparison operators for values that are ordered or unordered. template [[nodiscard]] TEST_FUNC constexpr bool testComparisonsComplete(const T& t1, const U& t2, bool isEqual, bool isLess, bool isGreater) { assert(((isEqual ? 1 : 0) + (isLess ? 1 : 0) + (isGreater ? 1 : 0) <= 1) && "at most one of isEqual, isLess, and isGreater can be true"); if (isEqual) { if (!(t1 == t2)) { return false; } if (!(t2 == t1)) { return false; } if ((t1 != t2)) { return false; } if ((t2 != t1)) { return false; } if ((t1 < t2)) { return false; } if ((t2 < t1)) { return false; } if (!(t1 <= t2)) { return false; } if (!(t2 <= t1)) { return false; } if ((t1 > t2)) { return false; } if ((t2 > t1)) { return false; } if (!(t1 >= t2)) { return false; } if (!(t2 >= t1)) { return false; } } else if (isLess) { if ((t1 == t2)) { return false; } if ((t2 == t1)) { return false; } if (!(t1 != t2)) { return false; } if (!(t2 != t1)) { return false; } if (!(t1 < t2)) { return false; } if ((t2 < t1)) { return false; } if (!(t1 <= t2)) { return false; } if ((t2 <= t1)) { return false; } if ((t1 > t2)) { return false; } if (!(t2 > t1)) { return false; } if ((t1 >= t2)) { return false; } if (!(t2 >= t1)) { return false; } } else if (isGreater) { if ((t1 == t2)) { return false; } if ((t2 == t1)) { return false; } if (!(t1 != t2)) { return false; } if (!(t2 != t1)) { return false; } if ((t1 < t2)) { return false; } if (!(t2 < t1)) { return false; } if ((t1 <= t2)) { return false; } if (!(t2 <= t1)) { return false; } if (!(t1 > t2)) { return false; } if ((t2 > t1)) { return false; } if (!(t1 >= t2)) { return false; } if ((t2 >= t1)) { return false; } } else { // unordered if ((t1 == t2)) { return false; } if ((t2 == t1)) { return false; } if (!(t1 != t2)) { return false; } if (!(t2 != t1)) { return false; } if ((t1 < t2)) { return false; } if ((t2 < t1)) { return false; } if ((t1 <= t2)) { return false; } if ((t2 <= t1)) { return false; } if ((t1 > t2)) { return false; } if ((t2 > t1)) { return false; } if ((t1 >= t2)) { return false; } if ((t2 >= t1)) { return false; } } return true; } // Test the six basic comparison operators for ordered values. template [[nodiscard]] TEST_FUNC constexpr bool testComparisons(const T& t1, const U& t2, bool isEqual, bool isLess) { assert(!(isEqual && isLess) && "isEqual and isLess cannot be both true"); bool isGreater = !isEqual && !isLess; return testComparisonsComplete(t1, t2, isEqual, isLess, isGreater); } // Easy call when you can init from something already comparable. template [[nodiscard]] TEST_FUNC constexpr bool testComparisonsValues(Param val1, Param val2) { const bool isEqual = val1 == val2; const bool isLess = val1 < val2; const bool isGreater = val1 > val2; return testComparisonsComplete(T(val1), T(val2), isEqual, isLess, isGreater); } template TEST_FUNC constexpr void AssertComparisonsAreNoexcept() { static_assert(noexcept(cuda::std::declval() == cuda::std::declval())); static_assert(noexcept(cuda::std::declval() != cuda::std::declval())); static_assert(noexcept(cuda::std::declval() < cuda::std::declval())); static_assert(noexcept(cuda::std::declval() <= cuda::std::declval())); static_assert(noexcept(cuda::std::declval() > cuda::std::declval())); static_assert(noexcept(cuda::std::declval() >= cuda::std::declval())); } template TEST_FUNC constexpr void AssertComparisonsReturnBool() { static_assert(cuda::std::is_same_v() == cuda::std::declval()), bool>); static_assert(cuda::std::is_same_v() != cuda::std::declval()), bool>); static_assert(cuda::std::is_same_v() < cuda::std::declval()), bool>); static_assert(cuda::std::is_same_v() <= cuda::std::declval()), bool>); static_assert(cuda::std::is_same_v() > cuda::std::declval()), bool>); static_assert(cuda::std::is_same_v() >= cuda::std::declval()), bool>); } template TEST_FUNC constexpr void AssertComparisonsConvertibleToBool() { static_assert(( cuda::std::is_convertible() == cuda::std::declval()), bool>::value)); static_assert(( cuda::std::is_convertible() != cuda::std::declval()), bool>::value)); static_assert(( cuda::std::is_convertible() < cuda::std::declval()), bool>::value)); static_assert(( cuda::std::is_convertible() <= cuda::std::declval()), bool>::value)); static_assert(( cuda::std::is_convertible() > cuda::std::declval()), bool>::value)); static_assert(( cuda::std::is_convertible() >= cuda::std::declval()), bool>::value)); } #if TEST_STD_VER > 2017 && _LIBCUDACXX_HAS_SPACESHIP_OPERATOR() template TEST_FUNC constexpr void AssertOrderAreNoexcept() { AssertComparisonsAreNoexcept(); static_assert(noexcept(cuda::std::declval() <=> cuda::std::declval())); } template TEST_FUNC constexpr void AssertOrderReturn() { AssertComparisonsReturnBool(); static_assert( cuda::std::is_same_v() <=> cuda::std::declval()), Order>); } template [[nodiscard]] TEST_FUNC constexpr bool testOrder(const T& t1, const U& t2, Order order) { bool equal = order == Order::equivalent; bool less = order == Order::less; bool greater = order == Order::greater; return (t1 <=> t2 == order) && testComparisonsComplete(t1, t2, equal, less, greater); } template [[nodiscard]] TEST_FUNC constexpr bool testOrderValues(Param val1, Param val2) { return testOrder(T(val1), T(val2), val1 <=> val2); } #endif // TEST_STD_VER > 2017 && _LIBCUDACXX_HAS_SPACESHIP_OPERATOR() // Test all two comparison operations for sanity template [[nodiscard]] TEST_FUNC constexpr bool testEquality(const T& t1, const U& t2, bool isEqual) { if (isEqual) { if (!(t1 == t2)) { return false; } if (!(t2 == t1)) { return false; } if ((t1 != t2)) { return false; } if ((t2 != t1)) { return false; } } else /* not equal */ { if ((t1 == t2)) { return false; } if ((t2 == t1)) { return false; } if (!(t1 != t2)) { return false; } if (!(t2 != t1)) { return false; } } return true; } // Easy call when you can init from something already comparable. template [[nodiscard]] TEST_FUNC constexpr bool testEqualityValues(Param val1, Param val2) { const bool isEqual = val1 == val2; return testEquality(T(val1), T(val2), isEqual); } template TEST_FUNC constexpr void AssertEqualityAreNoexcept() { static_assert(noexcept(cuda::std::declval() == cuda::std::declval())); static_assert(noexcept(cuda::std::declval() != cuda::std::declval())); } template TEST_FUNC constexpr void AssertEqualityReturnBool() { static_assert(cuda::std::is_same_v() == cuda::std::declval()), bool>); static_assert(cuda::std::is_same_v() != cuda::std::declval()), bool>); } template TEST_FUNC constexpr void AssertEqualityConvertibleToBool() { static_assert(( cuda::std::is_convertible() == cuda::std::declval()), bool>::value)); static_assert(( cuda::std::is_convertible() != cuda::std::declval()), bool>::value)); } struct LessAndEqComp { int value; TEST_FUNC constexpr LessAndEqComp(int v) : value(v) {} TEST_FUNC friend constexpr bool operator<(const LessAndEqComp& lhs, const LessAndEqComp& rhs) { return lhs.value < rhs.value; } TEST_FUNC friend constexpr bool operator==(const LessAndEqComp& lhs, const LessAndEqComp& rhs) { return lhs.value == rhs.value; } }; #if TEST_STD_VER > 2017 && _LIBCUDACXX_HAS_SPACESHIP_OPERATOR() struct StrongOrder { int value; TEST_FUNC constexpr StrongOrder(int v) : value(v) {} TEST_FUNC friend cuda::std::strong_ordering operator<=>(StrongOrder, StrongOrder) = default; }; struct WeakOrder { int value; TEST_FUNC constexpr WeakOrder(int v) : value(v) {} TEST_FUNC friend cuda::std::weak_ordering operator<=>(WeakOrder, WeakOrder) = default; }; struct PartialOrder { int value; TEST_FUNC constexpr PartialOrder(int v) : value(v) {} TEST_FUNC friend constexpr cuda::std::partial_ordering operator<=>(PartialOrder lhs, PartialOrder rhs) { if (lhs.value == cuda::std::numeric_limits::min() || rhs.value == cuda::std::numeric_limits::min()) { return cuda::std::partial_ordering::unordered; } return lhs.value <=> rhs.value; } TEST_FUNC friend constexpr bool operator==(PartialOrder lhs, PartialOrder rhs) { return (lhs <=> rhs) == cuda::std::partial_ordering::equivalent; } }; #endif // TEST_STD_VER > 2017 && _LIBCUDACXX_HAS_SPACESHIP_OPERATOR() #endif // TEST_COMPARISONS_H