[libcxx-commits] [libcxx] [libc++][spaceship] Implements X::iterator container requirements. (PR #99343)

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 23 10:01:47 PDT 2024


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/99343

>From fcc71b513bfa4d2d18d37b144651683b99356019 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Wed, 17 Jul 2024 17:38:41 +0200
Subject: [PATCH 1/6] [libc++][spaceship] Implements X::iterator container
 requirements.

This implements the requirements for the container iterator requirements for
array, deque, vector, and vector<bool>.

Implements:
- LWG3352 strong_equality isn't a thing

Implements parts of:
- P1614R2 The Mothership has Landed

Fixes: https://github.com/llvm/llvm-project/issues/62486
---
 libcxx/docs/Status/Cxx20Issues.csv            |  2 +-
 libcxx/docs/Status/SpaceshipProjects.csv      |  2 +-
 libcxx/include/__bit_reference                | 14 ++++++++++++++
 libcxx/include/__iterator/wrap_iter.h         | 19 +++++++++++++++++++
 libcxx/include/deque                          | 15 +++++++++++++--
 .../sequences/array/iterators.pass.cpp        | 18 ++++++++++++++++++
 .../sequences/deque/iterators.pass.cpp        |  9 +++++++++
 .../sequences/vector.bool/iterators.pass.cpp  |  9 +++++++++
 .../sequences/vector/iterators.pass.cpp       | 10 +++++++++-
 9 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 1a40a4472a405..8a431c922a2d9 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -264,7 +264,7 @@
 "`3349 <https://wg21.link/LWG3349>`__","Missing ``__cpp_lib_constexpr_complex``\  for P0415R1","Prague","|Complete|","16.0"
 "`3350 <https://wg21.link/LWG3350>`__","Simplify return type of ``lexicographical_compare_three_way``\ ","Prague","|Complete|","17.0","|spaceship|"
 "`3351 <https://wg21.link/LWG3351>`__","``ranges::enable_safe_range``\  should not be constrained","Prague","|Complete|","15.0","|ranges|"
-"`3352 <https://wg21.link/LWG3352>`__","``strong_equality``\  isn't a thing","Prague","|Nothing To Do|","","|spaceship|"
+"`3352 <https://wg21.link/LWG3352>`__","``strong_equality``\  isn't a thing","Prague","|Complete|","19.0","|spaceship|"
 "`3354 <https://wg21.link/LWG3354>`__","``has_strong_structural_equality``\  has a meaningless definition","Prague","|Nothing To Do|","","|spaceship|"
 "`3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","|Complete|","15.0","|ranges|"
 "`3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\  should be ``__cpp_lib_is_nothrow_convertible``\ ","Prague","|Complete|","12.0"
diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index e1cf2044cfd78..4dc43cdbbd08f 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -83,7 +83,7 @@ Section,Description,Dependencies,Assignee,Complete
 "| `[string.view.synop] <https://wg21.link/string.view.synop>`_
 | `[string.view.comparison] <https://wg21.link/string.view.comparison>`_",| `basic_string_view <https://reviews.llvm.org/D130295>`_,None,Mark de Wever,|Complete|
 - `5.7 Clause 22: Containers library <https://wg21.link/p1614r2#clause-22-containers-library>`_,,,,
-| `[container.requirements.general] <https://wg21.link/container.requirements.general>`_,|,None,Unassigned,|Not Started|
+| `[container.requirements.general] <https://wg21.link/container.requirements.general>`_,|,None,Mark de Wever,|Complete|
 | `[array.syn] <https://wg21.link/array.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),| `array <https://reviews.llvm.org/D132265>`_,[expos.only.func],"| Adrian Vogelsgesang
 | Hristo Hristov",|Complete|
 | `[deque.syn] <https://wg21.link/deque.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),| `deque <https://reviews.llvm.org/D144821>`_,[expos.only.func],Hristo Hristov,|Complete|
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 606069d98be72..22637d4397412 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -16,6 +16,7 @@
 #include <__bit/countr.h>
 #include <__bit/invert_if.h>
 #include <__bit/popcount.h>
+#include <__compare/ordering.h>
 #include <__config>
 #include <__fwd/bit_reference.h>
 #include <__iterator/iterator_traits.h>
@@ -913,6 +914,7 @@ public:
     return __x.__seg_ == __y.__seg_ && __x.__ctz_ == __y.__ctz_;
   }
 
+#if _LIBCPP_STD_VER <= 17
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
   operator!=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x == __y);
@@ -937,6 +939,18 @@ public:
   operator>=(const __bit_iterator& __x, const __bit_iterator& __y) {
     return !(__x < __y);
   }
+#else  // _LIBCPP_STD_VER <= 17
+  _LIBCPP_HIDE_FROM_ABI constexpr friend strong_ordering
+  operator<=>(const __bit_iterator& __x, const __bit_iterator& __y) {
+    if (__x.__seg_ < __y.__seg_)
+      return strong_ordering::less;
+
+    if (__x.__seg_ == __y.__seg_)
+      return __x.__ctz_ <=> __y.__ctz_;
+
+    return strong_ordering::greater;
+  }
+#endif // _LIBCPP_STD_VER <= 17
 
 private:
   _LIBCPP_HIDE_FROM_ABI
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 252d13b26c9e2..a181cbd7c726e 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___ITERATOR_WRAP_ITER_H
 #define _LIBCPP___ITERATOR_WRAP_ITER_H
 
+#include <__compare/ordering.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
 #include <__memory/addressof.h>
@@ -119,6 +120,8 @@ operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
   return __x.base() == __y.base();
 }
 
+#if _LIBCPP_STD_VER <= 17
+
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
@@ -179,6 +182,22 @@ operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
   return !(__y < __x);
 }
 
