diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -39,7 +39,7 @@ "`3455 `__","Incorrect Postconditions on ``unique_ptr`` move assignment","November 2020","|Nothing To Do|","" "`3460 `__","Unimplementable ``noop_coroutine_handle`` guarantees","November 2020","","" "`3461 `__","``convertible_to``'s description mishandles cv-qualified ``void``","November 2020","","" -"`3465 `__","``compare_partial_order_fallback`` requires ``F < E``","November 2020","","","|spaceship|" +"`3465 `__","``compare_partial_order_fallback`` requires ``F < E``","November 2020","|Complete|","14.0","|spaceship|" "`3466 `__","Specify the requirements for ``promise``/``future``/``shared_future`` consistently","November 2020","","" "`3467 `__","``bool`` can't be an integer-like type","November 2020","","" "`3472 `__","``counted_iterator`` is missing preconditions","November 2020","","","|ranges|" diff --git a/libcxx/docs/Status/Spaceship.rst b/libcxx/docs/Status/Spaceship.rst --- a/libcxx/docs/Status/Spaceship.rst +++ b/libcxx/docs/Status/Spaceship.rst @@ -32,6 +32,10 @@ :header-rows: 1 :widths: auto +.. note:: + + .. [#note-strongorder] ``std::strong_order(long double, long double)`` is not yet implemented. + Misc. Items and TODOs ==================================== diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -7,7 +7,7 @@ | `[comparisons.three.way] `_,| `compare_three_way `_,[cmp.concept],Arthur O'Dwyer,|Complete| | `[cmp.alg] `_,"| `strong_order `_ | `weak_order `_ -| `partial_order `_",None,Arthur O'Dwyer,|In Progress| +| `partial_order `_",None,Arthur O'Dwyer,|Complete| [#note-strongorder]_ | `[alg.three.way] `_,| `lexicographical_compare_three_way `_,[comparisons.three.way],Christopher Di Bella,|In Progress| | `[coroutine.handle.compare] `_,| coroutine_handle,[comparisons.three.way],Unassigned,|Complete| | `[pairs.spec] `_,| `pair `_,[expos.only.func],Kent Ross,|Complete| diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -103,8 +103,11 @@ __charconv/from_chars_result.h __charconv/to_chars_result.h __compare/common_comparison_category.h + __compare/compare_partial_order_fallback.h + __compare/compare_strong_order_fallback.h __compare/compare_three_way.h __compare/compare_three_way_result.h + __compare/compare_weak_order_fallback.h __compare/is_eq.h __compare/ordering.h __compare/partial_order.h diff --git a/libcxx/include/__compare/compare_partial_order_fallback.h b/libcxx/include/__compare/compare_partial_order_fallback.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__compare/compare_partial_order_fallback.h @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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_COMPARE_PARTIAL_ORDER_FALLBACK +#define _LIBCPP___COMPARE_COMPARE_PARTIAL_ORDER_FALLBACK + +#include <__compare/partial_order.h> +#include <__compare/ordering.h> +#include <__config> +#include <__utility/forward.h> +#include <__utility/priority_tag.h> +#include + +#ifndef _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) + +// [cmp.alg] +namespace __compare_partial_order_fallback { + struct __fn { + template + requires is_same_v, decay_t<_Up>> + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __go(_Tp&& __t, _Up&& __u, __priority_tag<1>) + noexcept(noexcept(_VSTD::partial_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)))) + -> decltype( _VSTD::partial_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u))) + { return _VSTD::partial_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)); } + + template + requires is_same_v, decay_t<_Up>> + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) + noexcept(noexcept( _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? partial_ordering::equivalent : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? partial_ordering::less : + _VSTD::forward<_Up>(__u) < _VSTD::forward<_Tp>(__t) ? partial_ordering::greater : + partial_ordering::unordered )) + -> decltype( _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? partial_ordering::equivalent : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? partial_ordering::less : + _VSTD::forward<_Up>(__u) < _VSTD::forward<_Tp>(__t) ? partial_ordering::greater : + partial_ordering::unordered ) + { + return _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? partial_ordering::equivalent : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? partial_ordering::less : + _VSTD::forward<_Up>(__u) < _VSTD::forward<_Tp>(__t) ? partial_ordering::greater : + partial_ordering::unordered; + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t, _Up&& __u) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>()))) + -> decltype( __go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>())) + { return __go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>()); } + }; +} // namespace __compare_partial_order_fallback + +inline namespace __cpo { + inline constexpr auto compare_partial_order_fallback = __compare_partial_order_fallback::__fn{}; +} // namespace __cpo + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_COMPARE_PARTIAL_ORDER_FALLBACK diff --git a/libcxx/include/__compare/compare_strong_order_fallback.h b/libcxx/include/__compare/compare_strong_order_fallback.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__compare/compare_strong_order_fallback.h @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// 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_COMPARE_STRONG_ORDER_FALLBACK +#define _LIBCPP___COMPARE_COMPARE_STRONG_ORDER_FALLBACK + +#include <__compare/strong_order.h> +#include <__compare/ordering.h> +#include <__config> +#include <__utility/forward.h> +#include <__utility/priority_tag.h> +#include + +#ifndef _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) + +// [cmp.alg] +namespace __compare_strong_order_fallback { + struct __fn { + template + requires is_same_v, decay_t<_Up>> + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __go(_Tp&& __t, _Up&& __u, __priority_tag<1>) + noexcept(noexcept(_VSTD::strong_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)))) + -> decltype( _VSTD::strong_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u))) + { return _VSTD::strong_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)); } + + template + requires is_same_v, decay_t<_Up>> + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) + noexcept(noexcept( _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? strong_ordering::equal : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? strong_ordering::less : + strong_ordering::greater )) + -> decltype( _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? strong_ordering::equal : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? strong_ordering::less : + strong_ordering::greater ) + { + return _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? strong_ordering::equal : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? strong_ordering::less : + strong_ordering::greater; + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t, _Up&& __u) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>()))) + -> decltype( __go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>())) + { return __go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>()); } + }; +} // namespace __compare_strong_order_fallback + +inline namespace __cpo { + inline constexpr auto compare_strong_order_fallback = __compare_strong_order_fallback::__fn{}; +} // namespace __cpo + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_COMPARE_STRONG_ORDER_FALLBACK diff --git a/libcxx/include/__compare/compare_weak_order_fallback.h b/libcxx/include/__compare/compare_weak_order_fallback.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__compare/compare_weak_order_fallback.h @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// 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_COMPARE_WEAK_ORDER_FALLBACK +#define _LIBCPP___COMPARE_COMPARE_WEAK_ORDER_FALLBACK + +#include <__compare/weak_order.h> +#include <__compare/ordering.h> +#include <__config> +#include <__utility/forward.h> +#include <__utility/priority_tag.h> +#include + +#ifndef _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) + +// [cmp.alg] +namespace __compare_weak_order_fallback { + struct __fn { + template + requires is_same_v, decay_t<_Up>> + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __go(_Tp&& __t, _Up&& __u, __priority_tag<1>) + noexcept(noexcept(_VSTD::weak_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)))) + -> decltype( _VSTD::weak_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u))) + { return _VSTD::weak_order(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)); } + + template + requires is_same_v, decay_t<_Up>> + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __go(_Tp&& __t, _Up&& __u, __priority_tag<0>) + noexcept(noexcept( _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? weak_ordering::equivalent : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? weak_ordering::less : + weak_ordering::greater )) + -> decltype( _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? weak_ordering::equivalent : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? weak_ordering::less : + weak_ordering::greater ) + { + return _VSTD::forward<_Tp>(__t) == _VSTD::forward<_Up>(__u) ? weak_ordering::equivalent : + _VSTD::forward<_Tp>(__t) < _VSTD::forward<_Up>(__u) ? weak_ordering::less : + weak_ordering::greater; + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t, _Up&& __u) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>()))) + -> decltype( __go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>())) + { return __go(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u), __priority_tag<1>()); } + }; +} // namespace __compare_weak_order_fallback + +inline namespace __cpo { + inline constexpr auto compare_weak_order_fallback = __compare_weak_order_fallback::__fn{}; +} // namespace __cpo + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_COMPARE_WEAK_ORDER_FALLBACK diff --git a/libcxx/include/compare b/libcxx/include/compare --- a/libcxx/include/compare +++ b/libcxx/include/compare @@ -51,9 +51,14 @@ struct compare_three_way; // C++20 // [cmp.alg], comparison algorithms - template constexpr strong_ordering strong_order(const T& a, const T& b); - template constexpr weak_ordering weak_order(const T& a, const T& b); - template constexpr partial_ordering partial_order(const T& a, const T& b); + inline namespace unspecified { + inline constexpr unspecified strong_order = unspecified; + inline constexpr unspecified weak_order = unspecified; + inline constexpr unspecified partial_order = unspecified; + inline constexpr unspecified compare_strong_order_fallback = unspecified; + inline constexpr unspecified compare_weak_order_fallback = unspecified; + inline constexpr unspecified compare_partial_order_fallback = unspecified; + } // [cmp.partialord], Class partial_ordering class partial_ordering { @@ -136,8 +141,11 @@ */ #include <__compare/common_comparison_category.h> +#include <__compare/compare_partial_order_fallback.h> +#include <__compare/compare_strong_order_fallback.h> #include <__compare/compare_three_way.h> #include <__compare/compare_three_way_result.h> +#include <__compare/compare_weak_order_fallback.h> #include <__compare/is_eq.h> #include <__compare/ordering.h> #include <__compare/partial_order.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -372,16 +372,19 @@ export * module __compare { - module common_comparison_category { private header "__compare/common_comparison_category.h" } - module compare_three_way { private header "__compare/compare_three_way.h" } - module compare_three_way_result { private header "__compare/compare_three_way_result.h" } - module is_eq { private header "__compare/is_eq.h" } - module ordering { private header "__compare/ordering.h" } - module partial_order { private header "__compare/partial_order.h" } - module strong_order { private header "__compare/strong_order.h" } - module synth_three_way { private header "__compare/synth_three_way.h" } - module three_way_comparable { private header "__compare/three_way_comparable.h" } - module weak_order { private header "__compare/weak_order.h" } + module common_comparison_category { private header "__compare/common_comparison_category.h" } + module compare_partial_order_fallback { private header "__compare/compare_partial_order_fallback.h" } + module compare_strong_order_fallback { private header "__compare/compare_strong_order_fallback.h" } + module compare_three_way { private header "__compare/compare_three_way.h" } + module compare_three_way_result { private header "__compare/compare_three_way_result.h" } + module compare_weak_order_fallback { private header "__compare/compare_weak_order_fallback.h" } + module is_eq { private header "__compare/is_eq.h" } + module ordering { private header "__compare/ordering.h" } + module partial_order { private header "__compare/partial_order.h" } + module strong_order { private header "__compare/strong_order.h" } + module synth_three_way { private header "__compare/synth_three_way.h" } + module three_way_comparable { private header "__compare/three_way_comparable.h" } + module weak_order { private header "__compare/weak_order.h" } } } module complex { diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_partial_order_fallback.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_partial_order_fallback.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_partial_order_fallback.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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/compare_partial_order_fallback.h'}} +#include <__compare/compare_partial_order_fallback.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_strong_order_fallback.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_strong_order_fallback.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_strong_order_fallback.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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/compare_strong_order_fallback.h'}} +#include <__compare/compare_strong_order_fallback.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_weak_order_fallback.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_weak_order_fallback.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_weak_order_fallback.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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/compare_weak_order_fallback.h'}} +#include <__compare/compare_weak_order_fallback.h> diff --git a/libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp b/libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp @@ -0,0 +1,326 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts + +// + +// template constexpr partial_ordering compare_partial_order_fallback(const T& a, const T& b); + +#include + +#include +#include +#include // std::size +#include +#include +#include + +#include "test_macros.h" + +template +constexpr auto has_partial_order(T&& t, U&& u) + -> decltype(std::compare_partial_order_fallback(static_cast(t), static_cast(u)), true) +{ + return true; +} + +constexpr bool has_partial_order(...) { + return false; +} + +namespace N11 { + struct A {}; + struct B {}; + std::strong_ordering partial_order(const A&, const A&) { return std::strong_ordering::less; } + std::strong_ordering partial_order(const A&, const B&); +} + +void test_1_1() +{ + // If the decayed types of E and F differ, partial_order(E, F) is ill-formed. + + static_assert( has_partial_order(1, 2)); + static_assert(!has_partial_order(1, (short)2)); + static_assert(!has_partial_order(1, 2.0)); + static_assert(!has_partial_order(1.0f, 2.0)); + + static_assert( has_partial_order((int*)nullptr, (int*)nullptr)); + static_assert(!has_partial_order((int*)nullptr, (const int*)nullptr)); + static_assert(!has_partial_order((const int*)nullptr, (int*)nullptr)); + static_assert( has_partial_order((const int*)nullptr, (const int*)nullptr)); + + N11::A a; + N11::B b; + static_assert( has_partial_order(a, a)); + static_assert(!has_partial_order(a, b)); +} + +namespace N12 { + struct A {}; + std::strong_ordering partial_order(A&, A&&) { return std::strong_ordering::less; } + std::weak_ordering partial_order(A&&, A&&) { return std::weak_ordering::equivalent; } + std::strong_ordering partial_order(const A&, const A&); + + struct B { + friend int partial_order(B, B); + }; + + struct PartialOrder { + explicit operator std::partial_ordering() const { return std::partial_ordering::less; } + }; + struct C { + bool touched = false; + friend PartialOrder partial_order(C& lhs, C&) { lhs.touched = true; return PartialOrder(); } + }; +} + +void test_1_2() +{ + // Otherwise, partial_ordering(partial_order(E, F)) + // if it is a well-formed expression with overload resolution performed + // in a context that does not include a declaration of std::partial_order. + + // Test that partial_order does not const-qualify the forwarded arguments. + N12::A a; + assert(std::compare_partial_order_fallback(a, std::move(a)) == std::partial_ordering::less); + assert(std::compare_partial_order_fallback(std::move(a), std::move(a)) == std::partial_ordering::equivalent); + + // The type of partial_order(e,f) must be explicitly convertible to partial_ordering. + N12::B b; + static_assert(!has_partial_order(b, b)); + + N12::C c1, c2; + ASSERT_SAME_TYPE(decltype(std::compare_partial_order_fallback(c1, c2)), std::partial_ordering); + assert(std::partial_order(c1, c2) == std::partial_ordering::less); + assert(c1.touched); + assert(!c2.touched); +} + +namespace N13 { + // Compare to N12::A. + struct A {}; + bool operator==(const A&, const A&); + constexpr std::partial_ordering operator<=>(A&, A&&) { return std::partial_ordering::less; } + constexpr std::partial_ordering operator<=>(A&&, A&&) { return std::partial_ordering::equivalent; } + std::partial_ordering operator<=>(const A&, const A&); + static_assert(std::three_way_comparable); + + struct B { + std::partial_ordering operator<=>(const B&) const; // lacks operator== + }; + static_assert(!std::three_way_comparable); + + struct C { + bool *touched; + bool operator==(const C&) const; + constexpr std::partial_ordering operator<=>(const C& rhs) const { + *rhs.touched = true; + return std::partial_ordering::equivalent; + } + }; + static_assert(std::three_way_comparable); +} + +constexpr bool test_1_3() +{ + // Otherwise, partial_ordering(compare_three_way()(E, F)) if it is a well-formed expression. + + // Test neither partial_order nor compare_three_way const-qualify the forwarded arguments. + N13::A a; + assert(std::compare_partial_order_fallback(a, std::move(a)) == std::partial_ordering::less); + assert(std::compare_partial_order_fallback(std::move(a), std::move(a)) == std::partial_ordering::equivalent); + + N13::B b; + static_assert(!has_partial_order(b, b)); + + // Test that the arguments are passed to <=> in the correct order. + bool c1_touched = false; + bool c2_touched = false; + N13::C c1 = {&c1_touched}; + N13::C c2 = {&c2_touched}; + assert(std::compare_partial_order_fallback(c1, c2) == std::partial_ordering::equivalent); + assert(!c1_touched); + assert(c2_touched); + + // For partial_order, this bullet point takes care of floating-point types; + // they receive their natural partial order. + { + using F = float; + F nan = std::numeric_limits::quiet_NaN(); + assert(std::compare_partial_order_fallback(F(1), F(2)) == std::partial_ordering::less); + assert(std::compare_partial_order_fallback(F(0), -F(0)) == std::partial_ordering::equivalent); +#ifndef TEST_COMPILER_GCC // GCC can't compare NaN to non-NaN in a constant-expression + assert(std::compare_partial_order_fallback(nan, F(1)) == std::partial_ordering::unordered); +#endif + assert(std::compare_partial_order_fallback(nan, nan) == std::partial_ordering::unordered); + } + { + using F = double; + F nan = std::numeric_limits::quiet_NaN(); + assert(std::compare_partial_order_fallback(F(1), F(2)) == std::partial_ordering::less); + assert(std::compare_partial_order_fallback(F(0), -F(0)) == std::partial_ordering::equivalent); +#ifndef TEST_COMPILER_GCC + assert(std::compare_partial_order_fallback(nan, F(1)) == std::partial_ordering::unordered); +#endif + assert(std::compare_partial_order_fallback(nan, nan) == std::partial_ordering::unordered); + } + { + using F = long double; + F nan = std::numeric_limits::quiet_NaN(); + assert(std::compare_partial_order_fallback(F(1), F(2)) == std::partial_ordering::less); + assert(std::compare_partial_order_fallback(F(0), -F(0)) == std::partial_ordering::equivalent); +#ifndef TEST_COMPILER_GCC + assert(std::compare_partial_order_fallback(nan, F(1)) == std::partial_ordering::unordered); +#endif + assert(std::compare_partial_order_fallback(nan, nan) == std::partial_ordering::unordered); + } + + return true; +} + +namespace N14 { + struct A {}; + constexpr std::strong_ordering weak_order(A&, A&&) { return std::strong_ordering::less; } + constexpr std::strong_ordering weak_order(A&&, A&&) { return std::strong_ordering::equal; } + std::strong_ordering weak_order(const A&, const A&); + + struct B { + friend std::partial_ordering weak_order(B, B); + }; + + struct StrongOrder { + operator std::strong_ordering() const { return std::strong_ordering::less; } + }; + struct C { + friend StrongOrder weak_order(C& lhs, C&); + }; + + struct WeakOrder { + constexpr explicit operator std::weak_ordering() const { return std::weak_ordering::less; } + operator std::partial_ordering() const = delete; + }; + struct D { + bool touched = false; + friend constexpr WeakOrder weak_order(D& lhs, D&) { lhs.touched = true; return WeakOrder(); } + }; +} + +constexpr bool test_1_4() +{ + // Otherwise, partial_ordering(weak_order(E, F)) [that is, std::weak_order] + // if it is a well-formed expression. + + // Test that partial_order and weak_order do not const-qualify the forwarded arguments. + N14::A a; + assert(std::compare_partial_order_fallback(a, std::move(a)) == std::partial_ordering::less); + assert(std::compare_partial_order_fallback(std::move(a), std::move(a)) == std::partial_ordering::equivalent); + + // The type of ADL weak_order(e,f) must be explicitly convertible to weak_ordering + // (not just to partial_ordering), or else std::weak_order(e,f) won't exist. + N14::B b; + static_assert(!has_partial_order(b, b)); + + // The type of ADL weak_order(e,f) must be explicitly convertible to weak_ordering + // (not just to strong_ordering), or else std::weak_order(e,f) won't exist. + N14::C c; + static_assert(!has_partial_order(c, c)); + + N14::D d1, d2; + ASSERT_SAME_TYPE(decltype(std::compare_partial_order_fallback(d1, d2)), std::partial_ordering); + assert(std::compare_partial_order_fallback(d1, d2) == std::partial_ordering::less); + assert(d1.touched); + assert(!d2.touched); + + return true; +} + +namespace N2 { + struct Stats { + int eq = 0; + int lt = 0; + }; + struct A { + Stats *stats_; + double value_; + constexpr explicit A(Stats *stats, double value) : stats_(stats), value_(value) {} + friend constexpr bool operator==(A a, A b) { a.stats_->eq += 1; return a.value_ == b.value_; } + friend constexpr bool operator<(A a, A b) { a.stats_->lt += 1; return a.value_ < b.value_; } + }; + struct NoEquality { + friend bool operator<(NoEquality, NoEquality); + }; + struct VC1 { + // Deliberately asymmetric `const` qualifiers here. + friend bool operator==(const VC1&, VC1&); + friend bool operator<(const VC1&, VC1&); + }; + struct VC2 { + // Deliberately asymmetric `const` qualifiers here. + friend bool operator==(const VC2&, VC2&); + friend bool operator==(VC2&, const VC2&) = delete; + friend bool operator<(const VC2&, VC2&); + friend bool operator<(VC2&, const VC2&); + }; +} + +constexpr bool test_2() +{ + { + N2::Stats stats; + N2::Stats bstats; + assert(std::compare_partial_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 1)) == std::partial_ordering::equivalent); + assert(stats.eq == 1 && stats.lt == 0); + stats = {}; + assert(std::compare_partial_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 2)) == std::partial_ordering::less); + assert(stats.eq == 1 && stats.lt == 1); + stats = {}; + assert(std::compare_partial_order_fallback(N2::A(&stats, 2), N2::A(&bstats, 1)) == std::partial_ordering::greater); + assert(stats.eq == 1 && stats.lt == 1 && bstats.lt == 1); + stats = {}; + bstats = {}; + double nan = std::numeric_limits::quiet_NaN(); + assert(std::compare_partial_order_fallback(N2::A(&stats, nan), N2::A(&bstats, nan)) == std::partial_ordering::unordered); + assert(stats.eq == 1 && stats.lt == 1 && bstats.lt == 1); + } + { + N2::NoEquality ne; + assert(!has_partial_order(ne, ne)); + } + { + // LWG3465: (cvc < vc) is well-formed, (vc < cvc) is not. Substitution failure. + N2::VC1 vc; + const N2::VC1 cvc; + assert(!has_partial_order(cvc, vc)); + assert(!has_partial_order(vc, cvc)); + } + { + // LWG3465: (cvc == vc) is well-formed, (vc == cvc) is not. That's fine. + N2::VC2 vc; + const N2::VC2 cvc; + assert( has_partial_order(cvc, vc)); + assert(!has_partial_order(vc, cvc)); + } + return true; +} + +int main(int, char**) +{ + test_1_1(); + test_1_2(); + test_1_3(); + test_1_4(); + test_2(); + + static_assert(test_1_3()); + static_assert(test_1_4()); + static_assert(test_2()); + + return 0; +} diff --git a/libcxx/test/std/language.support/cmp/cmp.alg/compare_strong_order_fallback.pass.cpp b/libcxx/test/std/language.support/cmp/cmp.alg/compare_strong_order_fallback.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/cmp/cmp.alg/compare_strong_order_fallback.pass.cpp @@ -0,0 +1,530 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts + +// + +// template constexpr strong_ordering compare_strong_order_fallback(const T& a, const T& b); + +#include + +#include +#include +#include // std::size +#include +#include +#include + +#include "test_macros.h" + +#if defined(__i386__) + #define TEST_BUGGY_SIGNALING_NAN +#endif + +template +constexpr auto has_strong_order(T&& t, U&& u) + -> decltype(std::compare_strong_order_fallback(static_cast(t), static_cast(u)), true) +{ + return true; +} + +constexpr bool has_strong_order(...) { + return false; +} + +namespace N11 { + struct A {}; + struct B {}; + std::strong_ordering strong_order(const A&, const A&) { return std::strong_ordering::less; } + std::strong_ordering strong_order(const A&, const B&); +} + +void test_1_1() +{ + // If the decayed types of E and F differ, strong_order(E, F) is ill-formed. + + static_assert( has_strong_order(1, 2)); + static_assert(!has_strong_order(1, (short)2)); + static_assert(!has_strong_order(1, 2.0)); + static_assert(!has_strong_order(1.0f, 2.0)); + + static_assert( has_strong_order((int*)nullptr, (int*)nullptr)); + static_assert(!has_strong_order((int*)nullptr, (const int*)nullptr)); + static_assert(!has_strong_order((const int*)nullptr, (int*)nullptr)); + static_assert( has_strong_order((const int*)nullptr, (const int*)nullptr)); + + N11::A a; + N11::B b; + static_assert( has_strong_order(a, a)); + static_assert(!has_strong_order(a, b)); +} + +namespace N12 { + struct A {}; + std::strong_ordering strong_order(A&, A&&) { return std::strong_ordering::less; } + std::strong_ordering strong_order(A&&, A&&) { return std::strong_ordering::equal; } + std::strong_ordering strong_order(const A&, const A&); + + struct B { + friend std::weak_ordering strong_order(B&, B&); + }; + + struct StrongOrder { + explicit operator std::strong_ordering() const { return std::strong_ordering::less; } + }; + struct C { + bool touched = false; + friend StrongOrder strong_order(C& lhs, C&) { lhs.touched = true; return StrongOrder(); } + }; +} + +void test_1_2() +{ + // Otherwise, strong_ordering(strong_order(E, F)) + // if it is a well-formed expression with overload resolution performed + // in a context that does not include a declaration of std::strong_order. + + // Test that strong_order does not const-qualify the forwarded arguments. + N12::A a; + assert(std::compare_strong_order_fallback(a, std::move(a)) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(std::move(a), std::move(a)) == std::strong_ordering::equal); + + // The type of strong_order(e,f) must be explicitly convertible to strong_ordering. + N12::B b; + static_assert(!has_strong_order(b, b)); + + N12::C c1, c2; + ASSERT_SAME_TYPE(decltype(std::compare_strong_order_fallback(c1, c2)), std::strong_ordering); + assert(std::compare_strong_order_fallback(c1, c2) == std::strong_ordering::less); + assert(c1.touched); + assert(!c2.touched); +} + +template +constexpr bool test_1_3() +{ + // Otherwise, if the decayed type T of E is a floating-point type, + // yields a value of type strong_ordering that is consistent with + // the ordering observed by T's comparison operators, + // and if numeric_limits::is_iec559 is true, is additionally consistent with + // the totalOrder operation as specified in ISO/IEC/IEEE 60559. + + static_assert(std::numeric_limits::is_iec559); + + ASSERT_SAME_TYPE(decltype(std::compare_strong_order_fallback(F(0), F(0))), std::strong_ordering); + + F v[] = { + -std::numeric_limits::infinity(), + std::numeric_limits::lowest(), // largest (finite) negative number + F(-1.0), F(-0.1), + -std::numeric_limits::min(), // smallest (normal) negative number + F(-0.0), // negative zero + F(0.0), + std::numeric_limits::min(), // smallest (normal) positive number + F(0.1), F(1.0), F(2.0), F(3.14), + std::numeric_limits::max(), // largest (finite) positive number + std::numeric_limits::infinity(), + }; + + static_assert(std::size(v) == 14); + + // Sanity-check that array 'v' is indeed in the right order. + for (int i=0; i < 14; ++i) { + for (int j=0; j < 14; ++j) { + auto naturalOrder = (v[i] <=> v[j]); + if (v[i] == 0 && v[j] == 0) { + assert(naturalOrder == std::partial_ordering::equivalent); + } else { + assert(naturalOrder == std::partial_ordering::unordered || naturalOrder == (i <=> j)); + } + } + } + + assert(std::compare_strong_order_fallback(v[0], v[0]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[0], v[1]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[2]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[3]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[4]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[5]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[6]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[0], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[1], v[1]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[1], v[2]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[3]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[4]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[5]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[6]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[1], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[2], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[2], v[2]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[2], v[3]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[4]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[5]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[6]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[2], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[3], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[3], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[3], v[3]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[3], v[4]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[5]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[6]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[3], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[4], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[4], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[4], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[4], v[4]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[4], v[5]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[6]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[4], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[5], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[5], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[5], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[5], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[5], v[5]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[5], v[6]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[5], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[6], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[6], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[6], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[6], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[6], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[6], v[6]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[6], v[7]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[6], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[7], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[7], v[7]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[7], v[8]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[7], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[7], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[7], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[7], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[7], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[8], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[7]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[8], v[8]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[8], v[9]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[8], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[8], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[8], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[8], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[9], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[7]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[8]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[9], v[9]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[9], v[10]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[9], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[9], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[9], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[10], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[7]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[8]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[9]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[10], v[10]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[10], v[11]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[10], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[10], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[11], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[7]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[8]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[9]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[10]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[11], v[11]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[11], v[12]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[11], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[12], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[7]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[8]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[9]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[10]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[11]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[12], v[12]) == std::strong_ordering::equal); + assert(std::compare_strong_order_fallback(v[12], v[13]) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(v[13], v[0]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[1]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[2]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[3]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[4]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[5]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[6]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[7]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[8]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[9]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[10]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[11]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[12]) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(v[13], v[13]) == std::strong_ordering::equal); + + + // There's no way to produce a specifically positive or negative NAN + // at compile-time, so the NAN-related tests must be runtime-only. + + if (!std::is_constant_evaluated()) { + F nq = _VSTD::copysign(std::numeric_limits::quiet_NaN(), F(-1)); + F ns = _VSTD::copysign(std::numeric_limits::signaling_NaN(), F(-1)); + F ps = _VSTD::copysign(std::numeric_limits::signaling_NaN(), F(+1)); + F pq = _VSTD::copysign(std::numeric_limits::quiet_NaN(), F(+1)); + + assert(std::compare_strong_order_fallback(nq, nq) == std::strong_ordering::equal); +#ifndef TEST_BUGGY_SIGNALING_NAN + assert(std::compare_strong_order_fallback(nq, ns) == std::strong_ordering::less); +#endif + for (int i=0; i < 14; ++i) { + assert(std::compare_strong_order_fallback(nq, v[i]) == std::strong_ordering::less); + } + assert(std::compare_strong_order_fallback(nq, ps) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(nq, pq) == std::strong_ordering::less); + +#ifndef TEST_BUGGY_SIGNALING_NAN + assert(std::compare_strong_order_fallback(ns, nq) == std::strong_ordering::greater); +#endif + assert(std::compare_strong_order_fallback(ns, ns) == std::strong_ordering::equal); + for (int i=0; i < 14; ++i) { + assert(std::compare_strong_order_fallback(ns, v[i]) == std::strong_ordering::less); + } + assert(std::compare_strong_order_fallback(ns, ps) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(ns, pq) == std::strong_ordering::less); + + assert(std::compare_strong_order_fallback(ps, nq) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(ps, ns) == std::strong_ordering::greater); + for (int i=0; i < 14; ++i) { + assert(std::compare_strong_order_fallback(ps, v[i]) == std::strong_ordering::greater); + } + assert(std::compare_strong_order_fallback(ps, ps) == std::strong_ordering::equal); +#ifndef TEST_BUGGY_SIGNALING_NAN + assert(std::compare_strong_order_fallback(ps, pq) == std::strong_ordering::less); +#endif + + assert(std::compare_strong_order_fallback(pq, nq) == std::strong_ordering::greater); + assert(std::compare_strong_order_fallback(pq, ns) == std::strong_ordering::greater); + for (int i=0; i < 14; ++i) { + assert(std::compare_strong_order_fallback(pq, v[i]) == std::strong_ordering::greater); + } +#ifndef TEST_BUGGY_SIGNALING_NAN + assert(std::compare_strong_order_fallback(pq, ps) == std::strong_ordering::greater); +#endif + assert(std::compare_strong_order_fallback(pq, pq) == std::strong_ordering::equal); + } + + return true; +} + +namespace N14 { + // Compare to N12::A. + struct A {}; + bool operator==(const A&, const A&); + constexpr std::strong_ordering operator<=>(A&, A&&) { return std::strong_ordering::less; } + constexpr std::strong_ordering operator<=>(A&&, A&&) { return std::strong_ordering::equal; } + std::strong_ordering operator<=>(const A&, const A&); + static_assert(std::three_way_comparable); + + struct B { + std::strong_ordering operator<=>(const B&) const; // lacks operator== + }; + static_assert(!std::three_way_comparable); + + struct C { + bool *touched; + bool operator==(const C&) const; + constexpr std::strong_ordering operator<=>(const C& rhs) const { + *rhs.touched = true; + return std::strong_ordering::equal; + } + }; + static_assert(std::three_way_comparable); +} + +constexpr bool test_1_4() +{ + // Otherwise, strong_ordering(compare_three_way()(E, F)) if it is a well-formed expression. + + // Test neither strong_order nor compare_three_way const-qualify the forwarded arguments. + N14::A a; + assert(std::compare_strong_order_fallback(a, std::move(a)) == std::strong_ordering::less); + assert(std::compare_strong_order_fallback(std::move(a), std::move(a)) == std::strong_ordering::equal); + + N14::B b; + static_assert(!has_strong_order(b, b)); + + // Test that the arguments are passed to <=> in the correct order. + bool c1_touched = false; + bool c2_touched = false; + N14::C c1 = {&c1_touched}; + N14::C c2 = {&c2_touched}; + assert(std::compare_strong_order_fallback(c1, c2) == std::strong_ordering::equal); + assert(!c1_touched); + assert(c2_touched); + + return true; +} + +namespace N2 { + struct Stats { + int eq = 0; + int lt = 0; + }; + struct A { + Stats *stats_; + double value_; + constexpr explicit A(Stats *stats, double value) : stats_(stats), value_(value) {} + friend constexpr bool operator==(A a, A b) { a.stats_->eq += 1; return a.value_ == b.value_; } + friend constexpr bool operator<(A a, A b) { a.stats_->lt += 1; return a.value_ < b.value_; } + }; + struct NoEquality { + friend bool operator<(NoEquality, NoEquality); + }; + struct VC1 { + // Deliberately asymmetric `const` qualifiers here. + friend bool operator==(const VC1&, VC1&); + friend bool operator<(const VC1&, VC1&); + }; + struct VC2 { + // Deliberately asymmetric `const` qualifiers here. + friend bool operator==(const VC2&, VC2&); + friend bool operator==(VC2&, const VC2&) = delete; + friend bool operator<(const VC2&, VC2&); + friend bool operator<(VC2&, const VC2&); + }; +} + +constexpr bool test_2() +{ + { + N2::Stats stats; + assert(std::compare_strong_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 1)) == std::strong_ordering::equal); + assert(stats.eq == 1 && stats.lt == 0); + stats = {}; + assert(std::compare_strong_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 2)) == std::strong_ordering::less); + assert(stats.eq == 1 && stats.lt == 1); + stats = {}; + assert(std::compare_strong_order_fallback(N2::A(&stats, 2), N2::A(nullptr, 1)) == std::strong_ordering::greater); + assert(stats.eq == 1 && stats.lt == 1); + } + { + N2::NoEquality ne; + assert(!has_strong_order(ne, ne)); + } + { + // LWG3465: (cvc < vc) is well-formed, (vc < cvc) is not. That's fine, for strong ordering. + N2::VC1 vc; + const N2::VC1 cvc; + assert( has_strong_order(cvc, vc)); + assert(!has_strong_order(vc, cvc)); + } + { + // LWG3465: (cvc == vc) is well-formed, (vc == cvc) is not. That's fine. + N2::VC2 vc; + const N2::VC2 cvc; + assert( has_strong_order(cvc, vc)); + assert(!has_strong_order(vc, cvc)); + } + return true; +} + +int main(int, char**) +{ + test_1_1(); + test_1_2(); + test_1_3(); + test_1_3(); + // test_1_3(); // UNIMPLEMENTED + test_1_4(); + test_2(); + + static_assert(test_1_3()); + static_assert(test_1_3()); + // static_assert(test_1_3()); // UNIMPLEMENTED + static_assert(test_1_4()); + static_assert(test_2()); + + return 0; +} diff --git a/libcxx/test/std/language.support/cmp/cmp.alg/compare_weak_order_fallback.pass.cpp b/libcxx/test/std/language.support/cmp/cmp.alg/compare_weak_order_fallback.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/cmp/cmp.alg/compare_weak_order_fallback.pass.cpp @@ -0,0 +1,579 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts + +// + +// template constexpr weak_ordering compare_weak_order_fallback(const T& a, const T& b); + +#include + +#include +#include +#include // std::size +#include +#include +#include + +#include "test_macros.h" + +template +constexpr auto has_weak_order(T&& t, U&& u) + -> decltype(std::compare_weak_order_fallback(static_cast(t), static_cast(u)), true) +{ + return true; +} + +constexpr bool has_weak_order(...) { + return false; +} + +namespace N11 { + struct A {}; + struct B {}; + std::strong_ordering weak_order(const A&, const A&) { return std::strong_ordering::less; } + std::strong_ordering weak_order(const A&, const B&); +} + +void test_1_1() +{ + // If the decayed types of E and F differ, weak_order(E, F) is ill-formed. + + static_assert( has_weak_order(1, 2)); + static_assert(!has_weak_order(1, (short)2)); + static_assert(!has_weak_order(1, 2.0)); + static_assert(!has_weak_order(1.0f, 2.0)); + + static_assert( has_weak_order((int*)nullptr, (int*)nullptr)); + static_assert(!has_weak_order((int*)nullptr, (const int*)nullptr)); + static_assert(!has_weak_order((const int*)nullptr, (int*)nullptr)); + static_assert( has_weak_order((const int*)nullptr, (const int*)nullptr)); + + N11::A a; + N11::B b; + static_assert( has_weak_order(a, a)); + static_assert(!has_weak_order(a, b)); +} + +namespace N12 { + struct A {}; + std::strong_ordering weak_order(A&, A&&) { return std::strong_ordering::less; } + std::strong_ordering weak_order(A&&, A&&) { return std::strong_ordering::equal; } + std::strong_ordering weak_order(const A&, const A&); + + struct B { + friend std::partial_ordering weak_order(B&, B&); + }; + + struct WeakOrder { + explicit operator std::weak_ordering() const { return std::weak_ordering::less; } + }; + struct C { + bool touched = false; + friend WeakOrder weak_order(C& lhs, C&) { lhs.touched = true; return WeakOrder(); } + }; +} + +void test_1_2() +{ + // Otherwise, weak_ordering(weak_order(E, F)) + // if it is a well-formed expression with overload resolution performed + // in a context that does not include a declaration of std::weak_order. + + // Test that weak_order does not const-qualify the forwarded arguments. + N12::A a; + assert(std::compare_weak_order_fallback(a, std::move(a)) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(std::move(a), std::move(a)) == std::weak_ordering::equivalent); + + // The type of weak_order(e,f) must be explicitly convertible to weak_ordering. + N12::B b; + static_assert(!has_weak_order(b, b)); + + N12::C c1, c2; + ASSERT_SAME_TYPE(decltype(std::compare_weak_order_fallback(c1, c2)), std::weak_ordering); + assert(std::compare_weak_order_fallback(c1, c2) == std::weak_ordering::less); + assert(c1.touched); + assert(!c2.touched); +} + +template +constexpr bool test_1_3() +{ + // Otherwise, if the decayed type T of E is a floating-point type, + // yields a value of type weak_ordering that is consistent with + // the ordering observed by T's comparison operators and strong_order, + // and if numeric_limits::is_iec559 is true, is additionally consistent with + // the following equivalence classes... + + // std::numeric_limits::is_iec559 is usually true. + // It is false for F=long double on AIX; but this test is still expected + // to pass (e.g. std::weak_order(+0, -0) == weak_ordering::equivalent, + // even on AIX). + + ASSERT_SAME_TYPE(decltype(std::compare_weak_order_fallback(F(0), F(0))), std::weak_ordering); + + F v[] = { + -std::numeric_limits::infinity(), + std::numeric_limits::lowest(), // largest (finite) negative number + F(-1.0), F(-0.1), + -std::numeric_limits::min(), // smallest (normal) negative number + F(-0.0), // negative zero + F(0.0), + std::numeric_limits::min(), // smallest (normal) positive number + F(0.1), F(1.0), F(2.0), F(3.14), + std::numeric_limits::max(), // largest (finite) positive number + std::numeric_limits::infinity(), + }; + + static_assert(std::size(v) == 14); + + // Sanity-check that array 'v' is indeed in the right order. + for (int i=0; i < 14; ++i) { + for (int j=0; j < 14; ++j) { + auto naturalOrder = (v[i] <=> v[j]); + if (v[i] == 0 && v[j] == 0) { + assert(naturalOrder == std::partial_ordering::equivalent); + } else { + assert(naturalOrder == std::partial_ordering::unordered || naturalOrder == (i <=> j)); + } + } + } + + assert(std::compare_weak_order_fallback(v[0], v[0]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[0], v[1]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[2]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[3]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[4]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[5]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[6]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[0], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[1], v[1]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[1], v[2]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[3]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[4]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[5]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[6]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[1], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[2], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[2], v[2]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[2], v[3]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[4]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[5]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[6]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[2], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[3], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[3], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[3], v[3]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[3], v[4]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[5]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[6]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[3], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[4], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[4], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[4], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[4], v[4]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[4], v[5]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[6]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[4], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[5], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[5], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[5], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[5], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[5], v[5]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[5], v[6]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[5], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[5], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[6], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[6], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[6], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[6], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[6], v[5]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[6], v[6]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[6], v[7]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[6], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[7], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[7], v[7]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[7], v[8]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[7], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[7], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[7], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[7], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[7], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[8], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[7]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[8], v[8]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[8], v[9]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[8], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[8], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[8], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[8], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[9], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[7]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[8]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[9], v[9]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[9], v[10]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[9], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[9], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[9], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[10], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[7]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[8]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[9]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[10], v[10]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[10], v[11]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[10], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[10], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[11], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[7]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[8]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[9]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[10]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[11], v[11]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[11], v[12]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[11], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[12], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[7]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[8]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[9]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[10]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[11]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[12], v[12]) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(v[12], v[13]) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(v[13], v[0]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[1]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[2]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[3]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[4]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[5]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[6]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[7]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[8]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[9]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[10]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[11]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[12]) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(v[13], v[13]) == std::weak_ordering::equivalent); + + + // There's no way to produce a specifically positive or negative NAN + // at compile-time, so the NAN-related tests must be runtime-only. + + if (!std::is_constant_evaluated()) { + F nq = _VSTD::copysign(std::numeric_limits::quiet_NaN(), F(-1)); + F ns = _VSTD::copysign(std::numeric_limits::signaling_NaN(), F(-1)); + F ps = _VSTD::copysign(std::numeric_limits::signaling_NaN(), F(+1)); + F pq = _VSTD::copysign(std::numeric_limits::quiet_NaN(), F(+1)); + + assert(std::compare_weak_order_fallback(nq, nq) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(nq, ns) == std::weak_ordering::equivalent); + for (int i=0; i < 14; ++i) { + assert(std::compare_weak_order_fallback(nq, v[i]) == std::weak_ordering::less); + } + assert(std::compare_weak_order_fallback(nq, ps) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(nq, pq) == std::weak_ordering::less); + + assert(std::compare_weak_order_fallback(ns, nq) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(ns, ns) == std::weak_ordering::equivalent); + for (int i=0; i < 14; ++i) { + assert(std::compare_weak_order_fallback(ns, v[i]) == std::weak_ordering::less); + } + assert(std::compare_weak_order_fallback(ns, ps) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(ns, pq) == std::weak_ordering::less); + + assert(std::compare_weak_order_fallback(ps, nq) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(ps, ns) == std::weak_ordering::greater); + for (int i=0; i < 14; ++i) { + assert(std::compare_weak_order_fallback(ps, v[i]) == std::weak_ordering::greater); + } + assert(std::compare_weak_order_fallback(ps, ps) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(ps, pq) == std::weak_ordering::equivalent); + + assert(std::compare_weak_order_fallback(pq, nq) == std::weak_ordering::greater); + assert(std::compare_weak_order_fallback(pq, ns) == std::weak_ordering::greater); + for (int i=0; i < 14; ++i) { + assert(std::compare_weak_order_fallback(pq, v[i]) == std::weak_ordering::greater); + } + assert(std::compare_weak_order_fallback(pq, ps) == std::weak_ordering::equivalent); + assert(std::compare_weak_order_fallback(pq, pq) == std::weak_ordering::equivalent); + } + + return true; +} + +namespace N14 { + // Compare to N12::A. + struct A {}; + bool operator==(const A&, const A&); + constexpr std::weak_ordering operator<=>(A&, A&&) { return std::weak_ordering::less; } + constexpr std::weak_ordering operator<=>(A&&, A&&) { return std::weak_ordering::equivalent; } + std::weak_ordering operator<=>(const A&, const A&); + static_assert(std::three_way_comparable); + + struct B { + std::weak_ordering operator<=>(const B&) const; // lacks operator== + }; + static_assert(!std::three_way_comparable); + + struct C { + bool *touched; + bool operator==(const C&) const; + constexpr std::weak_ordering operator<=>(const C& rhs) const { + *rhs.touched = true; + return std::weak_ordering::equivalent; + } + }; + static_assert(std::three_way_comparable); +} + +constexpr bool test_1_4() +{ + // Otherwise, weak_ordering(compare_three_way()(E, F)) if it is a well-formed expression. + + // Test neither weak_order nor compare_three_way const-qualify the forwarded arguments. + N14::A a; + assert(std::compare_weak_order_fallback(a, std::move(a)) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(std::move(a), std::move(a)) == std::weak_ordering::equivalent); + + N14::B b; + static_assert(!has_weak_order(b, b)); + + // Test that the arguments are passed to <=> in the correct order. + bool c1_touched = false; + bool c2_touched = false; + N14::C c1 = {&c1_touched}; + N14::C c2 = {&c2_touched}; + assert(std::compare_weak_order_fallback(c1, c2) == std::weak_ordering::equivalent); + assert(!c1_touched); + assert(c2_touched); + + return true; +} + +namespace N15 { + struct A {}; + constexpr std::strong_ordering strong_order(A&, A&&) { return std::strong_ordering::less; } + constexpr std::strong_ordering strong_order(A&&, A&&) { return std::strong_ordering::equal; } + std::strong_ordering strong_order(const A&, const A&); + + struct B { + friend std::weak_ordering strong_order(B&, B&); + }; + + struct WeakOrder { + operator std::weak_ordering() const { return std::weak_ordering::less; } + }; + struct C { + friend WeakOrder strong_order(C& lhs, C&); + }; + + struct StrongOrder { + constexpr explicit operator std::strong_ordering() const { return std::strong_ordering::less; } + operator std::weak_ordering() const = delete; + }; + struct D { + bool touched = false; + friend constexpr StrongOrder strong_order(D& lhs, D&) { lhs.touched = true; return StrongOrder(); } + }; +} + +constexpr bool test_1_5() +{ + // Otherwise, weak_ordering(strong_order(E, F)) [that is, std::strong_order] + // if it is a well-formed expression. + + // Test that weak_order and strong_order do not const-qualify the forwarded arguments. + N15::A a; + assert(std::compare_weak_order_fallback(a, std::move(a)) == std::weak_ordering::less); + assert(std::compare_weak_order_fallback(std::move(a), std::move(a)) == std::weak_ordering::equivalent); + + // The type of ADL strong_order(e,f) must be explicitly convertible to strong_ordering + // (not just to weak_ordering), or else std::strong_order(e,f) won't exist. + N15::B b; + static_assert(!has_weak_order(b, b)); + + // The type of ADL strong_order(e,f) must be explicitly convertible to strong_ordering + // (not just to weak_ordering), or else std::strong_order(e,f) won't exist. + N15::C c; + static_assert(!has_weak_order(c, c)); + + N15::D d1, d2; + ASSERT_SAME_TYPE(decltype(std::compare_weak_order_fallback(d1, d2)), std::weak_ordering); + assert(std::compare_weak_order_fallback(d1, d2) == std::weak_ordering::less); + assert(d1.touched); + assert(!d2.touched); + + return true; +} + +namespace N2 { + struct Stats { + int eq = 0; + int lt = 0; + }; + struct A { + Stats *stats_; + double value_; + constexpr explicit A(Stats *stats, double value) : stats_(stats), value_(value) {} + friend constexpr bool operator==(A a, A b) { a.stats_->eq += 1; return a.value_ == b.value_; } + friend constexpr bool operator<(A a, A b) { a.stats_->lt += 1; return a.value_ < b.value_; } + }; + struct NoEquality { + friend bool operator<(NoEquality, NoEquality); + }; + struct VC1 { + // Deliberately asymmetric `const` qualifiers here. + friend bool operator==(const VC1&, VC1&); + friend bool operator<(const VC1&, VC1&); + }; + struct VC2 { + // Deliberately asymmetric `const` qualifiers here. + friend bool operator==(const VC2&, VC2&); + friend bool operator==(VC2&, const VC2&) = delete; + friend bool operator<(const VC2&, VC2&); + friend bool operator<(VC2&, const VC2&); + }; +} + +constexpr bool test_2() +{ + { + N2::Stats stats; + assert(std::compare_weak_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 1)) == std::weak_ordering::equivalent); + assert(stats.eq == 1 && stats.lt == 0); + stats = {}; + assert(std::compare_weak_order_fallback(N2::A(&stats, 1), N2::A(nullptr, 2)) == std::weak_ordering::less); + assert(stats.eq == 1 && stats.lt == 1); + stats = {}; + assert(std::compare_weak_order_fallback(N2::A(&stats, 2), N2::A(nullptr, 1)) == std::weak_ordering::greater); + assert(stats.eq == 1 && stats.lt == 1); + } + { + N2::NoEquality ne; + assert(!has_weak_order(ne, ne)); + } + { + // LWG3465: (cvc < vc) is well-formed, (vc < cvc) is not. That's fine, for weak ordering. + N2::VC1 vc; + const N2::VC1 cvc; + assert( has_weak_order(cvc, vc)); + assert(!has_weak_order(vc, cvc)); + } + { + // LWG3465: (cvc == vc) is well-formed, (vc == cvc) is not. That's fine. + N2::VC2 vc; + const N2::VC2 cvc; + assert( has_weak_order(cvc, vc)); + assert(!has_weak_order(vc, cvc)); + } + return true; +} + +int main(int, char**) +{ + test_1_1(); + test_1_2(); + test_1_3(); + test_1_3(); + test_1_3(); + test_1_4(); + test_1_5(); + test_2(); + + static_assert(test_1_3()); + static_assert(test_1_3()); + static_assert(test_1_3()); + static_assert(test_1_4()); + static_assert(test_1_5()); + static_assert(test_2()); + + return 0; +}