[libcxx-commits] [libcxx] 03cda77 - [libc++][spaceship] Implement `operator<=>` for `optional`

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Thu May 4 23:59:29 PDT 2023


Author: Hristo Hristov
Date: 2023-05-05T09:59:22+03:00
New Revision: 03cda77409023e64d6338dcb4e36bf1e40b33ea3

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

LOG: [libc++][spaceship] Implement `operator<=>` for `optional`

Implements parts of **P1614R2**: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1614r2.html

- Implemented `operator<=>` for `optional`
- Updated "optional synopsis" to match the current draft https://eel.is/c++draft/optional closer
- Implemented https://cplusplus.github.io/LWG/issue3566
- Implemented https://cplusplus.github.io/LWG/issue3746

Reviewed By: #libc, philnik, ldionne

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

Added: 
    libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp
    libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp
    libcxx/test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx2bIssues.csv
    libcxx/docs/Status/SpaceshipProjects.csv
    libcxx/include/optional

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 8fa96fe2cb658..5ed62484f0407 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -119,7 +119,7 @@
 `3560 <https://wg21.link/LWG3560>`__,"``ranges::equal`` and ``ranges::is_permutation`` should short-circuit for ``sized_ranges``","October 2021","","","|ranges|"
 `3561 <https://wg21.link/LWG3561>`__,"Issue with internal counter in ``discard_block_engine``","October 2021","",""
 `3563 <https://wg21.link/LWG3563>`__,"``keys_view`` example is broken","October 2021","","","|ranges|"
-`3566 <https://wg21.link/LWG3566>`__,"Constraint recursion for ``operator<=>(optional<T>, U)``","October 2021","","","|spaceship|"
+`3566 <https://wg21.link/LWG3566>`__,"Constraint recursion for ``operator<=>(optional<T>, U)``","October 2021","Complete","17.0","|spaceship|"
 `3567 <https://wg21.link/LWG3567>`__,"Formatting move-only iterators take two","October 2021","|Complete|","16.0","|format| |ranges|"
 `3568 <https://wg21.link/LWG3568>`__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","|Complete|","16.0","|ranges|"
 `3570 <https://wg21.link/LWG3570>`__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","",""
@@ -206,7 +206,7 @@
 "`3738 <https://wg21.link/LWG3738>`__","Missing preconditions for ``take_view`` constructor", "November 2022","|Complete|","16.0","|ranges|"
 "`3743 <https://wg21.link/LWG3743>`__","``ranges::to``'s reserve may be ill-formed", "November 2022","","","|ranges|"
 "`3745 <https://wg21.link/LWG3745>`__","``std::atomic_wait`` and its friends lack ``noexcept``", "November 2022","|Complete|","16.0",""
-"`3746 <https://wg21.link/LWG3746>`__","``optional``'s spaceship with ``U`` with a type derived from optional causes infinite constraint meta-recursion", "November 2022","","","|spaceship|"
+"`3746 <https://wg21.link/LWG3746>`__","``optional``'s spaceship with ``U`` with a type derived from optional causes infinite constraint meta-recursion", "November 2022","Complete","17.0","|spaceship|"
 "`3747 <https://wg21.link/LWG3747>`__","``ranges::uninitialized_copy_n``, ``ranges::uninitialized_move_n``, and ``ranges::destroy_n`` should use ``std::move``", "November 2022","","","|ranges|"
 "`3750 <https://wg21.link/LWG3750>`__","Too many papers bump ``__cpp_lib_format``", "November 2022","|Partial| [#note-LWG3750]_","","|format|"
 "`3751 <https://wg21.link/LWG3751>`__","Missing feature macro for ``flat_set``", "November 2022","","","|flat_containers|"

diff  --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index e8624cb6a6724..3eecc354b8113 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -22,7 +22,7 @@ Section,Description,Dependencies,Assignee,Complete
 "| `[optional.relops] <https://wg21.link/optional.relops>`_
 | `[optional.nullops] <https://wg21.link/optional.nullops>`_
 | `[optional.comp.with.t] <https://wg21.link/optional.comp.with.t>`_","| `optional <https://reviews.llvm.org/D146392>`_