+#else // _LIBCPP_STD_VER <= 17
+
+template <class _Iter1>
+_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
+operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noexcept {
+  return __x.base() <=> __y.base();
+}
+
+template <class _Iter1, class _Iter2>
+_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
+operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept {
+  return __x.base() <=> __y.base();
+}
+
+#endif // _LIBCPP_STD_VER <= 17
+
 template <class _Iter1, class _Iter2>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
 #ifndef _LIBCPP_CXX03_LANG
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 4fc994a6e229b..ff4c93dae6147 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -376,6 +376,7 @@ public:
     return __x.__ptr_ == __y.__ptr_;
   }
 
+#if _LIBCPP_STD_VER <= 17
   _LIBCPP_HIDE_FROM_ABI friend bool operator!=(const __deque_iterator& __x, const __deque_iterator& __y) {
     return !(__x == __y);
   }
@@ -395,6 +396,17 @@ public:
   _LIBCPP_HIDE_FROM_ABI friend bool operator>=(const __deque_iterator& __x, const __deque_iterator& __y) {
     return !(__x < __y);
   }
+#else  // _LIBCPP_STD_VER <= 17
+  _LIBCPP_HIDE_FROM_ABI friend strong_ordering operator<=>(const __deque_iterator& __x, const __deque_iterator& __y) {
+    if (__x.__m_iter_ < __y.__m_iter_)
+      return strong_ordering::less;
+
+    if (__x.__m_iter_ == __y.__m_iter_)
+      return __x.__ptr_ <=> __y.__ptr_;
+
+    return strong_ordering::greater;
+  }
+#endif // _LIBCPP_STD_VER <= 17
 
 private:
   _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(__map_iterator __m, pointer __p) _NOEXCEPT
@@ -2530,8 +2542,7 @@ inline _LIBCPP_HIDE_FROM_ABI bool operator<=(const deque<_Tp, _Allocator>& __x,
 template <class _Tp, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Tp>
 operator<=>(const deque<_Tp, _Allocator>& __x, const deque<_Tp, _Allocator>& __y) {
-  return std::lexicographical_compare_three_way(
-      __x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
+  return std::lexicographical_compare_three_way(__x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way);
 }
 
 #endif // _LIBCPP_STD_VER <= 17
diff --git a/libcxx/test/std/containers/sequences/array/iterators.pass.cpp b/libcxx/test/std/containers/sequences/array/iterators.pass.cpp
index 106bc45c70998..9cbe3cb396c9a 100644
--- a/libcxx/test/std/containers/sequences/array/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/iterators.pass.cpp
@@ -148,6 +148,15 @@ TEST_CONSTEXPR_CXX17 bool tests()
             assert(std::rbegin(c)  != std::rend(c));
             assert(std::cbegin(c)  != std::cend(c));
             assert(std::crbegin(c) != std::crend(c));
+
+#  if TEST_STD_VER >= 20
+            // P1614 + LWG3352
+            std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+            assert(r1 == std::strong_ordering::equal);
+
+            std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+            assert(r2 == std::strong_ordering::equal);
+#  endif
         }
         {
             typedef std::array<int, 0> C;
@@ -189,6 +198,15 @@ TEST_CONSTEXPR_CXX17 bool tests()
             assert(std::rbegin(c)  == std::rend(c));
             assert(std::cbegin(c)  == std::cend(c));
             assert(std::crbegin(c) == std::crend(c));
+
+#  if TEST_STD_VER >= 20
+            // P1614 + LWG3352
+            std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+            assert(r1 == std::strong_ordering::equal);
+
+            std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+            assert(r2 == std::strong_ordering::equal);
+#  endif
         }
     }
 #endif
diff --git a/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp b/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
index 1f06ffde41ac2..a8b5db656b205 100644
--- a/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
@@ -74,6 +74,15 @@ int main(int, char**)
 //         assert ( cii != c.begin());
 //         assert ( cii != c.cend());
 //         assert ( ii1 != c.end());
+
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+        assert(r1 == std::strong_ordering::equal);
+
+        std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+        assert(r2 == std::strong_ordering::equal);
+#  endif // TEST_STD_VER > 20
     }
 #endif
 
diff --git a/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp
index 9aaaac7a5557f..2a701cc4ad19f 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp
@@ -131,6 +131,15 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert ( (cii >= ii1 ));
         assert (cii - ii1 == 0);
         assert (ii1 - cii == 0);
+
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+        assert(r1 == std::strong_ordering::equal);
+
+        std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+        assert(r2 == std::strong_ordering::equal);
+#  endif // TEST_STD_VER > 20
     }
 #endif
 
diff --git a/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp b/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
index 70e0e35767e09..457791b8ee6af 100644
--- a/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
@@ -164,8 +164,16 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert ( (cii >= ii1 ));
         assert (cii - ii1 == 0);
         assert (ii1 - cii == 0);
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+        assert(r1 == std::strong_ordering::equal);
+
+        std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+        assert(r2 == std::strong_ordering::equal);
+#  endif // TEST_STD_VER > 20
     }
-#endif
+#endif // TEST_STD_VER > 11
 
     return true;
 }

>From 2dc8392f7882e3b1e135d18c14fee585ee6f0685 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 20 Jul 2024 12:45:41 +0200
Subject: [PATCH 2/6] bounded_iterator

---
 libcxx/include/__iterator/bounded_iter.h      | 11 +++
 .../span.iterators/iterator.pass.cpp          | 89 +++++++++++++++++++
 .../string.view.iterators/iterators.pass.cpp  | 84 +++++++++++++++++
 3 files changed, 184 insertions(+)
 create mode 100644 libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
 create mode 100644 libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp

diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h
index a8f66f4a0126f..b8a72c73b0a32 100644
--- a/libcxx/include/__iterator/bounded_iter.h
+++ b/libcxx/include/__iterator/bounded_iter.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___ITERATOR_BOUNDED_ITER_H
 
 #include <__assert>
+#include <__compare/ordering.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
@@ -201,6 +202,7 @@ struct __bounded_iter {
   operator==(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ == __y.__current_;
   }
+#if _LIBCPP_STD_VER <= 17
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
   operator!=(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ != __y.__current_;
@@ -222,6 +224,15 @@ struct __bounded_iter {
     return __x.__current_ >= __y.__current_;
   }
 
+#else // _LIBCPP_STD_VER <= 17
+
+  _LIBCPP_HIDE_FROM_ABI constexpr friend strong_ordering
+  operator<=>(__bounded_iter const& __x, __bounded_iter const& __y) noexcept {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+#endif // _LIBCPP_STD_VER <= 17
+
 private:
   template <class>
   friend struct pointer_traits;
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
new file mode 100644
index 0000000000000..cd834fe5fb4fc
--- /dev/null
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <span>
+
+// class iterator
+
+#include <span>
+#include <cassert>
+#include <string>
+#include <version> // __cpp_lib_ranges_as_const is not defined in span.
+
+#include "test_macros.h"
+
+template <class T>
+TEST_CONSTEXPR void test_type() {
+#
+
+  using C = std::span<T>;
+  typename C::iterator ii1{}, ii2{};
+  typename C::iterator ii4 = ii1;
+  // TODO Test against C++23 after implementing
+  // P2278R4 cbegin should always return a constant iterator
+#ifdef __cpp_lib_ranges_as_const
+  typename C::const_iterator cii{};
+#endif
+  assert(ii1 == ii2);
+  assert(ii1 == ii4);
+#ifdef __cpp_lib_ranges_as_const
+  assert(ii1 == cii);
+#endif
+
+  assert(!(ii1 != ii2));
+#ifdef __cpp_lib_ranges_as_const
+  assert(!(ii1 != cii));
+#endif
+
+  T v;
+  C c{&v, 1};
+  assert(c.begin() == std::begin(c));
+  assert(c.rbegin() == std::rbegin(c));
+#ifdef __cpp_lib_ranges_as_const
+  assert(c.cbegin() == std::cbegin(c));
+  assert(c.crbegin() == std::crbegin(c));
+#endif
+
+  assert(c.end() == std::end(c));
+  assert(c.rend() == std::rend(c));
+#ifdef __cpp_lib_ranges_as_const
+  assert(c.cend() == std::cend(c));
+  assert(c.crend() == std::crend(c));
+#endif
+
+  assert(std::begin(c) != std::end(c));
+  assert(std::rbegin(c) != std::rend(c));
+#ifdef __cpp_lib_ranges_as_const
+  assert(std::cbegin(c) != std::cend(c));
+  assert(std::crbegin(c) != std::crend(c));
+#endif
+
+  // P1614 + LWG3352
+  std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+  assert(r1 == std::strong_ordering::equal);
+
+  std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+  assert(r2 == std::strong_ordering::equal);
+}
+
+TEST_CONSTEXPR bool test() {
+  test_type<char>();
+  test_type<int>();
+  test_type<std::string>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test(), "");
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp b/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp
new file mode 100644
index 0000000000000..6a2cd046a2b68
--- /dev/null
+++ b/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: !stdlib=libc++ && (c++03 || c++11 || c++14)
+
+// <string_view>
+
+// class iterator
+
+#include <string_view>
+#include <cassert>
+#include <iterator>
+
+#include "test_macros.h"
+#include "make_string.h"
+
+template <class CharT>
+TEST_CONSTEXPR_CXX14 void test_type() {
+  using C                  = std::basic_string_view<CharT>;
+  typename C::iterator ii1 = typename C::iterator(), ii2 = typename C::iterator();
+  typename C::iterator ii4       = ii1;
+  typename C::const_iterator cii = typename C::const_iterator();
+  assert(ii1 == ii2);
+  assert(ii1 == ii4);
+  assert(ii1 == cii);
+
+  assert(!(ii1 != ii2));
+  assert(!(ii1 != cii));
+
+#if TEST_STD_VER >= 11
+  C c = MAKE_STRING_VIEW(CharT, "abc");
+  assert(c.begin() == std::begin(c));
+  assert(c.rbegin() == std::rbegin(c));
+  assert(c.cbegin() == std::cbegin(c));
+  assert(c.crbegin() == std::crbegin(c));
+
+  assert(c.end() == std::end(c));
+  assert(c.rend() == std::rend(c));
+  assert(c.cend() == std::cend(c));
+  assert(c.crend() == std::crend(c));
+
+  assert(std::begin(c) != std::end(c));
+  assert(std::rbegin(c) != std::rend(c));
+  assert(std::cbegin(c) != std::cend(c));
+  assert(std::crbegin(c) != std::crend(c));
+#endif
+
+#if TEST_STD_VER >= 20
+  // P1614 + LWG3352
+  std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
+  assert(r1 == std::strong_ordering::equal);
+
+  std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+  assert(r2 == std::strong_ordering::equal);
+#endif
+}
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  test_type<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_type<wchar_t>();
+#endif
+#ifndef TEST_HAS_NO_CHAR8_T
+  test_type<char8_t>();
+#endif
+  test_type<char16_t>();
+  test_type<char32_t>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 14
+  static_assert(test(), "");
+#endif
+
+  return 0;
+}

>From cd9e497a3ad2e0693dd4800258ec42f3927cf58f Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 20 Jul 2024 13:55:32 +0200
Subject: [PATCH 3/6] CI fixes.

---
 libcxx/include/__iterator/bounded_iter.h       | 16 +++++++++++-----
 .../iterators/bounded_iter/comparison.pass.cpp | 18 ++++++++++++++++--
 .../span.iterators/iterator.pass.cpp           |  4 +++-
 .../string.view.iterators/iterators.pass.cpp   |  5 +++--
 libcxx/test/support/test_iterators.h           |  2 ++
 5 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h
index b8a72c73b0a32..10825043bf3bb 100644
--- a/libcxx/include/__iterator/bounded_iter.h
+++ b/libcxx/include/__iterator/bounded_iter.h
@@ -12,6 +12,7 @@
 
 #include <__assert>
 #include <__compare/ordering.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
@@ -202,7 +203,7 @@ struct __bounded_iter {
   operator==(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ == __y.__current_;
   }
-#if _LIBCPP_STD_VER <= 17
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
   operator!=(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ != __y.__current_;
@@ -224,14 +225,19 @@ struct __bounded_iter {
     return __x.__current_ >= __y.__current_;
   }
 
-#else // _LIBCPP_STD_VER <= 17
-
+#if _LIBCPP_STD_VER >= 20
+  // It is not required that the underlying iterator supports operator<=>.
+  // Therefore this operator is only conditionally provided, which requires a
+  // templated function.
+  template <class _Tp = void>
+    requires requires(_Iterator const& __i) {
+      { __i <=> __i } -> same_as<strong_ordering>;
+    }
   _LIBCPP_HIDE_FROM_ABI constexpr friend strong_ordering
   operator<=>(__bounded_iter const& __x, __bounded_iter const& __y) noexcept {
     return __x.__current_ <=> __y.__current_;
   }
-
-#endif // _LIBCPP_STD_VER <= 17
+#endif // _LIBCPP_STD_VER >= 20
 
 private:
   template <class>
diff --git a/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
index 9c5df5da55b9c..071a67fce9332 100644
--- a/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
@@ -11,6 +11,7 @@
 //
 // Comparison operators
 
+#include <concepts>
 #include <__iterator/bounded_iter.h>
 
 #include "test_iterators.h"
@@ -59,6 +60,16 @@ TEST_CONSTEXPR_CXX14 bool tests() {
     assert(iter1 >= iter1);
   }
 
+#if TEST_STD_VER >= 20
+  if constexpr (requires(std::__bounded_iter<Iter> const iter) {
+                  { iter <=> iter } -> std::same_as<std::strong_ordering>;
+                }) {
+    // P1614
+    std::same_as<std::strong_ordering> decltype(auto) r1 = iter1 <=> iter2;
+    assert(r1 == std::strong_ordering::less);
+  }
+#endif
+
   return true;
 }
 
@@ -69,8 +80,11 @@ int main(int, char**) {
 #endif
 
 #if TEST_STD_VER > 17
-  tests<contiguous_iterator<int*> >();
-  static_assert(tests<contiguous_iterator<int*> >(), "");
+  tests<contiguous_iterator<int*>>();
+  static_assert(tests<contiguous_iterator<int*>>());
+
+  tests<three_way_contiguous_iterator<int*>>();
+  static_assert(tests<three_way_contiguous_iterator<int*>>());
 #endif
 
   return 0;
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
index cd834fe5fb4fc..3d37267d61d63 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
@@ -12,8 +12,10 @@
 
 // class iterator
 
-#include <span>
 #include <cassert>
+#include <concepts>
+#include <iterator>
+#include <span>
 #include <string>
 #include <version> // __cpp_lib_ranges_as_const is not defined in span.
 
diff --git a/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp b/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp
index 6a2cd046a2b68..75d492bf7b3c6 100644
--- a/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp
+++ b/libcxx/test/std/strings/string.view/string.view.iterators/iterators.pass.cpp
@@ -12,9 +12,10 @@
 
 // class iterator
 
-#include <string_view>
 #include <cassert>
+#include <concepts>
 #include <iterator>
+#include <string_view>
 
 #include "test_macros.h"
 #include "make_string.h"
@@ -32,7 +33,7 @@ TEST_CONSTEXPR_CXX14 void test_type() {
   assert(!(ii1 != ii2));
   assert(!(ii1 != cii));
 
-#if TEST_STD_VER >= 11
+#if TEST_STD_VER >= 17
   C c = MAKE_STRING_VIEW(CharT, "abc");
   assert(c.begin() == std::begin(c));
   assert(c.rbegin() == std::rbegin(c));
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 31564a3977317..95d1b7df0007c 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -389,6 +389,8 @@ class contiguous_iterator
     friend TEST_CONSTEXPR bool operator> (const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ >  y.it_;}
     friend TEST_CONSTEXPR bool operator>=(const contiguous_iterator& x, const contiguous_iterator& y) {return x.it_ >= y.it_;}
 
+    // Note no operator<=>, use three_way_contiguous_iterator for testing operator<=>
+
     friend TEST_CONSTEXPR It base(const contiguous_iterator& i) { return i.it_; }
 
     template <class T>

>From 0739782f26319457bf389425df918a27db08398f Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 20 Jul 2024 20:09:59 +0200
Subject: [PATCH 4/6] CI fixes and improvements

Note the code is not yet conforming.
---
 libcxx/include/__iterator/bounded_iter.h      | 13 +++----
 libcxx/include/__iterator/wrap_iter.h         | 15 ++++----
 libcxx/include/deque                          | 11 +++---
 .../sequences/array/iterators.pass.cpp        |  4 +--
 .../sequences/deque/iterators.pass.cpp        | 18 +++++++++-
 .../sequences/vector.bool/iterators.pass.cpp  | 30 +++++++++++++++-
 .../sequences/vector/iterators.pass.cpp       | 34 ++++++++++++++++++-
 .../span.iterators/iterator.pass.cpp          |  4 ++-
 8 files changed, 104 insertions(+), 25 deletions(-)

diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h
index 10825043bf3bb..805730f5b5042 100644
--- a/libcxx/include/__iterator/bounded_iter.h
+++ b/libcxx/include/__iterator/bounded_iter.h
@@ -12,7 +12,7 @@
 
 #include <__assert>
 #include <__compare/ordering.h>
-#include <__concepts/same_as.h>
+#include <__compare/three_way_comparable.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
 #include <__memory/pointer_traits.h>
@@ -226,15 +226,10 @@ struct __bounded_iter {
   }
 
 #if _LIBCPP_STD_VER >= 20
-  // It is not required that the underlying iterator supports operator<=>.
-  // Therefore this operator is only conditionally provided, which requires a
-  // templated function.
-  template <class _Tp = void>
-    requires requires(_Iterator const& __i) {
-      { __i <=> __i } -> same_as<strong_ordering>;
-    }
   _LIBCPP_HIDE_FROM_ABI constexpr friend strong_ordering
-  operator<=>(__bounded_iter const& __x, __bounded_iter const& __y) noexcept {
+  operator<=>(__bounded_iter const& __x, __bounded_iter const& __y) noexcept
+    requires three_way_comparable<_Iterator, strong_ordering>
+  {
     return __x.__current_ <=> __y.__current_;
   }
 #endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index a181cbd7c726e..8462b0c823700 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___ITERATOR_WRAP_ITER_H
 
 #include <__compare/ordering.h>
+#include <__compare/three_way_comparable.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
 #include <__memory/addressof.h>
@@ -120,8 +121,6 @@ operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
   return __x.base() == __y.base();
 }
 
-#if _LIBCPP_STD_VER <= 17
-
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
 operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
@@ -182,21 +181,25 @@ operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
   return !(__y < __x);
 }
 
-#else // _LIBCPP_STD_VER <= 17
+#if _LIBCPP_STD_VER >= 20
 
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
-operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noexcept {
+operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noexcept
+  requires three_way_comparable<_Iter1, strong_ordering>
+{
   return __x.base() <=> __y.base();
 }
 
 template <class _Iter1, class _Iter2>
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
-operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept {
+operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept
+  requires three_way_comparable_with<_Iter1, _Iter2, strong_ordering>
+{
   return __x.base() <=> __y.base();
 }
 
-#endif // _LIBCPP_STD_VER <= 17
+#endif // _LIBCPP_STD_VER >= 20
 
 template <class _Iter1, class _Iter2>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
diff --git a/libcxx/include/deque b/libcxx/include/deque
index ff4c93dae6147..782a9db79c7ae 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -376,7 +376,6 @@ public:
     return __x.__ptr_ == __y.__ptr_;
   }
 
-#if _LIBCPP_STD_VER <= 17
   _LIBCPP_HIDE_FROM_ABI friend bool operator!=(const __deque_iterator& __x, const __deque_iterator& __y) {
     return !(__x == __y);
   }
@@ -396,8 +395,12 @@ public:
   _LIBCPP_HIDE_FROM_ABI friend bool operator>=(const __deque_iterator& __x, const __deque_iterator& __y) {
     return !(__x < __y);
   }
-#else  // _LIBCPP_STD_VER <= 17
-  _LIBCPP_HIDE_FROM_ABI friend strong_ordering operator<=>(const __deque_iterator& __x, const __deque_iterator& __y) {
+
+#if _LIBCPP_STD_VER >= 20
+  //  template <class _Tp = void>
+  _LIBCPP_HIDE_FROM_ABI friend strong_ordering operator<=>(const __deque_iterator& __x, const __deque_iterator& __y)
+    requires three_way_comparable<pointer, strong_ordering>
+  {
     if (__x.__m_iter_ < __y.__m_iter_)
       return strong_ordering::less;
 
@@ -406,7 +409,7 @@ public:
 
     return strong_ordering::greater;
   }
-#endif // _LIBCPP_STD_VER <= 17
+#endif // _LIBCPP_STD_VER >= 20
 
 private:
   _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(__map_iterator __m, pointer __p) _NOEXCEPT
diff --git a/libcxx/test/std/containers/sequences/array/iterators.pass.cpp b/libcxx/test/std/containers/sequences/array/iterators.pass.cpp
index 9cbe3cb396c9a..710994c68295e 100644
--- a/libcxx/test/std/containers/sequences/array/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/iterators.pass.cpp
@@ -154,7 +154,7 @@ TEST_CONSTEXPR_CXX17 bool tests()
             std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
             assert(r1 == std::strong_ordering::equal);
 
-            std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+            std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
             assert(r2 == std::strong_ordering::equal);
 #  endif
         }
@@ -204,7 +204,7 @@ TEST_CONSTEXPR_CXX17 bool tests()
             std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
             assert(r1 == std::strong_ordering::equal);
 
-            std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+            std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
             assert(r2 == std::strong_ordering::equal);
 #  endif
         }
diff --git a/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp b/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
index a8b5db656b205..396fd56dc6413 100644
--- a/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
@@ -41,7 +41,23 @@ int main(int, char**)
     i = c.begin();
     C::const_iterator j;
     j = c.cbegin();
+
     assert(i == j);
+    assert(!(i != j));
+
+    assert(!(i < j));
+    assert((i <= j));
+
+    assert(!(i > j));
+    assert((i >= j));
+
+#  if TEST_STD_VER >= 20
+    // P1614 + LWG3352
+    // When the allocator does not have operator<=> then neither does the iterator.
+    // Make sure to test with an allocator that does not have operator<=>.
+    static_assert(!std::three_way_comparable<min_allocator<int>, std::strong_ordering>);
+    static_assert(!std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+#  endif
     }
 #endif
 #if TEST_STD_VER > 11
@@ -80,7 +96,7 @@ int main(int, char**)
         std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
         assert(r1 == std::strong_ordering::equal);
 
-        std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+        std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
         assert(r2 == std::strong_ordering::equal);
 #  endif // TEST_STD_VER > 20
     }
diff --git a/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp
index 2a701cc4ad19f..1e4877e8d2443 100644
--- a/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector.bool/iterators.pass.cpp
@@ -77,7 +77,21 @@ TEST_CONSTEXPR_CXX20 bool tests()
         C::iterator i = c.begin();
         C::iterator j = c.end();
         assert(std::distance(i, j) == 0);
+
         assert(i == j);
+        assert(!(i != j));
+
+        assert(!(i < j));
+        assert((i <= j));
+
+        assert(!(i > j));
+        assert((i >= j));
+
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        std::same_as<std::strong_ordering> decltype(auto) r = i <=> j;
+        assert(r == std::strong_ordering::equal);
+#  endif
     }
     {
         typedef bool T;
@@ -86,7 +100,21 @@ TEST_CONSTEXPR_CXX20 bool tests()
         C::const_iterator i = c.begin();
         C::const_iterator j = c.end();
         assert(std::distance(i, j) == 0);
+
         assert(i == j);
+        assert(!(i != j));
+
+        assert(!(i < j));
+        assert((i <= j));
+
+        assert(!(i > j));
+        assert((i >= j));
+
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        std::same_as<std::strong_ordering> decltype(auto) r = i <=> j;
+        assert(r == std::strong_ordering::equal);
+#  endif
     }
     {
         typedef bool T;
@@ -137,7 +165,7 @@ TEST_CONSTEXPR_CXX20 bool tests()
         std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
         assert(r1 == std::strong_ordering::equal);
 
-        std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+        std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
         assert(r2 == std::strong_ordering::equal);
 #  endif // TEST_STD_VER > 20
     }
diff --git a/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp b/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
index 457791b8ee6af..1d750cb1110f9 100644
--- a/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
@@ -87,7 +87,23 @@ TEST_CONSTEXPR_CXX20 bool tests()
         C::iterator i = c.begin();
         C::iterator j = c.end();
         assert(std::distance(i, j) == 0);
+
         assert(i == j);
+        assert(!(i != j));
+
+        assert(!(i < j));
+        assert((i <= j));
+
+        assert(!(i > j));
+        assert((i >= j));
+
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        // When the allocator does not have operator<=> then neither does the iterator.
+        // Make sure to test with an allocator that does not have operator<=>.
+        static_assert(!std::three_way_comparable<min_allocator<int>, std::strong_ordering>);
+        static_assert(!std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+#  endif
     }
     {
         typedef int T;
@@ -96,7 +112,23 @@ TEST_CONSTEXPR_CXX20 bool tests()
         C::const_iterator i = c.begin();
         C::const_iterator j = c.end();
         assert(std::distance(i, j) == 0);
+
         assert(i == j);
+        assert(!(i != j));
+
+        assert(!(i < j));
+        assert((i <= j));
+
+        assert(!(i > j));
+        assert((i >= j));
+
+#  if TEST_STD_VER >= 20
+        // P1614 + LWG3352
+        // When the allocator does not have operator<=> then neither does the iterator.
+        // Make sure to test with an allocator that does not have operator<=>.
+        static_assert(!std::three_way_comparable<min_allocator<int>, std::strong_ordering>);
+        static_assert(!std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+#  endif
     }
     {
         typedef int T;
@@ -169,7 +201,7 @@ TEST_CONSTEXPR_CXX20 bool tests()
         std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
         assert(r1 == std::strong_ordering::equal);
 
-        std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+        std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
         assert(r2 == std::strong_ordering::equal);
 #  endif // TEST_STD_VER > 20
     }
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
index 3d37267d61d63..b097e4dc994a7 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
@@ -71,8 +71,10 @@ TEST_CONSTEXPR void test_type() {
   std::same_as<std::strong_ordering> decltype(auto) r1 = ii1 <=> ii2;
   assert(r1 == std::strong_ordering::equal);
 
-  std::same_as<std::strong_ordering> decltype(auto) r2 = ii1 <=> ii2;
+#ifdef __cpp_lib_ranges_as_const
+  std::same_as<std::strong_ordering> decltype(auto) r2 = cii <=> ii2;
   assert(r2 == std::strong_ordering::equal);
+#endif
 }
 
 TEST_CONSTEXPR bool test() {

>From ecd6887bec874200a5c4167b464630d192d012ed Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 21 Jul 2024 17:36:49 +0200
Subject: [PATCH 5/6] Polish the patch.

---
 libcxx/include/__iterator/bounded_iter.h      | 19 ++++++++---
 libcxx/include/__iterator/wrap_iter.h         | 34 ++++++++++++++-----
 libcxx/include/deque                          | 21 +++++++++---
 .../sequences/deque/iterators.pass.cpp        |  8 +++--
 .../sequences/vector/iterators.pass.cpp       | 17 +++++++---
 5 files changed, 75 insertions(+), 24 deletions(-)

diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h
index 805730f5b5042..78a4e46cd4281 100644
--- a/libcxx/include/__iterator/bounded_iter.h
+++ b/libcxx/include/__iterator/bounded_iter.h
@@ -204,10 +204,13 @@ struct __bounded_iter {
     return __x.__current_ == __y.__current_;
   }
 
+#if _LIBCPP_STD_VER <= 17
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
   operator!=(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ != __y.__current_;
   }
+#endif
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
   operator<(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ < __y.__current_;
@@ -227,10 +230,18 @@ struct __bounded_iter {
 
 #if _LIBCPP_STD_VER >= 20
   _LIBCPP_HIDE_FROM_ABI constexpr friend strong_ordering
-  operator<=>(__bounded_iter const& __x, __bounded_iter const& __y) noexcept
-    requires three_way_comparable<_Iterator, strong_ordering>
-  {
-    return __x.__current_ <=> __y.__current_;
+  operator<=>(__bounded_iter const& __x, __bounded_iter const& __y) noexcept {
+    if constexpr (three_way_comparable<_Iterator, strong_ordering>) {
+      return __x.__current_ <=> __y.__current_;
+    } else {
+      if (__x.__current_ < __y.__current_)
+        return strong_ordering::less;
+
+      if (__x.__current_ == __y.__current_)
+        return strong_ordering::equal;
+
+      return strong_ordering::greater;
+    }
   }
 #endif // _LIBCPP_STD_VER >= 20
 
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 8462b0c823700..2598ed4544345 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -133,6 +133,7 @@ operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXC
   return __x.base() < __y.base();
 }
 
+#if _LIBCPP_STD_VER <= 17
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
 operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
@@ -144,6 +145,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
 operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
   return !(__x == __y);
 }
+#endif
 
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
@@ -185,18 +187,34 @@ operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
 
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
-operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noexcept
-  requires three_way_comparable<_Iter1, strong_ordering>
-{
-  return __x.base() <=> __y.base();
+operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noexcept {
+  if constexpr (three_way_comparable<_Iter1, strong_ordering>) {
+    return __x.base() <=> __y.base();
+  } else {
+    if (__x.base() < __y.base())
+      return strong_ordering::less;
+
+    if (__x.base() == __y.base())
+      return strong_ordering::equal;
+
+    return strong_ordering::greater;
+  }
 }
 
 template <class _Iter1, class _Iter2>
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
-operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept
-  requires three_way_comparable_with<_Iter1, _Iter2, strong_ordering>
-{
-  return __x.base() <=> __y.base();
+operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept {
+  if constexpr (three_way_comparable_with<_Iter1, _Iter2, strong_ordering>) {
+    return __x.base() <=> __y.base();
+  } else {
+    if (__x.base() < __y.base())
+      return strong_ordering::less;
+
+    if (__x.base() == __y.base())
+      return strong_ordering::equal;
+
+    return strong_ordering::greater;
+  }
 }
 
 #endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 782a9db79c7ae..a5b3ee298fb51 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -376,9 +376,11 @@ public:
     return __x.__ptr_ == __y.__ptr_;
   }
 
+#if _LIBCPP_STD_VER <= 17
   _LIBCPP_HIDE_FROM_ABI friend bool operator!=(const __deque_iterator& __x, const __deque_iterator& __y) {
     return !(__x == __y);
   }
+#endif
 
   _LIBCPP_HIDE_FROM_ABI friend bool operator<(const __deque_iterator& __x, const __deque_iterator& __y) {
     return __x.__m_iter_ < __y.__m_iter_ || (__x.__m_iter_ == __y.__m_iter_ && __x.__ptr_ < __y.__ptr_);
@@ -398,14 +400,23 @@ public:
 
 #if _LIBCPP_STD_VER >= 20
   //  template <class _Tp = void>
-  _LIBCPP_HIDE_FROM_ABI friend strong_ordering operator<=>(const __deque_iterator& __x, const __deque_iterator& __y)
-    requires three_way_comparable<pointer, strong_ordering>
-  {
+  _LIBCPP_HIDE_FROM_ABI friend strong_ordering operator<=>(const __deque_iterator& __x, const __deque_iterator& __y) {
     if (__x.__m_iter_ < __y.__m_iter_)
       return strong_ordering::less;
 
-    if (__x.__m_iter_ == __y.__m_iter_)
-      return __x.__ptr_ <=> __y.__ptr_;
+    if (__x.__m_iter_ == __y.__m_iter_) {
+      if constexpr (three_way_comparable<pointer, strong_ordering>) {
+        return __x.__ptr_ <=> __y.__ptr_;
+      } else {
+        if (__x.__ptr_ < __y.__ptr_)
+          return strong_ordering::less;
+
+        if (__x.__ptr_ == __y.__ptr_)
+          return strong_ordering::equal;
+
+        return strong_ordering::greater;
+      }
+    }
 
     return strong_ordering::greater;
   }
diff --git a/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp b/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
index 396fd56dc6413..484a2961fdb0c 100644
--- a/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/iterators.pass.cpp
@@ -53,10 +53,14 @@ int main(int, char**)
 
 #  if TEST_STD_VER >= 20
     // P1614 + LWG3352
-    // When the allocator does not have operator<=> then neither does the iterator.
+    // When the allocator does not have operator<=> then the iterator uses a
+    // fallback to provide operator<=>.
     // Make sure to test with an allocator that does not have operator<=>.
     static_assert(!std::three_way_comparable<min_allocator<int>, std::strong_ordering>);
-    static_assert(!std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+    static_assert(std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+
+    std::same_as<std::strong_ordering> decltype(auto) r1 = i <=> j;
+    assert(r1 == std::strong_ordering::equal);
 #  endif
     }
 #endif
diff --git a/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp b/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
index 1d750cb1110f9..0aa7ad0d42ed7 100644
--- a/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/iterators.pass.cpp
@@ -99,10 +99,14 @@ TEST_CONSTEXPR_CXX20 bool tests()
 
 #  if TEST_STD_VER >= 20
         // P1614 + LWG3352
-        // When the allocator does not have operator<=> then neither does the iterator.
+        // When the allocator does not have operator<=> then the iterator uses a
+        // fallback to provide operator<=>.
         // Make sure to test with an allocator that does not have operator<=>.
         static_assert(!std::three_way_comparable<min_allocator<int>, std::strong_ordering>);
-        static_assert(!std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+        static_assert(std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+
+        std::same_as<std::strong_ordering> decltype(auto) r1 = i <=> j;
+        assert(r1 == std::strong_ordering::equal);
 #  endif
     }
     {
@@ -123,11 +127,14 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert((i >= j));
 
 #  if TEST_STD_VER >= 20
-        // P1614 + LWG3352
-        // When the allocator does not have operator<=> then neither does the iterator.
+        // When the allocator does not have operator<=> then the iterator uses a
+        // fallback to provide operator<=>.
         // Make sure to test with an allocator that does not have operator<=>.
         static_assert(!std::three_way_comparable<min_allocator<int>, std::strong_ordering>);
-        static_assert(!std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+        static_assert(std::three_way_comparable<typename C::iterator, std::strong_ordering>);
+
+        std::same_as<std::strong_ordering> decltype(auto) r1 = i <=> j;
+        assert(r1 == std::strong_ordering::equal);
 #  endif
     }
     {

>From 462924bfb5083ce7106fb0431f9daad380463f3f Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 23 Jul 2024 18:56:36 +0200
Subject: [PATCH 6/6] Addresses review comments.

---
 libcxx/include/__iterator/bounded_iter.h               |  1 +
 libcxx/include/__iterator/wrap_iter.h                  |  5 +++--
 libcxx/include/deque                                   |  2 +-
 .../libcxx/iterators/bounded_iter/comparison.pass.cpp  | 10 +++-------
 .../views/views.span/span.iterators/iterator.pass.cpp  |  9 ++++-----
 5 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__iterator/bounded_iter.h b/libcxx/include/__iterator/bounded_iter.h
index 78a4e46cd4281..76647dcaaf02c 100644
--- a/libcxx/include/__iterator/bounded_iter.h
+++ b/libcxx/include/__iterator/bounded_iter.h
@@ -211,6 +211,7 @@ struct __bounded_iter {
   }
 #endif
 
+  // TODO(mordante) disable these overloads in the LLVM 20 release.
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR friend bool
   operator<(__bounded_iter const& __x, __bounded_iter const& __y) _NOEXCEPT {
     return __x.__current_ < __y.__current_;
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 2598ed4544345..575eb82a2ce7f 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -147,6 +147,7 @@ operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
 }
 #endif
 
+// TODO(mordante) disable these overloads in the LLVM 20 release.
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
 operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
@@ -184,7 +185,7 @@ operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEX
 }
 
 #if _LIBCPP_STD_VER >= 20
-
+#  if 0
 template <class _Iter1>
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
 operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noexcept {
@@ -200,7 +201,7 @@ operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) noex
     return strong_ordering::greater;
   }
 }
-
+#  endif
 template <class _Iter1, class _Iter2>
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
 operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept {
diff --git a/libcxx/include/deque b/libcxx/include/deque
index a5b3ee298fb51..e73135a8647b9 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -382,6 +382,7 @@ public:
   }
 #endif
 
+  // TODO(mordante) disable these overloads in the LLVM 20 release.
   _LIBCPP_HIDE_FROM_ABI friend bool operator<(const __deque_iterator& __x, const __deque_iterator& __y) {
     return __x.__m_iter_ < __y.__m_iter_ || (__x.__m_iter_ == __y.__m_iter_ && __x.__ptr_ < __y.__ptr_);
   }
@@ -399,7 +400,6 @@ public:
   }
 
 #if _LIBCPP_STD_VER >= 20
-  //  template <class _Tp = void>
   _LIBCPP_HIDE_FROM_ABI friend strong_ordering operator<=>(const __deque_iterator& __x, const __deque_iterator& __y) {
     if (__x.__m_iter_ < __y.__m_iter_)
       return strong_ordering::less;
diff --git a/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp b/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
index 071a67fce9332..cef2157469c8f 100644
--- a/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
+++ b/libcxx/test/libcxx/iterators/bounded_iter/comparison.pass.cpp
@@ -61,13 +61,9 @@ TEST_CONSTEXPR_CXX14 bool tests() {
   }
 
 #if TEST_STD_VER >= 20
-  if constexpr (requires(std::__bounded_iter<Iter> const iter) {
-                  { iter <=> iter } -> std::same_as<std::strong_ordering>;
-                }) {
-    // P1614
-    std::same_as<std::strong_ordering> decltype(auto) r1 = iter1 <=> iter2;
-    assert(r1 == std::strong_ordering::less);
-  }
+  // P1614
+  std::same_as<std::strong_ordering> decltype(auto) r1 = iter1 <=> iter2;
+  assert(r1 == std::strong_ordering::less);
 #endif
 
   return true;
diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
index b097e4dc994a7..13a7628e6043d 100644
--- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp
@@ -22,14 +22,13 @@
 #include "test_macros.h"
 
 template <class T>
-TEST_CONSTEXPR void test_type() {
-#
-
+constexpr void test_type() {
   using C = std::span<T>;
   typename C::iterator ii1{}, ii2{};
   typename C::iterator ii4 = ii1;
   // TODO Test against C++23 after implementing
-  // P2278R4 cbegin should always return a constant iterator
+  //  P2278R4 cbegin should always return a constant iterator
+  // The means adjusting the #ifdef to guard against C++23.
 #ifdef __cpp_lib_ranges_as_const
   typename C::const_iterator cii{};
 #endif
@@ -77,7 +76,7 @@ TEST_CONSTEXPR void test_type() {
 #endif
 }
 
-TEST_CONSTEXPR bool test() {
+constexpr bool test() {
   test_type<char>();
   test_type<int>();
   test_type<std::string>();



More information about the libcxx-commits mailing list