//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// // // template class tuple; // template // auto // operator<=>(const tuple& t, const tuple& u); // UNSUPPORTED: c++03, c++11, c++14, c++17 #include "test_macros.h" TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare") TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare") TEST_MSVC_DIAGNOSTIC_IGNORED(4242 4244) #include #include #include #include #include // std::is_constant_evaluated // A custom three-way result type struct CustomEquality { friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; } friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; } friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; } }; constexpr bool test() { // Empty tuple { typedef std::tuple<> T0; // No member types yields strong ordering (all are equal). ASSERT_SAME_TYPE(decltype(T0() <=> T0()), std::strong_ordering); assert((T0() <=> T0()) == std::strong_ordering::equal); } // Mixed types with integers, which compare strongly ordered { typedef std::tuple T1; typedef std::tuple T2; ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering); assert((T1(1) <=> T2(1)) == std::strong_ordering::equal); assert((T1(1) <=> T2(0)) == std::strong_ordering::greater); assert((T1(1) <=> T2(2)) == std::strong_ordering::less); } { typedef std::tuple T1; typedef std::tuple T2; ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering); assert((T1(1, 2) <=> T2(1, 2)) == std::strong_ordering::equal); assert((T1(1, 2) <=> T2(0, 2)) == std::strong_ordering::greater); assert((T1(1, 2) <=> T2(2, 2)) == std::strong_ordering::less); assert((T1(1, 2) <=> T2(1, 1)) == std::strong_ordering::greater); assert((T1(1, 2) <=> T2(1, 3)) == std::strong_ordering::less); } { typedef std::tuple T1; typedef std::tuple T2; ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering); assert((T1(1, 2, 3) <=> T2(1, 2, 3)) == std::strong_ordering::equal); assert((T1(1, 2, 3) <=> T2(0, 2, 3)) == std::strong_ordering::greater); assert((T1(1, 2, 3) <=> T2(2, 2, 3)) == std::strong_ordering::less); assert((T1(1, 2, 3) <=> T2(1, 1, 3)) == std::strong_ordering::greater); assert((T1(1, 2, 3) <=> T2(1, 3, 3)) == std::strong_ordering::less); assert((T1(1, 2, 3) <=> T2(1, 2, 2)) == std::strong_ordering::greater); assert((T1(1, 2, 3) <=> T2(1, 2, 4)) == std::strong_ordering::less); } // Mixed types with floating point, which compare partially ordered { typedef std::tuple T1; typedef std::tuple T2; ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); assert((T1(1) <=> T2(1)) == std::partial_ordering::equivalent); assert((T1(1) <=> T2(0.9)) == std::partial_ordering::greater); assert((T1(1) <=> T2(1.1)) == std::partial_ordering::less); } { typedef std::tuple T1; typedef std::tuple T2; ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); assert((T1(1, 2) <=> T2(1, 2)) == std::partial_ordering::equivalent); assert((T1(1, 2) <=> T2(0.9, 2)) == std::partial_ordering::greater); assert((T1(1, 2) <=> T2(1.1, 2)) == std::partial_ordering::less); assert((T1(1, 2) <=> T2(1, 1)) == std::partial_ordering::greater); assert((T1(1, 2) <=> T2(1, 3)) == std::partial_ordering::less); } { typedef std::tuple T1; typedef std::tuple T2; ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); assert((T1(1, 2, 3) <=> T2(1, 2, 3)) == std::partial_ordering::equivalent); assert((T1(1, 2, 3) <=> T2(0.9, 2, 3)) == std::partial_ordering::greater); assert((T1(1, 2, 3) <=> T2(1.1, 2, 3)) == std::partial_ordering::less); assert((T1(1, 2, 3) <=> T2(1, 1, 3)) == std::partial_ordering::greater); assert((T1(1, 2, 3) <=> T2(1, 3, 3)) == std::partial_ordering::less); assert((T1(1, 2, 3) <=> T2(1, 2, 2)) == std::partial_ordering::greater); assert((T1(1, 2, 3) <=> T2(1, 2, 4)) == std::partial_ordering::less); } { typedef std::tuple T1; typedef std::tuple T2; constexpr double nan = std::numeric_limits::quiet_NaN(); // Comparisons with NaN and non-NaN are non-constexpr in GCC, so both sides must be NaN assert((T1(nan) <=> T2(nan)) == std::partial_ordering::unordered); } { typedef std::tuple T1; typedef std::tuple T2; constexpr double nan = std::numeric_limits::quiet_NaN(); assert((T1(nan, 2) <=> T2(nan, 2)) == std::partial_ordering::unordered); assert((T1(1, nan) <=> T2(1, nan)) == std::partial_ordering::unordered); } { typedef std::tuple T1; typedef std::tuple T2; constexpr double nan = std::numeric_limits::quiet_NaN(); assert((T1(nan, 2, 3) <=> T2(nan, 2, 3)) == std::partial_ordering::unordered); assert((T1(1, nan, 3) <=> T2(1, nan, 3)) == std::partial_ordering::unordered); assert((T1(1, 2, nan) <=> T2(1, 2, nan)) == std::partial_ordering::unordered); } // Ordering classes and synthesized three way comparison { typedef std::tuple T1; typedef std::tuple T2; // All strongly ordered members yields strong ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering); } { struct WeakSpaceship { constexpr bool operator==(const WeakSpaceship&) const { return true; } constexpr std::weak_ordering operator<=>(const WeakSpaceship&) const { return std::weak_ordering::equivalent; } }; { typedef std::tuple T1; typedef std::tuple T2; // Strongly ordered members and a weakly ordered member yields weak ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering); } { typedef std::tuple T1; typedef std::tuple T2; // Doubles are partially ordered, so one partial, one strong, and one weak ordering // yields partial ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); } } { struct NoSpaceship { constexpr bool operator==(const NoSpaceship&) const { return true; } constexpr bool operator<(const NoSpaceship&) const { return false; } }; typedef std::tuple T1; typedef std::tuple T2; // Strongly ordered members and a weakly ordered member (synthesized) yields weak ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering); } { struct SpaceshipNoEquals { constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const { return std::strong_ordering::equal; } constexpr bool operator<(const SpaceshipNoEquals&) const { return false; } }; typedef std::tuple T1; typedef std::tuple T2; // Spaceship operator with no == operator falls back on the < operator and weak ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering); } { struct CustomSpaceship { constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); } }; typedef std::tuple T1; typedef std::tuple T2; typedef std::tuple T3; // Custom three way return types cannot be used in synthesized three way comparison, // but they can be used for (rewritten) operator< when synthesizing a weak ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering); ASSERT_SAME_TYPE(decltype(T3() <=> T3()), std::weak_ordering); } { typedef std::tuple T1; typedef std::tuple T2; // Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator // between signed and unsigned types, so we should end up with a synthesized weak ordering. ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering); } #ifdef TEST_COMPILER_GCC // GCC cannot evaluate NaN @ non-NaN constexpr, so test that runtime-only. if (!std::is_constant_evaluated()) #endif { { typedef std::tuple T1; typedef std::tuple T2; constexpr double nan = std::numeric_limits::quiet_NaN(); ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); assert((T1(nan) <=> T2(1)) == std::partial_ordering::unordered); } { typedef std::tuple T1; typedef std::tuple T2; constexpr double nan = std::numeric_limits::quiet_NaN(); ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); assert((T1(nan, 2) <=> T2(1, 2)) == std::partial_ordering::unordered); assert((T1(1, nan) <=> T2(1, 2)) == std::partial_ordering::unordered); } { typedef std::tuple T1; typedef std::tuple T2; constexpr double nan = std::numeric_limits::quiet_NaN(); ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering); assert((T1(nan, 2, 3) <=> T2(1, 2, 3)) == std::partial_ordering::unordered); assert((T1(1, nan, 3) <=> T2(1, 2, 3)) == std::partial_ordering::unordered); assert((T1(1, 2, nan) <=> T2(1, 2, 3)) == std::partial_ordering::unordered); } } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }