[libcxx-commits] [libcxx] [libc++][spaceship] P2404R3: Move-only types for equality_comparable_with, totally_ordered_with, and three_way_comparable_with (PR #99420)

Janet Cobb via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 17 20:28:20 PDT 2024


https://github.com/randomnetcat updated https://github.com/llvm/llvm-project/pull/99420

>From 363738b998edbb39e6836a320d9ebc9c0c3cb28d Mon Sep 17 00:00:00 2001
From: Janet Cobb <git at randomcat.org>
Date: Wed, 17 Jul 2024 21:53:02 -0400
Subject: [PATCH 1/2] P2404R3: Move-only types for relational concepts

---
 libcxx/docs/ReleaseNotes/19.rst               |  1 +
 libcxx/docs/Status/Cxx23Papers.csv            |  2 +-
 libcxx/docs/Status/SpaceshipPapers.csv        |  2 +-
 libcxx/include/CMakeLists.txt                 |  1 +
 .../include/__compare/three_way_comparable.h  | 17 +++++++
 .../__concepts/comparison_common_type.h       | 39 ++++++++++++++++
 .../include/__concepts/equality_comparable.h  | 19 ++++++++
 libcxx/include/module.modulemap               | 45 ++++++++++---------
 .../equality_comparable_with.compile.pass.cpp | 10 +++++
 ...three_way_comparable_with.compile.pass.cpp | 28 ++++++++++++
 libcxx/test/support/compare_types.h           | 23 ++++++++++
 11 files changed, 163 insertions(+), 24 deletions(-)
 create mode 100644 libcxx/include/__concepts/comparison_common_type.h

diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index e6d8acb74aeb2..1699c97652488 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -55,6 +55,7 @@ Implemented Papers
 - P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
 - P0019R8 - ``std::atomic_ref``
 - P2389R2 - Alias template ``dims`` for the ``extents`` of ``mdspan``
+- P2404R3 - Move-only types for ``equality_comparable_with``, ``totally_ordered_with``, and ``three_way_comparable_with``
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 4f589cd938d7c..bc13e4d9ebfd3 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -67,7 +67,7 @@
 "`P2302R4 <https://wg21.link/P2302R4>`__","LWG","``std::ranges::contains``","July 2022","|Complete|","19.0","|ranges|"
 "`P2322R6 <https://wg21.link/P2322R6>`__","LWG","``ranges::fold``","July 2022","","","|ranges|"
 "`P2374R4 <https://wg21.link/P2374R4>`__","LWG","``views::cartesian_product``","July 2022","","","|ranges|"
-"`P2404R3 <https://wg21.link/P2404R3>`__","LWG","Move-only types for ``equality_comparable_with``, ``totally_ordered_with``, and ``three_way_comparable_with``","July 2022","",""
+"`P2404R3 <https://wg21.link/P2404R3>`__","LWG","Move-only types for ``equality_comparable_with``, ``totally_ordered_with``, and ``three_way_comparable_with``","July 2022","|Complete|","19.0"
 "`P2408R5 <https://wg21.link/P2408R5>`__","LWG","Ranges iterators as inputs to non-Ranges algorithms","July 2022","","","|ranges|"
 "`P2417R2 <https://wg21.link/P2417R2>`__","LWG","A more ``constexpr`` ``bitset``","July 2022","|Complete|","16.0"
 "`P2419R2 <https://wg21.link/P2419R2>`__","LWG","Clarify handling of encodings in localized formatting of chrono types","July 2022","",""
diff --git a/libcxx/docs/Status/SpaceshipPapers.csv b/libcxx/docs/Status/SpaceshipPapers.csv
index 39e1f968c1754..5fbf6a53b6db7 100644
--- a/libcxx/docs/Status/SpaceshipPapers.csv
+++ b/libcxx/docs/Status/SpaceshipPapers.csv
@@ -1,6 +1,6 @@
 "Number","Name","Status","First released version"
 `P1614R2 <https://wg21.link/P1614R2>`_,The Mothership has Landed,|In Progress|,
-`P2404R3 <https://wg21.link/P2404R3>`_,"Relaxing ``equality_comparable_with``'s, ``totally_ordered_with``'s, and ``three_way_comparable_with``'s common reference requirements to support move-only types",,
+`P2404R3 <https://wg21.link/P2404R3>`_,"Relaxing ``equality_comparable_with``'s, ``totally_ordered_with``'s, and ``three_way_comparable_with``'s common reference requirements to support move-only types","|Complete|","19.0"
 `LWG3330 <https://wg21.link/LWG3330>`_,Include ``<compare>`` from most library headers,"|Complete|","13.0"
 `LWG3347 <https://wg21.link/LWG3347>`_,"``std::pair<T, U>`` now requires ``T`` and ``U`` to be *less-than-comparable*",|Nothing To Do|,
 `LWG3350 <https://wg21.link/LWG3350>`_,Simplify return type of ``lexicographical_compare_three_way``,|Nothing To Do|,
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cd64fe91449c2..e618847b1e27c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -296,6 +296,7 @@ set(files
   __concepts/class_or_enum.h
   __concepts/common_reference_with.h
   __concepts/common_with.h
+  __concepts/comparison_common_type.h
   __concepts/constructible.h
   __concepts/convertible_to.h
   __concepts/copyable.h
diff --git a/libcxx/include/__compare/three_way_comparable.h b/libcxx/include/__compare/three_way_comparable.h
index 7a44ea9158a6f..3b14725be3309 100644
--- a/libcxx/include/__compare/three_way_comparable.h
+++ b/libcxx/include/__compare/three_way_comparable.h
@@ -12,6 +12,7 @@
 #include <__compare/common_comparison_category.h>
 #include <__compare/ordering.h>
 #include <__concepts/common_reference_with.h>
+#include <__concepts/comparison_common_type.h>
 #include <__concepts/equality_comparable.h>
 #include <__concepts/same_as.h>
 #include <__concepts/totally_ordered.h>
@@ -37,6 +38,20 @@ concept three_way_comparable =
       { __a <=> __b } -> __compares_as<_Cat>;
     };
 
+#  if _LIBCPP_STD_VER >= 23
+
+template <class _Tp, class _Up, class _cat = partial_ordering>
+concept three_way_comparable_with =
+    three_way_comparable<_Tp, _cat> && three_way_comparable<_Up, _cat> && __comparison_common_type_with<_Tp, _Up> &&
+    three_way_comparable<common_reference_t<__make_const_lvalue_ref<_Tp>, __make_const_lvalue_ref<_Up>>, _cat> &&
+    __weakly_equality_comparable_with<_Tp, _Up> && __partially_ordered_with<_Tp, _Up> &&
+    requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) {
+      { __t <=> __u } -> __compares_as<_cat>;
+      { __u <=> __t } -> __compares_as<_cat>;
+    };
+
+#  else
+
 template <class _Tp, class _Up, class _Cat = partial_ordering>
 concept three_way_comparable_with =
     three_way_comparable<_Tp, _Cat> && three_way_comparable<_Up, _Cat> &&
@@ -48,6 +63,8 @@ concept three_way_comparable_with =
       { __u <=> __t } -> __compares_as<_Cat>;
     };
 
+#  endif // _LIBCPP_STD_VER >= 23
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__concepts/comparison_common_type.h b/libcxx/include/__concepts/comparison_common_type.h
new file mode 100644
index 0000000000000..a1f246d2a6dca
--- /dev/null
+++ b/libcxx/include/__concepts/comparison_common_type.h
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___CONCEPTS_COMPARISON_COMMON_TYPE_H
+#define _LIBCPP___CONCEPTS_COMPARISON_COMMON_TYPE_H
+
+#include "__concepts/convertible_to.h"
+#include "__concepts/same_as.h"
+#include "__config"
+#include "__type_traits/common_reference.h"
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <class _Tp, class _Up, class _C = common_reference_t<const _Tp&, const _Up&>>
+concept __comparison_common_type_with_impl =
+    same_as<common_reference_t<const _Tp&, const _Up&>, common_reference_t<const _Up&, const _Tp&>> && requires {
+      requires convertible_to<const _Tp&, const _C&> || convertible_to<_Tp, const _C&>;
+      requires convertible_to<const _Up&, const _C&> || convertible_to<_Up, const _C&>;
+    };
+
+template <class _Tp, class _Up>
+concept __comparison_common_type_with = __comparison_common_type_with_impl<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___CONCEPTS_COMPARISON_COMMON_TYPE_H
diff --git a/libcxx/include/__concepts/equality_comparable.h b/libcxx/include/__concepts/equality_comparable.h
index 278fc76409289..1904b2e451a8d 100644
--- a/libcxx/include/__concepts/equality_comparable.h
+++ b/libcxx/include/__concepts/equality_comparable.h
@@ -11,6 +11,7 @@
 
 #include <__concepts/boolean_testable.h>
 #include <__concepts/common_reference_with.h>
