[libcxx-commits] [libcxx] bf20a09 - [libc++] [P1614] Implement the second half of [cmp.alg]: compare_{strong, weak, partial}_fallback.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jan 27 15:10:34 PST 2022


Author: Arthur O'Dwyer
Date: 2022-01-27T17:48:01-05:00
New Revision: bf20a09790cb2e5194f3f008ec1f2edbd4e2f709

URL: https://github.com/llvm/llvm-project/commit/bf20a09790cb2e5194f3f008ec1f2edbd4e2f709
DIFF: https://github.com/llvm/llvm-project/commit/bf20a09790cb2e5194f3f008ec1f2edbd4e2f709.diff

LOG: [libc++] [P1614] Implement the second half of [cmp.alg]: compare_{strong,weak,partial}_fallback.

The tests for these are just copy-pasted from the tests for std::{strong,weak,partial}_order,
and then I added an extra clause in each (test_2()) to test the stuff that's not just the same
as std::*_order.

This also includes the fix for https://wg21.link/LWG3465 (which falls naturally out of the
"you must write it three times" style, but I've added test cases for it also).

There is an action item here to go back and give good diagnostics for SFINAE failures
in these CPOs. I've filed this as https://github.com/llvm/llvm-project/issues/53456 .

Differential Revision: https://reviews.llvm.org/D111514

Added: 
    libcxx/include/__compare/compare_partial_order_fallback.h
    libcxx/include/__compare/compare_strong_order_fallback.h
    libcxx/include/__compare/compare_weak_order_fallback.h
    libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_partial_order_fallback.module.verify.cpp
    libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_strong_order_fallback.module.verify.cpp
    libcxx/test/libcxx/diagnostics/detail.headers/compare/compare_weak_order_fallback.module.verify.cpp
    libcxx/test/std/language.support/cmp/cmp.alg/compare_partial_order_fallback.pass.cpp
    libcxx/test/std/language.support/cmp/cmp.alg/compare_strong_order_fallback.pass.cpp
    libcxx/test/std/language.support/cmp/cmp.alg/compare_weak_order_fallback.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx2bIssues.csv
    libcxx/docs/Status/Spaceship.rst
    libcxx/docs/Status/SpaceshipProjects.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/compare
    libcxx/include/module.modulemap
    libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 3dcb641a866bc..83d393706ec82 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -39,7 +39,7 @@
 "`3455 <https://wg21.link/LWG3455>`__","Incorrect Postconditions on ``unique_ptr`` move assignment","November 2020","|Nothing To Do|",""
 "`3460 <https://wg21.link/LWG3460>`__","Unimplementable ``noop_coroutine_handle`` guarantees","November 2020","|Complete|","14.0"
 "`3461 <https://wg21.link/LWG3461>`__","``convertible_to``'s description mishandles cv-qualified ``void``","November 2020","",""
-"`3465 <https://wg21.link/LWG3465>`__","``compare_partial_order_fallback`` requires ``F < E``","November 2020","","","|spaceship|"
+"`3465 <https://wg21.link/LWG3465>`__","``compare_partial_order_fallback`` requires ``F < E``","November 2020","|Complete|","14.0","|spaceship|"
 "`3466 <https://wg21.link/LWG3466>`__","Specify the requirements for ``promise``/``future``/``shared_future`` consistently","November 2020","",""
 "`3467 <https://wg21.link/LWG3467>`__","``bool`` can't be an integer-like type","November 2020","",""
 "`3472 <https://wg21.link/LWG3472>`__","``counted_iterator`` is missing preconditions","November 2020","","","|ranges|"

diff  --git a/libcxx/docs/Status/Spaceship.rst b/libcxx/docs/Status/Spaceship.rst
index 41116edd73b7b..d596c1128dbf6 100644
--- a/libcxx/docs/Status/Spaceship.rst
+++ b/libcxx/docs/Status/Spaceship.rst
@@ -32,6 +32,10 @@ Sub-Projects in the Implementation Effort
    :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
index 65dde3123a53c..25bb7d0779015 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -10,7 +10,7 @@ Section,Description,Dependencies,Assignee,Complete
 | `partial_order <https://reviews.llvm.org/D110738>`_
 | `strong_order_fallback <https://reviews.llvm.org/D111514>`_
 | `weak_order_fallback <https://reviews.llvm.org/D111514>`_
