diff --git a/libcxx/CREDITS.TXT b/libcxx/CREDITS.TXT --- a/libcxx/CREDITS.TXT +++ b/libcxx/CREDITS.TXT @@ -113,6 +113,10 @@ E: jroelofS@jroelofs.com D: Remote testing, Newlib port, baremetal/single-threaded support. +N: Kent Ross +E: k@mad.cash +D: C++20 language feature support + N: Jonathan Sauer D: Minor patches, mostly related to constexpr @@ -149,11 +153,11 @@ E: xingxue@ca.ibm.com D: AIX port -N: Zhihao Yuan -E: lichray@gmail.com -D: Standard compatibility fixes. - N: Jeffrey Yasskin E: jyasskin@gmail.com E: jyasskin@google.com D: Linux fixes. + +N: Zhihao Yuan +E: lichray@gmail.com +D: Standard compatibility fixes. diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -99,6 +99,8 @@ __bsd_locale_fallbacks.h __compare/common_comparison_category.h __compare/ordering.h + __compare/synth_three_way.h + __concepts/comparison.h # NOT FOR REVIEW __config __debug __errc diff --git a/libcxx/include/__compare/synth_three_way.h b/libcxx/include/__compare/synth_three_way.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__compare/synth_three_way.h @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___COMPARE_SYNTH_THREE_WAY_H +#define _LIBCPP___COMPARE_SYNTH_THREE_WAY_H + +#include <__config> +#include <__compare/ordering.h> +#include <__concepts/comparison.h> +#include <__utility/declval.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// [expos.only.func] + +constexpr auto __synth_three_way = + [](const _Tp& __t, const _Up& __u) + requires requires { + { __t < __u } -> __boolean_testable; + { __u < __t } -> __boolean_testable; + } + { + if constexpr (three_way_comparable_with<_Tp, _Up>) { + return __t <=> __u; + } else { + if (__t < __u) return weak_ordering::less; + if (__u < __t) return weak_ordering::greater; + return weak_ordering::equivalent; + } + }; + +template +using __synth_three_way_result = decltype(__synth_three_way(declval<_Tp&>(), declval<_Up&>())); + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_SYNTH_THREE_WAY_H diff --git a/libcxx/include/__concepts/comparison.h b/libcxx/include/__concepts/comparison.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__concepts/comparison.h @@ -0,0 +1,135 @@ +// THIS FILE NOT FOR REVIEW (see: https://reviews.llvm.org/D107584 ) +#ifndef INCLUDE___CONCEPTS_COMPARISON_H_ +#define INCLUDE___CONCEPTS_COMPARISON_H_ + +#include <__compare/common_comparison_category.h> +#include <__config> +#include <__utility/forward.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// [concept.same] + +template +concept __same_as_impl = _IsSame<_Tp, _Up>::value; + +template +concept same_as = __same_as_impl<_Tp, _Up> && __same_as_impl<_Up, _Tp>; + +// [concept.convertible] +template +concept convertible_to = + is_convertible_v<_From, _To> && + requires(add_rvalue_reference_t<_From> (&__f)()) { + static_cast<_To>(__f()); + }; + +// [concept.commonref] +template +concept common_reference_with = + same_as, common_reference_t<_Up, _Tp>> && + convertible_to<_Tp, common_reference_t<_Tp, _Up>> && + convertible_to<_Up, common_reference_t<_Tp, _Up>>; + +// [concept.booleantestable] +template +concept __boolean_testable_impl = convertible_to<_Tp, bool>; + +template +concept __boolean_testable = __boolean_testable_impl<_Tp> && requires(_Tp&& __t) { + { !_VSTD::forward<_Tp>(__t) } -> __boolean_testable_impl; +}; + +// [concept.equalitycomparable] +template +concept __weakly_equality_comparable_with = + requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) { + { __t == __u } -> __boolean_testable; + { __t != __u } -> __boolean_testable; + { __u == __t } -> __boolean_testable; + { __u != __t } -> __boolean_testable; + }; + +template +concept equality_comparable = __weakly_equality_comparable_with<_Tp, _Tp>; + +template +concept equality_comparable_with = + equality_comparable<_Tp> && equality_comparable<_Up> && + common_reference_with<__make_const_lvalue_ref<_Tp>, __make_const_lvalue_ref<_Up>> && + equality_comparable< + common_reference_t< + __make_const_lvalue_ref<_Tp>, + __make_const_lvalue_ref<_Up>>> && + __weakly_equality_comparable_with<_Tp, _Up>; + +// [concept.totallyordered] + +template +concept __partially_ordered_with = + requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) { + { __t < __u } -> __boolean_testable; + { __t > __u } -> __boolean_testable; + { __t <= __u } -> __boolean_testable; + { __t >= __u } -> __boolean_testable; + { __u < __t } -> __boolean_testable; + { __u > __t } -> __boolean_testable; + { __u <= __t } -> __boolean_testable; + { __u >= __t } -> __boolean_testable; + }; + +template +concept totally_ordered = equality_comparable<_Tp> && __partially_ordered_with<_Tp, _Tp>; + +template +concept totally_ordered_with = + totally_ordered<_Tp> && totally_ordered<_Up> && + equality_comparable_with<_Tp, _Up> && + totally_ordered< + common_reference_t< + __make_const_lvalue_ref<_Tp>, + __make_const_lvalue_ref<_Up>>> && + __partially_ordered_with<_Tp, _Up>; + +#if !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) + +template +concept __compares_as = + same_as, Cat>; + +template +concept three_way_comparable = + __weakly_equality_comparable_with && + __partially_ordered_with && + requires(const remove_reference_t& a, const remove_reference_t& b) { + { a <=> b } -> __compares_as; + }; + +template +concept three_way_comparable_with = + three_way_comparable && + three_way_comparable && + common_reference_with&, const remove_reference_t&> && + three_way_comparable< + common_reference_t&, const remove_reference_t&>, Cat> && + __weakly_equality_comparable_with && + __partially_ordered_with && + requires(const remove_reference_t& t, const remove_reference_t& u) { + { t <=> u } -> __compares_as; + { u <=> t } -> __compares_as; + }; + +#endif // !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif //INCLUDE___CONCEPTS_COMPARISON_H_ diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -9,6 +9,8 @@ #ifndef _LIBCPP___UTILITY_PAIR_H #define _LIBCPP___UTILITY_PAIR_H +#include <__compare/common_comparison_category.h> +#include <__compare/synth_three_way.h> #include <__config> #include <__functional/unwrap_ref.h> #include <__tuple> @@ -323,6 +325,21 @@ return __x.first == __y.first && __x.second == __y.second; } +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template +inline _LIBCPP_INLINE_VISIBILITY constexpr +common_comparison_category_t< + __synth_three_way_result<_T1>, + __synth_three_way_result<_T2> > +operator<=>(const pair<_T1,_T2>& __x, const pair<_T1,_T2>& __y) +{ + if (auto __c = _VSTD::__synth_three_way(__x.first, __y.first); __c != 0) return __c; + return _VSTD::__synth_three_way(__x.second, __y.second); +} + +#else // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 bool @@ -363,6 +380,8 @@ return !(__y < __x); } +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 typename enable_if diff --git a/libcxx/include/concepts b/libcxx/include/concepts --- a/libcxx/include/concepts +++ b/libcxx/include/concepts @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +// CHANGES IN THIS FILE NOT FOR REVIEW + #ifndef _LIBCPP_CONCEPTS #define _LIBCPP_CONCEPTS @@ -130,6 +132,7 @@ */ #include <__config> +#include <__concepts/comparison.h> #include <__functional/invoke.h> #include <__functional_base> #include @@ -147,35 +150,12 @@ #if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) -// [concept.same] - -template -concept __same_as_impl = _IsSame<_Tp, _Up>::value; - -template -concept same_as = __same_as_impl<_Tp, _Up> && __same_as_impl<_Up, _Tp>; - // [concept.derived] template concept derived_from = is_base_of_v<_Bp, _Dp> && is_convertible_v; -// [concept.convertible] -template -concept convertible_to = - is_convertible_v<_From, _To> && - requires(add_rvalue_reference_t<_From> (&__f)()) { - static_cast<_To>(__f()); - }; - -// [concept.commonref] -template -concept common_reference_with = - same_as, common_reference_t<_Up, _Tp>> && - convertible_to<_Tp, common_reference_t<_Tp, _Up>> && - convertible_to<_Up, common_reference_t<_Tp, _Up>>; - // [concept.common] template concept common_with = @@ -332,66 +312,6 @@ ranges::swap(_VSTD::forward<_Up>(__u), _VSTD::forward<_Tp>(__t)); }; -// [concept.booleantestable] -template -concept __boolean_testable_impl = convertible_to<_Tp, bool>; - -template -concept __boolean_testable = __boolean_testable_impl<_Tp> && requires(_Tp&& __t) { - { !std::forward<_Tp>(__t) } -> __boolean_testable_impl; -}; - -// [concept.equalitycomparable] -template -concept __weakly_equality_comparable_with = - requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) { - { __t == __u } -> __boolean_testable; - { __t != __u } -> __boolean_testable; - { __u == __t } -> __boolean_testable; - { __u != __t } -> __boolean_testable; - }; - -template -concept equality_comparable = __weakly_equality_comparable_with<_Tp, _Tp>; - -template -concept equality_comparable_with = - equality_comparable<_Tp> && equality_comparable<_Up> && - common_reference_with<__make_const_lvalue_ref<_Tp>, __make_const_lvalue_ref<_Up>> && - equality_comparable< - common_reference_t< - __make_const_lvalue_ref<_Tp>, - __make_const_lvalue_ref<_Up>>> && - __weakly_equality_comparable_with<_Tp, _Up>; - -// [concept.totallyordered] - -template -concept __partially_ordered_with = - requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) { - { __t < __u } -> __boolean_testable; - { __t > __u } -> __boolean_testable; - { __t <= __u } -> __boolean_testable; - { __t >= __u } -> __boolean_testable; - { __u < __t } -> __boolean_testable; - { __u > __t } -> __boolean_testable; - { __u <= __t } -> __boolean_testable; - { __u >= __t } -> __boolean_testable; - }; - -template -concept totally_ordered = equality_comparable<_Tp> && __partially_ordered_with<_Tp, _Tp>; - -template -concept totally_ordered_with = - totally_ordered<_Tp> && totally_ordered<_Up> && - equality_comparable_with<_Tp, _Up> && - totally_ordered< - common_reference_t< - __make_const_lvalue_ref<_Tp>, - __make_const_lvalue_ref<_Up>>> && - __partially_ordered_with<_Tp, _Up>; - // [concepts.object] template concept movable = diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -362,6 +362,7 @@ module __compare { module common_comparison_category { private header "__compare/common_comparison_category.h" } module ordering { private header "__compare/ordering.h" } + module synth_three_way { private header "__compare/synth_three_way.h" } } } module complex { @@ -371,6 +372,10 @@ module concepts { header "concepts" export * + // NOT FOR REVIEW + module __concepts { + module comparison { private header "__concepts/comparison.h" } + } } module condition_variable { header "condition_variable" diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -101,6 +101,7 @@ template bool operator> (const pair&, const pair&); // constexpr in C++14 template bool operator>=(const pair&, const pair&); // constexpr in C++14 template bool operator<=(const pair&, const pair&); // constexpr in C++14 +template constexpr auto operator<=>(const pair&, const pair&); // C++20 template pair make_pair(T1&&, T2&&); // constexpr in C++14 template diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/compare/synth_three_way.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/compare/synth_three_way.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/compare/synth_three_way.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__compare/synth_three_way.h'}} +#include <__compare/synth_three_way.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/concepts/comparison.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/concepts/comparison.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/concepts/comparison.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__concepts/comparison.h'}} +#include <__concepts/comparison.h> diff --git a/libcxx/test/libcxx/library/description/conventions/expos.only.func/synth_three_way.pass.cpp b/libcxx/test/libcxx/library/description/conventions/expos.only.func/synth_three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/library/description/conventions/expos.only.func/synth_three_way.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, libcpp-no-concepts + +// template auto __synth_three_way(T __t, U __u); + +#include <__compare/synth_three_way.h> +#include +#include +#include +#include + +#include "test_macros.h" + +// A custom three-way result type +struct CustomResult { + friend constexpr bool operator==(int, const CustomResult&) noexcept { return true; } + friend constexpr bool operator==(const CustomResult&, int) noexcept { return true; } + friend constexpr bool operator<(int, const CustomResult&) noexcept { return false; } + friend constexpr bool operator<(const CustomResult&, int) noexcept { return false; } +}; + +int main(int, char**) { + static_assert(std::__synth_three_way(1, 2) == std::strong_ordering::less); + ASSERT_SAME_TYPE(std::strong_ordering, std::__synth_three_way_result); + + { + struct NoSpaceship { + int value; + constexpr bool operator==(const NoSpaceship&) const = default; + constexpr bool operator<(const NoSpaceship& other) const { return value < other.value; }; + }; + static_assert(NoSpaceship{1} < NoSpaceship{2}, ""); + static_assert(!(NoSpaceship{1} < NoSpaceship{1}), ""); + static_assert(std::__synth_three_way(NoSpaceship{1}, NoSpaceship{1}) == std::weak_ordering::equivalent); + static_assert(std::__synth_three_way(NoSpaceship{2}, NoSpaceship{1}) == std::weak_ordering::greater); + static_assert(std::__synth_three_way(NoSpaceship{1}, NoSpaceship{2}) == std::weak_ordering::less); + ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result); + } + { + struct WithSpaceship { + int value; + constexpr bool operator==(const WithSpaceship&) const = default; + constexpr std::strong_ordering operator<=>(const WithSpaceship& other) const { return value <=> other.value; }; + }; + static_assert(WithSpaceship{1} < WithSpaceship{2}, ""); + static_assert(!(WithSpaceship{1} < WithSpaceship{1}), ""); + static_assert(std::__synth_three_way(WithSpaceship{1}, WithSpaceship{1}) == std::strong_ordering::equivalent, ""); + static_assert(std::__synth_three_way(WithSpaceship{2}, WithSpaceship{1}) == std::strong_ordering::greater, ""); + static_assert(std::__synth_three_way(WithSpaceship{1}, WithSpaceship{2}) == std::strong_ordering::less, ""); + ASSERT_SAME_TYPE(std::strong_ordering, std::__synth_three_way_result); + } + { + struct WithPartialSpaceship { + double value; + constexpr bool operator==(const WithPartialSpaceship&) const = default; + constexpr std::partial_ordering operator<=>(const WithPartialSpaceship& other) const { + return value <=> other.value; + } + }; + static_assert(WithPartialSpaceship{1.0} < WithPartialSpaceship{2.0}, ""); + static_assert(!(WithPartialSpaceship{1.0} < WithPartialSpaceship{1.0}), ""); + static_assert(std::__synth_three_way(WithPartialSpaceship{1.0}, + WithPartialSpaceship{1.0}) == std::partial_ordering::equivalent, ""); + static_assert(std::__synth_three_way(WithPartialSpaceship{2.0}, + WithPartialSpaceship{1.0}) == std::partial_ordering::greater, ""); + static_assert(std::__synth_three_way(WithPartialSpaceship{1.0}, + WithPartialSpaceship{2.0}) == std::partial_ordering::less, ""); + static_assert(std::__synth_three_way(WithPartialSpaceship{std::numeric_limits::quiet_NaN()}, + WithPartialSpaceship{std::numeric_limits::quiet_NaN()}) + == std::partial_ordering::unordered, ""); + ASSERT_SAME_TYPE(std::partial_ordering, std::__synth_three_way_result); + } + { + // Types with operator<=> but no operator== are not three_way_comparable and will fall back to operator< and + // compare as weakly ordered. + struct SpaceshipNoEquals { + constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const { return std::strong_ordering::equivalent; } + constexpr bool operator<(const SpaceshipNoEquals&) const { return false; } + }; + static_assert(std::__synth_three_way(SpaceshipNoEquals{}, SpaceshipNoEquals{}) == std::weak_ordering::equivalent, ""); + ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result); + } + { + // Custom three-way-comparison result types cannot satisfy standard concepts (and therefore synth-three-way) + // because they are not understood by std::common_comparison_category, but they can still be used in + // the same way as standard orderings to do comparisons, and thus can be used by synth-three-way to yield a + // weakly-ordered result. + struct CustomSpaceship { + constexpr CustomResult operator<=>(const CustomSpaceship&) const { return CustomResult(); } + }; + static_assert((CustomSpaceship() <=> CustomSpaceship()) == 0, ""); + static_assert(!(CustomSpaceship() < CustomSpaceship()), ""); + static_assert(std::__synth_three_way(CustomSpaceship(), CustomSpaceship()) == std::weak_ordering::equivalent, ""); + ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result); + } + + return 0; +} diff --git a/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/comparison.pass.cpp b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/comparison.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/utility/pairs/pairs.pair/comparison.pass.cpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, libcpp-no-concepts + +#include +#include +#include +#include + +#include "test_macros.h" + +int main(int, char**) { + { + // Pairs of types that both have strong ordering should compare with strong ordering. + static_assert((std::make_pair(1, 1) <=> std::make_pair(1, 2)) < 0, ""); + static_assert((std::make_pair(2, 1) <=> std::make_pair(1, 2)) > 0, ""); + auto same = std::make_pair(0, 0) <=> std::make_pair(0, 0); + assert(same == 0); + ASSERT_SAME_TYPE(std::strong_ordering, decltype(same)); + } + { + // Pairs of int and a type with no spaceship operator should compare with weak ordering. + struct NoSpaceship { + int value; + constexpr bool operator==(const NoSpaceship&) const = default; + constexpr bool operator<(const NoSpaceship& other) const { return value < other.value; } + }; + static_assert((std::make_pair(1, NoSpaceship{1}) <=> std::make_pair(1, NoSpaceship{2})) < 0, ""); + static_assert((std::make_pair(2, NoSpaceship{1}) <=> std::make_pair(1, NoSpaceship{2})) > 0, ""); + auto same = std::make_pair(0, NoSpaceship{0}) <=> std::make_pair(0, NoSpaceship{0}); + assert(same == 0); + ASSERT_SAME_TYPE(std::weak_ordering, decltype(same)); + } + { + // Pairs of int (strongly ordered) and double (partially ordered) should three-way compare with partial ordering. + static_assert((std::make_pair(1, 1.0) <=> std::make_pair(1, 2.0)) < 0, ""); + static_assert((std::make_pair(2, 1.0) <=> std::make_pair(1, 1.0)) > 0, ""); + static_assert((std::make_pair(std::numeric_limits::quiet_NaN(), 1) + <=> std::make_pair(std::numeric_limits::quiet_NaN(), 2)) + == std::partial_ordering::unordered, ""); + auto same = std::make_pair(0, 0.0) <=> std::make_pair(0, 0.0); + assert(same == 0); + ASSERT_SAME_TYPE(std::partial_ordering, decltype(same)); + } + + return 0; +}