-| `nullopt <https://reviews.llvm.org/D146392>`_",None,Hristo Hristov,|In Progress|
+| `nullopt <https://reviews.llvm.org/D146392>`_",None,Hristo Hristov,|Complete|
 "| `[variant.relops] <https://wg21.link/variant.relops>`_
 | `[variant.monostate.relops] <https://wg21.link/variant.monostate.relops>`_","| `monostate <https://reviews.llvm.org/D131372>`_
 | `variant <https://reviews.llvm.org/D131372>`_",None,Kent Ross,|Complete|

diff  --git a/libcxx/include/optional b/libcxx/include/optional
index a387de473f30e..8895ed152dc82 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -16,107 +16,126 @@
 // C++1z
 
 namespace std {
-  // 23.6.3, optional for object types
-  template <class T> class optional;
+  // [optional.optional], class template optional
+  template <class T>
+    class optional;
 
-  // 23.6.4, no-value state indicator
+  template<class T>
+    concept is-derived-from-optional = requires(const T& t) {       // exposition only
+      []<class U>(const optional<U>&){ }(t);
+    };
+
+  // [optional.nullopt], no-value state indicator
   struct nullopt_t{see below };
   inline constexpr nullopt_t nullopt(unspecified );
 
-  // 23.6.5, class bad_optional_access
+  // [optional.bad.access], class bad_optional_access
   class bad_optional_access;
 
-  // 23.6.6, relational operators
+  // [optional.relops], relational operators
   template <class T, class U>
-  constexpr bool operator==(const optional<T>&, const optional<U>&);
+    constexpr bool operator==(const optional<T>&, const optional<U>&);
   template <class T, class U>
-  constexpr bool operator!=(const optional<T>&, const optional<U>&);
+    constexpr bool operator!=(const optional<T>&, const optional<U>&);
   template <class T, class U>
-  constexpr bool operator<(const optional<T>&, const optional<U>&);
+    constexpr bool operator<(const optional<T>&, const optional<U>&);
   template <class T, class U>
-  constexpr bool operator>(const optional<T>&, const optional<U>&);
+    constexpr bool operator>(const optional<T>&, const optional<U>&);
   template <class T, class U>
-  constexpr bool operator<=(const optional<T>&, const optional<U>&);
+    constexpr bool operator<=(const optional<T>&, const optional<U>&);
   template <class T, class U>
-  constexpr bool operator>=(const optional<T>&, const optional<U>&);
-
-  // 23.6.7 comparison with nullopt
-  template <class T> constexpr bool operator==(const optional<T>&, nullopt_t) noexcept;
-  template <class T> constexpr bool operator==(nullopt_t, const optional<T>&) noexcept;
-  template <class T> constexpr bool operator!=(const optional<T>&, nullopt_t) noexcept;
-  template <class T> constexpr bool operator!=(nullopt_t, const optional<T>&) noexcept;
-  template <class T> constexpr bool operator<(const optional<T>&, nullopt_t) noexcept;
-  template <class T> constexpr bool operator<(nullopt_t, const optional<T>&) noexcept;
-  template <class T> constexpr bool operator<=(const optional<T>&, nullopt_t) noexcept;
-  template <class T> constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept;
-  template <class T> constexpr bool operator>(const optional<T>&, nullopt_t) noexcept;
-  template <class T> constexpr bool operator>(nullopt_t, const optional<T>&) noexcept;
-  template <class T> constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept;
-  template <class T> constexpr bool operator>=(nullopt_t, const optional<T>&) noexcept;
-
-  // 23.6.8, comparison with T
-  template <class T, class U> constexpr bool operator==(const optional<T>&, const U&);
-  template <class T, class U> constexpr bool operator==(const T&, const optional<U>&);
-  template <class T, class U> constexpr bool operator!=(const optional<T>&, const U&);
-  template <class T, class U> constexpr bool operator!=(const T&, const optional<U>&);
-  template <class T, class U> constexpr bool operator<(const optional<T>&, const U&);
-  template <class T, class U> constexpr bool operator<(const T&, const optional<U>&);
-  template <class T, class U> constexpr bool operator<=(const optional<T>&, const U&);
-  template <class T, class U> constexpr bool operator<=(const T&, const optional<U>&);
-  template <class T, class U> constexpr bool operator>(const optional<T>&, const U&);
-  template <class T, class U> constexpr bool operator>(const T&, const optional<U>&);
-  template <class T, class U> constexpr bool operator>=(const optional<T>&, const U&);
-  template <class T, class U> constexpr bool operator>=(const T&, const optional<U>&);
-
-  // 23.6.9, specialized algorithms
-  template <class T> void swap(optional<T>&, optional<T>&) noexcept(see below ); // constexpr in C++20
-  template <class T> constexpr optional<see below > make_optional(T&&);
-  template <class T, class... Args>
+    constexpr bool operator>=(const optional<T>&, const optional<U>&);
+  template<class T, three_way_comparable_with<T> U>
+    constexpr compare_three_way_result_t<T, U>
+      operator<=>(const optional<T>&, const optional<U>&); // since C++20
+
+  // [optional.nullops], comparison with nullopt
+  template<class T> constexpr bool operator==(const optional<T>&, nullopt_t) noexcept;
+  template<class T> constexpr bool operator==(nullopt_t, const optional<T>&) noexcept; // until C++17
+  template<class T> constexpr bool operator!=(const optional<T>&, nullopt_t) noexcept; // until C++17
+  template<class T> constexpr bool operator!=(nullopt_t, const optional<T>&) noexcept; // until C++17
+  template<class T> constexpr bool operator<(const optional<T>&, nullopt_t) noexcept;  // until C++17
+  template<class T> constexpr bool operator<(nullopt_t, const optional<T>&) noexcept;  // until C++17
+  template<class T> constexpr bool operator<=(const optional<T>&, nullopt_t) noexcept; // until C++17
+  template<class T> constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept; // until C++17
+  template<class T> constexpr bool operator>(const optional<T>&, nullopt_t) noexcept;  // until C++17
+  template<class T> constexpr bool operator>(nullopt_t, const optional<T>&) noexcept;  // until C++17
+  template<class T> constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept; // until C++17
+  template<class T> constexpr bool operator>=(nullopt_t, const optional<T>&) noexcept; // until C++17
+  template<class T>
+    constexpr strong_ordering operator<=>(const optional<T>&, nullopt_t) noexcept;     // since C++20
+
+  // [optional.comp.with.t], comparison with T
+  template<class T, class U> constexpr bool operator==(const optional<T>&, const U&);
+  template<class T, class U> constexpr bool operator==(const T&, const optional<U>&);
+  template<class T, class U> constexpr bool operator!=(const optional<T>&, const U&);
+  template<class T, class U> constexpr bool operator!=(const T&, const optional<U>&);
+  template<class T, class U> constexpr bool operator<(const optional<T>&, const U&);
+  template<class T, class U> constexpr bool operator<(const T&, const optional<U>&);
+  template<class T, class U> constexpr bool operator<=(const optional<T>&, const U&);
+  template<class T, class U> constexpr bool operator<=(const T&, const optional<U>&);
+  template<class T, class U> constexpr bool operator>(const optional<T>&, const U&);
+  template<class T, class U> constexpr bool operator>(const T&, const optional<U>&);
+  template<class T, class U> constexpr bool operator>=(const optional<T>&, const U&);
+  template<class T, class U> constexpr bool operator>=(const T&, const optional<U>&);
+  template<class T, class U>
+      requires (!is-derived-from-optional<U>) && three_way_comparable_with<T, U>
+    constexpr compare_three_way_result_t<T, U>
+      operator<=>(const optional<T>&, const U&);                                       // since C++20
+
+  // [optional.specalg], specialized algorithms
+  template<class T>
+    void swap(optional<T>&, optional<T>&) noexcept(see below ); // constexpr in C++20
+
+  template<class T>
+    constexpr optional<see below > make_optional(T&&);
+  template<class T, class... Args>
     constexpr optional<T> make_optional(Args&&... args);
-  template <class T, class U, class... Args>
+  template<class T, class U, class... Args>
     constexpr optional<T> make_optional(initializer_list<U> il, Args&&... args);
 
-  // 23.6.10, hash support
-  template <class T> struct hash;
-  template <class T> struct hash<optional<T>>;
+  // [optional.hash], hash support
+  template<class T> struct hash;
+  template<class T> struct hash<optional<T>>;
 
-  template <class T> class optional {
+  template<class T>
+  class optional {
   public:
     using value_type = T;
 
-    // 23.6.3.1, constructors
+    // [optional.ctor], constructors
     constexpr optional() noexcept;
     constexpr optional(nullopt_t) noexcept;
     constexpr optional(const optional &);
     constexpr optional(optional &&) noexcept(see below);
-    template <class... Args> constexpr explicit optional(in_place_t, Args &&...);
-    template <class U, class... Args>
+    template<class... Args>
+      constexpr explicit optional(in_place_t, Args &&...);
+    template<class U, class... Args>
       constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
-    template <class U = T>
+    template<class U = T>
       constexpr explicit(see-below) optional(U &&);
-    template <class U>
-      explicit(see-below) optional(const optional<U> &);         // constexpr in C++20
-    template <class U>
-      explicit(see-below) optional(optional<U> &&);              // constexpr in C++20
+    template<class U>
+      explicit(see-below) optional(const optional<U> &);                          // constexpr in C++20
+    template<class U>
+      explicit(see-below) optional(optional<U> &&);                               // constexpr in C++20
 
-    // 23.6.3.2, destructor
+    // [optional.dtor], destructor
     ~optional(); // constexpr in C++20
 
-    // 23.6.3.3, assignment
-    optional &operator=(nullopt_t) noexcept;                     // constexpr in C++20
+    // [optional.assign], assignment
+    optional &operator=(nullopt_t) noexcept;                                      // constexpr in C++20
     constexpr optional &operator=(const optional &);
     constexpr optional &operator=(optional &&) noexcept(see below);
-    template <class U = T> optional &operator=(U &&);            // constexpr in C++20
-    template <class U> optional &operator=(const optional<U> &); // constexpr in C++20
-    template <class U> optional &operator=(optional<U> &&);      // constexpr in C++20
-    template <class... Args> T& emplace(Args &&...);             // constexpr in C++20
-    template <class U, class... Args>
-      T& emplace(initializer_list<U>, Args &&...);               // constexpr in C++20
-
-    // 23.6.3.4, swap
+    template<class U = T> optional &operator=(U &&);                              // constexpr in C++20
+    template<class U> optional &operator=(const optional<U> &);                   // constexpr in C++20
+    template<class U> optional &operator=(optional<U> &&);                        // constexpr in C++20
+    template<class... Args> T& emplace(Args &&...);                               // constexpr in C++20
+    template<class U, class... Args> T& emplace(initializer_list<U>, Args &&...); // constexpr in C++20
+
+    // [optional.swap], swap
     void swap(optional &) noexcept(see below ); // constexpr in C++20
 
-    // 23.6.3.5, observers
+    // [optional.observe], observers
     constexpr T const *operator->() const;
     constexpr T *operator->();
     constexpr T const &operator*() const &;
@@ -129,8 +148,8 @@ namespace std {
     constexpr T &value() &;
     constexpr T &&value() &&;
     constexpr const T &&value() const &&;
-    template <class U> constexpr T value_or(U &&) const &;
-    template <class U> constexpr T value_or(U &&) &&;
+    template<class U> constexpr T value_or(U &&) const &;
+    template<class U> constexpr T value_or(U &&) &&;
 
     // [optional.monadic], monadic operations
     template<class F> constexpr auto and_then(F&& f) &;         // since C++23
@@ -144,15 +163,15 @@ namespace std {
     template<class F> constexpr optional or_else(F&& f) &&;     // since C++23
     template<class F> constexpr optional or_else(F&& f) const&; // since C++23
 
-    // 23.6.3.6, modifiers
-    void reset() noexcept; // constexpr in C++20
+    // [optional.mod], modifiers
+    void reset() noexcept;                                      // constexpr in C++20
 
   private:
-    T *val; // exposition only
+    T *val;         // exposition only
   };
 
-template<class T>
-  optional(T) -> optional<T>;
+  template<class T>
+    optional(T) -> optional<T>;
 
 } // namespace std
 
@@ -160,6 +179,8 @@ template<class T>
 
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__availability>
+#include <__compare/compare_three_way_result.h>
+#include <__compare/three_way_comparable.h>
 #include <__concepts/invocable.h>
 #include <__config>
 #include <__functional/hash.h>
@@ -633,6 +654,14 @@ using __optional_sfinae_assign_base_t = __sfinae_assign_base<
 
 template<class _Tp>
 class optional;
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Tp>
+concept __is_derived_from_optional = requires(const _Tp& __t) { []<class __Up>(const optional<__Up>&) {}(__t); };
+
+#  endif // _LIBCPP_STD_VER >= 20
+
 template <class _Tp>
 struct __is_std_optional : false_type {};
 template <class _Tp> struct __is_std_optional<optional<_Tp>> : true_type {};
@@ -1288,6 +1317,18 @@ operator>=(const optional<_Tp>& __x, const optional<_Up>& __y)
     return *__x >= *__y;
 }
 
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Tp, three_way_comparable_with<_Tp> _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+    if (__x && __y)
+        return *__x <=> *__y;
+    return __x.has_value() <=> __y.has_value();
+}
+
+#endif // _LIBCPP_STD_VER >= 20
+
 // Comparisons with nullopt
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY constexpr
@@ -1297,6 +1338,8 @@ operator==(const optional<_Tp>& __x, nullopt_t) noexcept
     return !static_cast<bool>(__x);
 }
 