-| `partial_order_fallback <https://reviews.llvm.org/D111514>`_",None,Arthur O'Dwyer,|In Progress|
+| `partial_order_fallback <https://reviews.llvm.org/D111514>`_",None,Arthur O'Dwyer,|Complete| [#note-strongorder]_
 | `[alg.three.way] <https://wg21.link/alg.three.way>`_,| `lexicographical_compare_three_way <https://reviews.llvm.org/D80902>`_,[comparisons.three.way],Christopher Di Bella,|In Progress|
 | `[coroutine.handle.compare] <https://wg21.link/coroutine.handle.compare>`_,| `coroutine_handle <https://reviews.llvm.org/D109433>`_,[comparisons.three.way],Chuanqi Xu,|Complete|
 | `[pairs.spec] <https://wg21.link/pairs.spec>`_,| `pair <https://reviews.llvm.org/D107721>`_,[expos.only.func],Kent Ross,|Complete|

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 278e848e6ecbf..8fbf853867193 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -113,8 +113,11 @@ set(files
   __chrono/system_clock.h
   __chrono/time_point.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
index 0000000000000..895523b38fb34
--- /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/ordering.h>
+#include <__compare/partial_order.h>
+#include <__config>
+#include <__utility/forward.h>
+#include <__utility/priority_tag.h>
+#include <type_traits>
+
+#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<class _Tp, class _Up>
+            requires is_same_v<decay_t<_Tp>, 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<class _Tp, class _Up>
+            requires is_same_v<decay_t<_Tp>, 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<class _Tp, class _Up>
+        _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
index 0000000000000..5fee7b4780684
--- /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/ordering.h>
+#include <__compare/strong_order.h>
+#include <__config>
+#include <__utility/forward.h>
+#include <__utility/priority_tag.h>
+#include <type_traits>
+
+#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<class _Tp, class _Up>
+            requires is_same_v<decay_t<_Tp>, 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<class _Tp, class _Up>
+            requires is_same_v<decay_t<_Tp>, 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<class _Tp, class _Up>
+        _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
index 0000000000000..0abd4f2dfbee4
--- /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/ordering.h>
+#include <__compare/weak_order.h>
+#include <__config>
+#include <__utility/forward.h>
+#include <__utility/priority_tag.h>
+#include <type_traits>
+
+#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<class _Tp, class _Up>
+            requires is_same_v<decay_t<_Tp>, 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<class _Tp, class _Up>
+            requires is_same_v<decay_t<_Tp>, 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<class _Tp, class _Up>
+        _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
index d686b5a369f2b..287e61690bbbe 100644
--- a/libcxx/include/compare
+++ b/libcxx/include/compare
@@ -51,9 +51,14 @@ namespace std {
   struct compare_three_way; // C++20
 
   // [cmp.alg], comparison algorithms
-  template<class T> constexpr strong_ordering strong_order(const T& a, const T& b);
-  template<class T> constexpr weak_ordering weak_order(const T& a, const T& b);
-  template<class T> 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 @@ namespace std {
 */
 
 #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
index 90fae9bb8362d..fc05f6c66641b 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -385,16 +385,19 @@ module std [system] {
     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
index 0000000000000..2fce55e31b183
--- /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
index 0000000000000..6d8d1b925285d
--- /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
index 0000000000000..1a2db89d45eac
--- /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
index 0000000000000..54cbe2bf7c70f
--- /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
+
+// <compare>
+
+// template<class T> constexpr partial_ordering compare_partial_order_fallback(const T& a, const T& b);
+
+#include <compare>
+
+#include <cassert>
+#include <cmath>
+#include <iterator> // std::size
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+template<class T, class U>
+constexpr auto has_partial_order(T&& t, U&& u)
+    -> decltype(std::compare_partial_order_fallback(static_cast<T&&>(t), static_cast<U&&>(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 
diff er, 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<A>);
+
+    struct B {
+        std::partial_ordering operator<=>(const B&) const;  // lacks operator==
+    };
+    static_assert(!std::three_way_comparable<B>);
+
+    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<C>);
+}
+
+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<F>::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<F>::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<F>::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<double>::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
index 0000000000000..440e0288626a1
--- /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
+
+// <compare>
+
+// template<class T> constexpr strong_ordering compare_strong_order_fallback(const T& a, const T& b);
+
+#include <compare>
+
+#include <cassert>
+#include <cmath>
+#include <iterator> // std::size
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+#if defined(__i386__)
+ #define TEST_BUGGY_SIGNALING_NAN
+#endif
+
+template<class T, class U>
+constexpr auto has_strong_order(T&& t, U&& u)
+    -> decltype(std::compare_strong_order_fallback(static_cast<T&&>(t), static_cast<U&&>(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 
diff er, 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<class F>
+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<T>::is_iec559 is true, is additionally consistent with
+    // the totalOrder operation as specified in ISO/IEC/IEEE 60559.
+
+    static_assert(std::numeric_limits<F>::is_iec559);
+
+    ASSERT_SAME_TYPE(decltype(std::compare_strong_order_fallback(F(0), F(0))), std::strong_ordering);
+
+    F v[] = {
+        -std::numeric_limits<F>::infinity(),
+        std::numeric_limits<F>::lowest(),  // largest (finite) negative number
+        F(-1.0), F(-0.1),
+        -std::numeric_limits<F>::min(),    // smallest (normal) negative number
+        F(-0.0),                           // negative zero
+        F(0.0),
+        std::numeric_limits<F>::min(),     // smallest (normal) positive number
+        F(0.1), F(1.0), F(2.0), F(3.14),
+        std::numeric_limits<F>::max(),     // largest (finite) positive number
+        std::numeric_limits<F>::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<F>::quiet_NaN(), F(-1));
+        F ns = _VSTD::copysign(std::numeric_limits<F>::signaling_NaN(), F(-1));
+        F ps = _VSTD::copysign(std::numeric_limits<F>::signaling_NaN(), F(+1));
+        F pq = _VSTD::copysign(std::numeric_limits<F>::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<A>);
+
+    struct B {
+        std::strong_ordering operator<=>(const B&) const;  // lacks operator==
+    };
+    static_assert(!std::three_way_comparable<B>);
+
+    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<C>);
+}
+
+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<float>();
+    test_1_3<double>();
+    // test_1_3<long double>();  // UNIMPLEMENTED
+    test_1_4();
+    test_2();
+
+    static_assert(test_1_3<float>());
+    static_assert(test_1_3<double>());
+    // static_assert(test_1_3<long double>());  // 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
index 0000000000000..1b5470c40a35e
--- /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
+
+// <compare>
+
+// template<class T> constexpr weak_ordering compare_weak_order_fallback(const T& a, const T& b);
+
+#include <compare>
+
+#include <cassert>
+#include <cmath>
+#include <iterator> // std::size
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+
+template<class T, class U>
+constexpr auto has_weak_order(T&& t, U&& u)
+    -> decltype(std::compare_weak_order_fallback(static_cast<T&&>(t), static_cast<U&&>(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 
diff er, 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<class F>
+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<T>::is_iec559 is true, is additionally consistent with
+    // the following equivalence classes...
+
+    // std::numeric_limits<F>::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<F>::infinity(),
+        std::numeric_limits<F>::lowest(),  // largest (finite) negative number
+        F(-1.0), F(-0.1),
+        -std::numeric_limits<F>::min(),    // smallest (normal) negative number
+        F(-0.0),                           // negative zero
+        F(0.0),
+        std::numeric_limits<F>::min(),     // smallest (normal) positive number
+        F(0.1), F(1.0), F(2.0), F(3.14),
+        std::numeric_limits<F>::max(),     // largest (finite) positive number
+        std::numeric_limits<F>::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<F>::quiet_NaN(), F(-1));
+        F ns = _VSTD::copysign(std::numeric_limits<F>::signaling_NaN(), F(-1));
+        F ps = _VSTD::copysign(std::numeric_limits<F>::signaling_NaN(), F(+1));
+        F pq = _VSTD::copysign(std::numeric_limits<F>::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<A>);
+
+    struct B {
+        std::weak_ordering operator<=>(const B&) const;  // lacks operator==
+    };
+    static_assert(!std::three_way_comparable<B>);
+
+    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<C>);
+}
+
+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<float>();
+    test_1_3<double>();
+    test_1_3<long double>();
+    test_1_4();
+    test_1_5();
+    test_2();
+
+    static_assert(test_1_3<float>());
+    static_assert(test_1_3<double>());
+    static_assert(test_1_3<long double>());
+    static_assert(test_1_4());
+    static_assert(test_1_5());
+    static_assert(test_2());
+
+    return 0;
+}

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 819aeb454a45c..29aa31f8b936b 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -56,9 +56,9 @@ static_assert(test(std::ranges::iter_swap, a + 0, a + 1));
 static_assert(test(std::partial_order, 1, 2));
 static_assert(test(std::strong_order, 1, 2));
 static_assert(test(std::weak_order, 1, 2));
-//static_assert(test(std::compare_partial_order_fallback, 1, 2));
-//static_assert(test(std::compare_strong_order_fallback, 1, 2));
-//static_assert(test(std::compare_weak_order_fallback, 1, 2));
+static_assert(test(std::compare_partial_order_fallback, 1, 2));
+static_assert(test(std::compare_strong_order_fallback, 1, 2));
+static_assert(test(std::compare_weak_order_fallback, 1, 2));
 
 // [range.access]
 static_assert(test(std::ranges::begin, a));


        


More information about the libcxx-commits mailing list