+#include <__concepts/comparison_common_type.h>
 #include <__config>
 #include <__type_traits/common_reference.h>
 #include <__type_traits/make_const_lvalue_ref.h>
@@ -37,6 +38,22 @@ concept __weakly_equality_comparable_with =
 template <class _Tp>
 concept equality_comparable = __weakly_equality_comparable_with<_Tp, _Tp>;
 
+#  if _LIBCPP_STD_VER >= 23
+
+// clang-format off
+template <class _Tp, class _Up>
+concept equality_comparable_with =
+    equality_comparable<_Tp> && equality_comparable<_Up> &&
+    __comparison_common_type_with<_Tp, _Up> &&
+    equality_comparable<
+        common_reference_t<
+            __make_const_lvalue_ref<_Tp>,
+            __make_const_lvalue_ref<_Up>>> &&
+    __weakly_equality_comparable_with<_Tp, _Up>;
+// clang-format on
+
+#  else
+
 // clang-format off
 template <class _Tp, class _Up>
 concept equality_comparable_with =
@@ -49,6 +66,8 @@ concept equality_comparable_with =
     __weakly_equality_comparable_with<_Tp, _Up>;
 // clang-format on
 
+#  endif // _LIBCPP_STD_VER >= 23
+
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index f4aaa14c1c2ee..eabb32daf2c96 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1173,43 +1173,44 @@ module std_private_compare_weak_order                     [system] { header "__c
 
 module std_private_complex_complex_fwd            [system] { header "__fwd/complex.h" }
 
-module std_private_concepts_arithmetic            [system] { header "__concepts/arithmetic.h" }
-module std_private_concepts_assignable            [system] { header "__concepts/assignable.h" }
-module std_private_concepts_boolean_testable      [system] { header "__concepts/boolean_testable.h" }
-module std_private_concepts_class_or_enum         [system] { header "__concepts/class_or_enum.h" }
-module std_private_concepts_common_reference_with [system] { header "__concepts/common_reference_with.h" }
-module std_private_concepts_common_with           [system] { header "__concepts/common_with.h" }
-module std_private_concepts_constructible         [system] {
+module std_private_concepts_arithmetic              [system] { header "__concepts/arithmetic.h" }
+module std_private_concepts_assignable              [system] { header "__concepts/assignable.h" }
+module std_private_concepts_boolean_testable        [system] { header "__concepts/boolean_testable.h" }
+module std_private_concepts_class_or_enum           [system] { header "__concepts/class_or_enum.h" }
+module std_private_concepts_common_reference_with   [system] { header "__concepts/common_reference_with.h" }
+module std_private_concepts_common_with             [system] { header "__concepts/common_with.h" }
+module std_private_cooncepts_comparison_common_type [system] { header "__concepts/comparison_common_type.h" }
+module std_private_concepts_constructible           [system] {
   header "__concepts/constructible.h"
   export std_private_concepts_destructible
 }
-module std_private_concepts_convertible_to        [system] { header "__concepts/convertible_to.h" }
-module std_private_concepts_copyable              [system] { header "__concepts/copyable.h" }
-module std_private_concepts_derived_from          [system] { header "__concepts/derived_from.h" }
-module std_private_concepts_destructible          [system] {
+module std_private_concepts_convertible_to          [system] { header "__concepts/convertible_to.h" }
+module std_private_concepts_copyable                [system] { header "__concepts/copyable.h" }
+module std_private_concepts_derived_from            [system] { header "__concepts/derived_from.h" }
+module std_private_concepts_destructible            [system] {
   header "__concepts/destructible.h"
   export std_private_type_traits_is_nothrow_destructible
 }
-module std_private_concepts_different_from        [system] { header "__concepts/different_from.h" }
-module std_private_concepts_equality_comparable   [system] {
+module std_private_concepts_different_from          [system] { header "__concepts/different_from.h" }
+module std_private_concepts_equality_comparable     [system] {
   header "__concepts/equality_comparable.h"
   export std_private_type_traits_common_reference
 }
-module std_private_concepts_invocable             [system] { header "__concepts/invocable.h" }
-module std_private_concepts_movable               [system] {
+module std_private_concepts_invocable               [system] { header "__concepts/invocable.h" }
+module std_private_concepts_movable                 [system] {
   header "__concepts/movable.h"
   export std_private_type_traits_is_object
 }
-module std_private_concepts_predicate             [system] { header "__concepts/predicate.h" }
-module std_private_concepts_regular               [system] { header "__concepts/regular.h" }
-module std_private_concepts_relation              [system] { header "__concepts/relation.h" }
-module std_private_concepts_same_as               [system] {
+module std_private_concepts_predicate               [system] { header "__concepts/predicate.h" }
+module std_private_concepts_regular                 [system] { header "__concepts/regular.h" }
+module std_private_concepts_relation                [system] { header "__concepts/relation.h" }
+module std_private_concepts_same_as                 [system] {
   header "__concepts/same_as.h"
   export std_private_type_traits_is_same
 }
-module std_private_concepts_semiregular           [system] { header "__concepts/semiregular.h" }
-module std_private_concepts_swappable             [system] { header "__concepts/swappable.h" }
-module std_private_concepts_totally_ordered       [system] { header "__concepts/totally_ordered.h" }
+module std_private_concepts_semiregular             [system] { header "__concepts/semiregular.h" }
+module std_private_concepts_swappable               [system] { header "__concepts/swappable.h" }
+module std_private_concepts_totally_ordered         [system] { header "__concepts/totally_ordered.h" }
 
 module std_private_condition_variable_condition_variable [system] {
   header "__condition_variable/condition_variable.h"
diff --git a/libcxx/test/std/concepts/concepts.compare/concept.equalitycomparable/equality_comparable_with.compile.pass.cpp b/libcxx/test/std/concepts/concepts.compare/concept.equalitycomparable/equality_comparable_with.compile.pass.cpp
index e0edd1f332f81..45e3e88a10d14 100644
--- a/libcxx/test/std/concepts/concepts.compare/concept.equalitycomparable/equality_comparable_with.compile.pass.cpp
+++ b/libcxx/test/std/concepts/concepts.compare/concept.equalitycomparable/equality_comparable_with.compile.pass.cpp
@@ -1119,4 +1119,14 @@ static_assert(
     std::common_reference_with<one_way_ne const&, explicit_operators const&>);
 static_assert(
     !check_equality_comparable_with<one_way_ne, explicit_operators>());
+
+#if TEST_STD_VER >= 23
+static_assert(check_equality_comparable_with<move_only_equality_with_int, int>());
+static_assert(check_equality_comparable_with<std::unique_ptr<int>, std::nullptr_t>());
+#else
+static_assert(!check_equality_comparable_with<move_only_equality_with_int, int>());
+static_assert(!check_equality_comparable_with<std::unique_ptr<int>, std::nullptr_t>());
+#endif // TEST_STD_VER >= 23
+
+static_assert(!check_equality_comparable_with<immobile_comparable_with_int, int>());
 } // namespace types_fit_for_purpose
diff --git a/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp b/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp
index 50d9722aa29c0..2920240dddf55 100644
--- a/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp
+++ b/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp
@@ -14,6 +14,7 @@
 #include <compare>
 
 #include "compare_types.h"
+#include "test_macros.h"
 
 template <class T, class U = T, typename Cat = std::partial_ordering>
 constexpr bool check_three_way_comparable_with() {
@@ -223,4 +224,31 @@ struct SpaceshipNonConstArgument {
 };
 
 static_assert(!check_three_way_comparable_with<SpaceshipNonConstArgument>());
+
+struct MoveOnlyIntComparable {
+  MoveOnlyIntComparable(int) {}
+
+  MoveOnlyIntComparable(MoveOnlyIntComparable&&)            = default;
+  MoveOnlyIntComparable& operator=(MoveOnlyIntComparable&&) = default;
+
+  friend auto operator<=>(MoveOnlyIntComparable const&, MoveOnlyIntComparable const&) = default;
+};
+
+#if TEST_STD_VER >= 23
+static_assert(check_three_way_comparable_with<MoveOnlyIntComparable, int>());
+#else
+static_assert(!check_three_way_comparable_with<MoveOnlyIntComparable, int>());
+#endif // TEST_STD_VER >= 23
+
+struct ImmobileIntComparable {
+  ImmobileIntComparable(int) {}
+
+  ImmobileIntComparable(ImmobileIntComparable&&)            = delete;
+  ImmobileIntComparable& operator=(ImmobileIntComparable&&) = delete;
+
+  friend auto operator<=>(ImmobileIntComparable const&, ImmobileIntComparable const&) = default;
+};
+
+static_assert(!check_three_way_comparable_with<ImmobileIntComparable, int>());
+
 } // namespace user_defined
diff --git a/libcxx/test/support/compare_types.h b/libcxx/test/support/compare_types.h
index 8155f6221540c..eea219d033ae9 100644
--- a/libcxx/test/support/compare_types.h
+++ b/libcxx/test/support/compare_types.h
@@ -529,4 +529,27 @@ struct ForwardingTestObject {
   constexpr bool operator>=(const ForwardingTestObject&) const& { return false; }
 };
 
+struct move_only_equality_with_int {
+  move_only_equality_with_int(int);
+
+  move_only_equality_with_int(move_only_equality_with_int&&)            = default;
+  move_only_equality_with_int& operator=(move_only_equality_with_int&&) = default;
+
+  move_only_equality_with_int(move_only_equality_with_int const&)            = delete;
+  move_only_equality_with_int& operator=(move_only_equality_with_int const&) = delete;
+
+  friend bool operator==(move_only_equality_with_int const&, move_only_equality_with_int const&) = default;
+  friend bool operator==(move_only_equality_with_int const&, int);
+};
+
+struct immobile_comparable_with_int {
+  immobile_comparable_with_int(int);
+
+  immobile_comparable_with_int(immobile_comparable_with_int&&)            = delete;
+  immobile_comparable_with_int& operator=(immobile_comparable_with_int&&) = delete;
+
+  friend bool operator==(immobile_comparable_with_int const&, immobile_comparable_with_int const&) = default;
+  friend bool operator==(immobile_comparable_with_int const&, int);
+};
+
 #endif // TEST_SUPPORT_COMPARE_TYPES_H

>From 400144dbc59f68edb96caacac12e8a6b4bac3352 Mon Sep 17 00:00:00 2001
From: Janet Cobb <git at randomcat.org>
Date: Wed, 17 Jul 2024 23:28:10 -0400
Subject: [PATCH 2/2] Fix _Cat capitalization

---
 libcxx/include/__compare/three_way_comparable.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__compare/three_way_comparable.h b/libcxx/include/__compare/three_way_comparable.h
index 3b14725be3309..f43c6fb010332 100644
--- a/libcxx/include/__compare/three_way_comparable.h
+++ b/libcxx/include/__compare/three_way_comparable.h
@@ -40,14 +40,14 @@ concept three_way_comparable =
 
 #  if _LIBCPP_STD_VER >= 23
 
-template <class _Tp, class _Up, class _cat = partial_ordering>
+template <class _Tp, class _Up, class _Cat = partial_ordering>
 concept three_way_comparable_with =
-    three_way_comparable<_Tp, _cat> && three_way_comparable<_Up, _cat> && __comparison_common_type_with<_Tp, _Up> &&
-    three_way_comparable<common_reference_t<__make_const_lvalue_ref<_Tp>, __make_const_lvalue_ref<_Up>>, _cat> &&
+    three_way_comparable<_Tp, _Cat> && three_way_comparable<_Up, _Cat> && __comparison_common_type_with<_Tp, _Up> &&
+    three_way_comparable<common_reference_t<__make_const_lvalue_ref<_Tp>, __make_const_lvalue_ref<_Up>>, _Cat> &&
     __weakly_equality_comparable_with<_Tp, _Up> && __partially_ordered_with<_Tp, _Up> &&
     requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) {
-      { __t <=> __u } -> __compares_as<_cat>;
-      { __u <=> __t } -> __compares_as<_cat>;
+      { __t <=> __u } -> __compares_as<_Cat>;
+      { __u <=> __t } -> __compares_as<_Cat>;
     };
 
 #  else



More information about the libcxx-commits mailing list