+#if _LIBCPP_STD_VER <= 17
+
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY constexpr
 bool
@@ -1385,6 +1428,15 @@ operator>=(nullopt_t, const optional<_Tp>& __x) noexcept
     return !static_cast<bool>(__x);
 }
 
+#else // _LIBCPP_STD_VER <= 17
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept {
+    return __x.has_value() <=> false;
+}
+
+#endif // _LIBCPP_STD_VER <= 17
+
 // Comparisons with T
 template <class _Tp, class _Up>
 _LIBCPP_INLINE_VISIBILITY constexpr
@@ -1530,6 +1582,17 @@ operator>=(const _Tp& __v, const optional<_Up>& __x)
     return static_cast<bool>(__x) ? __v >= *__x : true;
 }
 
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Tp, class _Up>
+  requires(!__is_derived_from_optional<_Up>) && three_way_comparable_with<_Tp, _Up>
+_LIBCPP_HIDE_FROM_ABI constexpr compare_three_way_result_t<_Tp, _Up>
+operator<=>(const optional<_Tp>& __x, const _Up& __v) {
+    return __x.has_value() ? *__x <=> __v : strong_ordering::less;
+}
+
+#endif // _LIBCPP_STD_VER >= 20
+
 
 template <class _Tp>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20

diff  --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..18abbdc1229d0
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <optional>
+
+// [optional.comp.with.t], comparison with T
+
+// template<class T, class U>
+//     requires (!is-derived-from-optional<U>) && three_way_comparable_with<T, U>
+//   constexpr compare_three_way_result_t<T, U>
+//     operator<=>(const optional<T>&, const U&);
+
+#include <cassert>
+#include <compare>
+#include <optional>
+
+#include "test_comparisons.h"
+
+struct SomeInt {
+  int value_;
+
+  constexpr explicit SomeInt(int value = 0) : value_(value) {}
+
+  auto operator<=>(const SomeInt&) const = default;
+};
+
+template <class T, class U>
+concept HasSpaceship = requires(T t, U u) { t <=> u; };
+
+// SFINAE tests.
+
+static_assert(std::three_way_comparable_with<std::optional<int>, std::optional<int>>);
+static_assert(HasSpaceship<std::optional<int>, std::optional<int>>);
+
+static_assert(std::three_way_comparable_with<std::optional<SomeInt>, std::optional<SomeInt>>);
+static_assert(HasSpaceship<std::optional<SomeInt>, std::optional<SomeInt>>);
+
+static_assert(!HasSpaceship<std::optional<int>, std::optional<SomeInt>>);
+
+// Runtime and static tests.
+
+constexpr void test_custom_integral() {
+  {
+    SomeInt t{3};
+    std::optional<SomeInt> op{3};
+    assert((t <=> op) == std::strong_ordering::equal);
+    assert(testOrder(t, op, std::strong_ordering::equal));
+  }
+  {
+    SomeInt t{2};
+    std::optional<SomeInt> op{3};
+    assert((t <=> op) == std::strong_ordering::less);
+    assert(testOrder(t, op, std::strong_ordering::less));
+  }
+  {
+    SomeInt t{3};
+    std::optional<SomeInt> op{2};
+    assert((t <=> op) == std::strong_ordering::greater);
+    assert(testOrder(t, op, std::strong_ordering::greater));
+  }
+}
+
+constexpr void test_int() {
+  {
+    int t{3};
+    std::optional<int> op{3};
+    assert((t <=> op) == std::strong_ordering::equal);
+    assert(testOrder(t, op, std::strong_ordering::equal));
+  }
+  {
+    int t{2};
+    std::optional<int> op{3};
+    assert((t <=> op) == std::strong_ordering::less);
+    assert(testOrder(t, op, std::strong_ordering::less));
+  }
+  {
+    int t{3};
+    std::optional<int> op{2};
+    assert((t <=> op) == std::strong_ordering::greater);
+    assert(testOrder(t, op, std::strong_ordering::greater));
+  }
+}
+
+constexpr bool test() {
+  test_custom_integral();
+  test_int();
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test());
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp b/libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..9acc9b7909d0d
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <optional>
+
+// [optional.nullops], comparison with nullopt
+
+// template<class T>
+//   constexpr strong_ordering operator<=>(const optional<T>&, nullopt_t) noexcept;
+
+#include <cassert>
+#include <compare>
+#include <optional>
+
+#include "test_comparisons.h"
+
+constexpr bool test() {
+  {
+    std::optional<int> op;
+    assert((std::nullopt <=> op) == std::strong_ordering::equal);
+    assert(testOrder(std::nullopt, op, std::strong_ordering::equal));
+    assert((op <=> std::nullopt) == std::strong_ordering::equal);
+    assert(testOrder(op, std::nullopt, std::strong_ordering::equal));
+  }
+  {
+    std::optional<int> op{1};
+    assert((std::nullopt <=> op) == std::strong_ordering::less);
+    assert(testOrder(std::nullopt, op, std::strong_ordering::less));
+  }
+  {
+    std::optional<int> op{1};
+    assert((op <=> std::nullopt) == std::strong_ordering::greater);
+    assert(testOrder(op, std::nullopt, std::strong_ordering::greater));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test());
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp b/libcxx/test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..b00fe46f37ff5
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <optional>
+
+// [optional.relops], relational operators
+
+// template<class T, three_way_comparable_with<T> U>
+//   constexpr compare_three_way_result_t<T, U>
+//     operator<=>(const optional<T>&, const optional<U>&);
+
+#include <cassert>
+#include <compare>
+#include <optional>
+
+#include "test_comparisons.h"
+
+constexpr bool test() {
+  {
+    std::optional<int> op1;
+    std::optional<int> op2;
+
+    assert((op1 <=> op2) == std::strong_ordering::equal);
+    assert(testOrder(op1, op2, std::strong_ordering::equal));
+  }
+  {
+    std::optional<int> op1{3};
+    std::optional<int> op2{3};
+    assert((op1 <=> op1) == std::strong_ordering::equal);
+    assert(testOrder(op1, op1, std::strong_ordering::equal));
+    assert((op1 <=> op2) == std::strong_ordering::equal);
+    assert(testOrder(op1, op2, std::strong_ordering::equal));
+    assert((op2 <=> op1) == std::strong_ordering::equal);
+    assert(testOrder(op2, op1, std::strong_ordering::equal));
+  }
+  {
+    std::optional<int> op;
+    std::optional<int> op1{2};
+    std::optional<int> op2{3};
+    assert((op <=> op2) == std::strong_ordering::less);
+    assert(testOrder(op, op2, std::strong_ordering::less));
+    assert((op1 <=> op2) == std::strong_ordering::less);
+    assert(testOrder(op1, op2, std::strong_ordering::less));
+  }
+  {
+    std::optional<int> op;
+    std::optional<int> op1{3};
+    std::optional<int> op2{2};
+    assert((op1 <=> op) == std::strong_ordering::greater);
+    assert(testOrder(op1, op, std::strong_ordering::greater));
+    assert((op1 <=> op2) == std::strong_ordering::greater);
+    assert(testOrder(op1, op2, std::strong_ordering::greater));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  assert(test());
+  static_assert(test());
+  return 0;
+}


        


More information about the libcxx-commits mailing list