[libcxx-commits] [libcxx] 2fb026e - Implement move_sentinel and C++20 move_iterator.

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Apr 13 06:51:54 PDT 2022


Author: Arthur O'Dwyer
Date: 2022-04-13T09:51:43-04:00
New Revision: 2fb026ee4d1a612f55d3f179b42b223d00ec8eda

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

LOG: Implement move_sentinel and C++20 move_iterator.

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

Added: 
    libcxx/include/__iterator/move_sentinel.h
    libcxx/test/libcxx/diagnostics/detail.headers/iterator/move_sentinel.module.verify.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_spaceship.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/constraints.compile.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp

Modified: 
    libcxx/docs/Status/Cxx2bIssues.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__iterator/move_iterator.h
    libcxx/include/iterator
    libcxx/include/module.modulemap
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_eq.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gt.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gte.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lt.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lte.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_neq.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/iter.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/iterator_concept_conformance.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index a9c67cf0714c1..7adfbf251ceb7 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -104,7 +104,7 @@
 `3123 <https://wg21.link/LWG3123>`__,"``duration`` constructor from representation shouldn't be effectively non-throwing","October 2021","","","|chrono|"
 `3146 <https://wg21.link/LWG3146>`__,"Excessive unwrapping in ``std::ref/cref``","October 2021","|Complete|","14.0"
 `3152 <https://wg21.link/LWG3152>`__,"``common_type`` and ``common_reference`` have flaws in common","October 2021","",""
-`3293 <https://wg21.link/LWG3293>`__,"``move_iterator operator+()`` has incorrect constraints","October 2021","","","|ranges|"
+`3293 <https://wg21.link/LWG3293>`__,"``move_iterator operator+()`` has incorrect constraints","October 2021","|Complete|","15.0","|ranges|"
 `3361 <https://wg21.link/LWG3361>`__,"``safe_range<SomeRange&>`` case","October 2021","|Nothing To Do|","","|ranges|"
 `3392 <https://wg21.link/LWG3392>`__,"``ranges::distance()`` cannot be used on a move-only iterator with a sized sentinel","October 2021","|Complete|","14.0","|ranges|"
 `3407 <https://wg21.link/LWG3407>`__,"Some problems with the wording changes of P1739R4","October 2021","","","|ranges|"

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 3b628406eac0f..a5e2f093f1173 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -270,6 +270,7 @@ set(files
   __iterator/iterator_traits.h
   __iterator/mergeable.h
   __iterator/move_iterator.h
+  __iterator/move_sentinel.h
   __iterator/next.h
   __iterator/ostream_iterator.h
   __iterator/ostreambuf_iterator.h

diff  --git a/libcxx/include/__iterator/move_iterator.h b/libcxx/include/__iterator/move_iterator.h
index e157b71a2f281..4776878f1f175 100644
--- a/libcxx/include/__iterator/move_iterator.h
+++ b/libcxx/include/__iterator/move_iterator.h
@@ -10,8 +10,20 @@
 #ifndef _LIBCPP___ITERATOR_MOVE_ITERATOR_H
 #define _LIBCPP___ITERATOR_MOVE_ITERATOR_H
 
+#include <__compare/compare_three_way_result.h>
+#include <__compare/three_way_comparable.h>
+#include <__concepts/assignable.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/same_as.h>
 #include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/move_sentinel.h>
+#include <__iterator/readable_traits.h>
 #include <__utility/move.h>
 #include <type_traits>
 
@@ -21,20 +33,48 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+template<class _Iter, class = void>
+struct __move_iter_category_base {};
+
+template<class _Iter>
+  requires requires { typename iterator_traits<_Iter>::iterator_category; }
+struct __move_iter_category_base<_Iter> {
+    using iterator_category = _If<
+        derived_from<typename iterator_traits<_Iter>::iterator_category, random_access_iterator_tag>,
+        random_access_iterator_tag,
+        typename iterator_traits<_Iter>::iterator_category
+    >;
+};
+
+template<class _Iter, class _Sent>
+concept __move_iter_comparable = requires {
+    { declval<const _Iter&>() == declval<_Sent>() } -> convertible_to<bool>;
+};
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
 template <class _Iter>
 class _LIBCPP_TEMPLATE_VIS move_iterator
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+    : public __move_iter_category_base<_Iter>
+#endif
 {
 public:
-#if _LIBCPP_STD_VER > 17
-    typedef input_iterator_tag iterator_concept;
-#endif
-
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+    using iterator_type = _Iter;
+    using iterator_concept = input_iterator_tag;
+    // iterator_category is inherited and not always present
+    using value_type = iter_value_t<_Iter>;
+    using 
diff erence_type = iter_
diff erence_t<_Iter>;
+    using pointer = _Iter;
+    using reference = iter_rvalue_reference_t<_Iter>;
+#else
     typedef _Iter iterator_type;
     typedef _If<
         __is_cpp17_random_access_iterator<_Iter>::value,
         random_access_iterator_tag,
         typename iterator_traits<_Iter>::iterator_category
-    >  iterator_category;
+    > iterator_category;
     typedef typename iterator_traits<iterator_type>::value_type value_type;
     typedef typename iterator_traits<iterator_type>::
diff erence_type 
diff erence_type;
     typedef iterator_type pointer;
@@ -50,11 +90,56 @@ class _LIBCPP_TEMPLATE_VIS move_iterator
     typedef typename iterator_traits<iterator_type>::reference reference;
 #endif
 
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    explicit move_iterator(_Iter __i) : __current_(std::move(__i)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    move_iterator() requires is_constructible_v<_Iter> : __current_() {}
+
+    template <class _Up>
+        requires (!_IsSame<_Up, _Iter>::value) && convertible_to<const _Up&, _Iter>
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    move_iterator(const move_iterator<_Up>& __u) : __current_(__u.base()) {}
+
+    template <class _Up>
+        requires (!_IsSame<_Up, _Iter>::value) &&
+                 convertible_to<const _Up&, _Iter> &&
+                 assignable_from<_Iter&, const _Up&>
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    move_iterator& operator=(const move_iterator<_Up>& __u) {
+        __current_ = __u.base();
+        return *this;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr const _Iter& base() const & noexcept { return __current_; }
+    _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() && { return std::move(__current_); }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    reference operator*() const { return ranges::iter_move(__current_); }
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    reference operator[](
diff erence_type __n) const { return ranges::iter_move(__current_ + __n); }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    move_iterator& operator++() { ++__current_; return *this; }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    auto operator++(int)
+        requires forward_iterator<_Iter>
+    {
+        move_iterator __tmp(*this); ++__current_; return __tmp;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    void operator++(int) { ++__current_; }
+#else
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
-    move_iterator() : __current_() {}
+    explicit move_iterator(_Iter __i) : __current_(std::move(__i)) {}
 
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
-    explicit move_iterator(_Iter __i) : __current_(_VSTD::move(__i)) {}
+    move_iterator() : __current_() {}
 
     template <class _Up, class = __enable_if_t<
         !is_same<_Up, _Iter>::value && is_convertible<const _Up&, _Iter>::value
@@ -87,6 +172,8 @@ class _LIBCPP_TEMPLATE_VIS move_iterator
     move_iterator& operator++() { ++__current_; return *this; }
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
     move_iterator operator++(int) { move_iterator __tmp(*this); ++__current_; return __tmp; }
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
     move_iterator& operator--() { --__current_; return *this; }
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
@@ -100,7 +187,48 @@ class _LIBCPP_TEMPLATE_VIS move_iterator
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
     move_iterator& operator-=(
diff erence_type __n) { __current_ -= __n; return *this; }
 
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+    template<sentinel_for<_Iter> _Sent>
+    friend _LIBCPP_HIDE_FROM_ABI constexpr
+    bool operator==(const move_iterator& __x, const move_sentinel<_Sent>& __y)
+        requires __move_iter_comparable<_Iter, _Sent>
+    {
+        return __x.base() == __y.base();
+    }
+
+    template<sized_sentinel_for<_Iter> _Sent>
+    friend _LIBCPP_HIDE_FROM_ABI constexpr
+    iter_
diff erence_t<_Iter> operator-(const move_sentinel<_Sent>& __x, const move_iterator& __y)
+    {
+        return __x.base() - __y.base();
+    }
+
+    template<sized_sentinel_for<_Iter> _Sent>
+    friend _LIBCPP_HIDE_FROM_ABI constexpr
+    iter_
diff erence_t<_Iter> operator-(const move_iterator& __x, const move_sentinel<_Sent>& __y)
+    {
+        return __x.base() - __y.base();
+    }
+
+    friend _LIBCPP_HIDE_FROM_ABI constexpr
+    iter_rvalue_reference_t<_Iter> iter_move(const move_iterator& __i)
+        noexcept(noexcept(ranges::iter_move(__i.__current_)))
+    {
+        return ranges::iter_move(__i.__current_);
+    }
+
+    template<indirectly_swappable<_Iter> _It2>
+    friend _LIBCPP_HIDE_FROM_ABI constexpr
+    void iter_swap(const move_iterator& __x, const move_iterator<_It2>& __y)
+        noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+    {
+        return ranges::iter_swap(__x.__current_, __y.__current_);
+    }
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
 private:
+    template<class _It2> friend class move_iterator;
+
     _Iter __current_;
 };
 
@@ -111,12 +239,14 @@ bool operator==(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& _
     return __x.base() == __y.base();
 }
 
+#if _LIBCPP_STD_VER <= 17
 template <class _Iter1, class _Iter2>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool operator!=(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
 {
     return __x.base() != __y.base();
 }
+#endif // _LIBCPP_STD_VER <= 17
 
 template <class _Iter1, class _Iter2>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
@@ -146,6 +276,20 @@ bool operator>=(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& _
     return __x.base() >= __y.base();
 }
 
+#if _LIBCPP_STD_VER > 17
+# ifdef _LIBCPP_HAS_NO_CONCEPTS
+template <class _Iter1, class _Iter2>
+# else
+template <class _Iter1, three_way_comparable_with<_Iter1> _Iter2>
+# endif // _LIBCPP_HAS_NO_CONCEPTS
+inline _LIBCPP_HIDE_FROM_ABI constexpr
+auto operator<=>(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
+    -> compare_three_way_result_t<_Iter1, _Iter2>
+{
+    return __x.base() <=> __y.base();
+}
+#endif // _LIBCPP_STD_VER > 17
+
 #ifndef _LIBCPP_CXX03_LANG
 template <class _Iter1, class _Iter2>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
@@ -164,6 +308,15 @@ operator-(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
 }
 #endif
 
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+template <class _Iter>
+inline _LIBCPP_HIDE_FROM_ABI constexpr
+move_iterator<_Iter> operator+(iter_
diff erence_t<_Iter> __n, const move_iterator<_Iter>& __x)
+    requires requires { { __x.base() + __n } -> same_as<_Iter>; }
+{
+    return __x + __n;
+}
+#else
 template <class _Iter>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
 move_iterator<_Iter>
@@ -171,13 +324,14 @@ operator+(typename move_iterator<_Iter>::
diff erence_type __n, const move_iterato
 {
     return move_iterator<_Iter>(__x.base() + __n);
 }
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
 
 template <class _Iter>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
 move_iterator<_Iter>
 make_move_iterator(_Iter __i)
 {
-    return move_iterator<_Iter>(_VSTD::move(__i));
+    return move_iterator<_Iter>(std::move(__i));
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__iterator/move_sentinel.h b/libcxx/include/__iterator/move_sentinel.h
new file mode 100644
index 0000000000000..fce56d62583ac
--- /dev/null
+++ b/libcxx/include/__iterator/move_sentinel.h
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ITERATOR_MOVE_SENTINEL_H
+#define _LIBCPP___ITERATOR_MOVE_SENTINEL_H
+
+#include <__concepts/assignable.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/semiregular.h>
+#include <__config>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
+template <semiregular _Sent>
+class _LIBCPP_TEMPLATE_VIS move_sentinel
+{
+public:
+  _LIBCPP_HIDE_FROM_ABI
+  move_sentinel() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  explicit move_sentinel(_Sent __s) : __last_(_VSTD::move(__s)) {}
+
+  template <class _S2>
+    requires convertible_to<const _S2&, _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  move_sentinel(const move_sentinel<_S2>& __s) : __last_(__s.base()) {}
+
+  template <class _S2>
+    requires assignable_from<_Sent&, const _S2&>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  move_sentinel& operator=(const move_sentinel<_S2>& __s)
+    { __last_ = __s.base(); return *this; }
+
+  constexpr _Sent base() const { return __last_; }
+
+private:
+    _Sent __last_ = _Sent();
+};
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ITERATOR_MOVE_SENTINEL_H

diff  --git a/libcxx/include/iterator b/libcxx/include/iterator
index 4060c2d9d4fd0..1f0390e83a59f 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -660,6 +660,7 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 #include <__iterator/iterator_traits.h>
 #include <__iterator/mergeable.h>
 #include <__iterator/move_iterator.h>
+#include <__iterator/move_sentinel.h>
 #include <__iterator/next.h>
 #include <__iterator/ostream_iterator.h>
 #include <__iterator/ostreambuf_iterator.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 6cfa65c5fd8a0..044809fd7b0ff 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -661,6 +661,7 @@ module std [system] {
       module iterator_traits       { private header "__iterator/iterator_traits.h" }
       module mergeable             { private header "__iterator/mergeable.h" }
       module move_iterator         { private header "__iterator/move_iterator.h" }
+      module move_sentinel         { private header "__iterator/move_sentinel.h" }
       module next                  { private header "__iterator/next.h" }
       module ostream_iterator      { private header "__iterator/ostream_iterator.h" }
       module ostreambuf_iterator   {

diff  --git a/libcxx/test/libcxx/diagnostics/detail.headers/iterator/move_sentinel.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/iterator/move_sentinel.module.verify.cpp
new file mode 100644
index 0000000000000..3c41e1a9db5c4
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/iterator/move_sentinel.module.verify.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__iterator/move_sentinel.h'}}
+#include <__iterator/move_sentinel.h>

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_eq.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_eq.pass.cpp
index 933dc68e61d78..b50299bad4add 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_eq.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_eq.pass.cpp
@@ -10,10 +10,8 @@
 
 // move_iterator
 
-// template <InputIterator Iter1, InputIterator Iter2>
-//   requires HasEqualTo<Iter1, Iter2>
-//   bool
-//   operator==(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
+// template <class Iter1, class Iter2>
+//   bool operator==(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
 //
 //  constexpr in C++17
 
@@ -23,40 +21,60 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
+// move_iterator's operator== calls the underlying iterator's operator==
+struct CustomIt {
+  using value_type = int;
+  using 
diff erence_type = int;
+  using reference = int&;
+  using pointer = int*;
+  using iterator_category = std::input_iterator_tag;
+  CustomIt() = default;
+  TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {}
+  int& operator*() const;
+  CustomIt& operator++();
+  CustomIt operator++(int);
+  TEST_CONSTEXPR_CXX17 friend bool operator==(const CustomIt& a, const CustomIt& b) { return a.p_ == b.p_; }
+  int *p_ = nullptr;
+};
+
 template <class It>
-void
-test(It l, It r, bool x)
+TEST_CONSTEXPR_CXX17 void test_one()
 {
-    const std::move_iterator<It> r1(l);
-    const std::move_iterator<It> r2(r);
-    assert((r1 == r2) == x);
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> r1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r2 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r3 = std::move_iterator<It>(It(a + 2));
+  ASSERT_SAME_TYPE(decltype(r1 == r2), bool);
+  assert( (r1 == r1));
+  assert( (r1 == r2));
+  assert( (r2 == r1));
+  assert(!(r1 == r3));
+  assert(!(r3 == r1));
 }
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    char s[] = "1234567890";
-    test(cpp17_input_iterator<char*>(s), cpp17_input_iterator<char*>(s), true);
-    test(cpp17_input_iterator<char*>(s), cpp17_input_iterator<char*>(s+1), false);
-    test(forward_iterator<char*>(s), forward_iterator<char*>(s), true);
-    test(forward_iterator<char*>(s), forward_iterator<char*>(s+1), false);
-    test(bidirectional_iterator<char*>(s), bidirectional_iterator<char*>(s), true);
-    test(bidirectional_iterator<char*>(s), bidirectional_iterator<char*>(s+1), false);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s), true);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s+1), false);
-    test(s, s, true);
-    test(s, s+1, false);
+  test_one<CustomIt>();
+  test_one<cpp17_input_iterator<int*> >();
+  test_one<forward_iterator<int*> >();
+  test_one<bidirectional_iterator<int*> >();
+  test_one<random_access_iterator<int*> >();
+  test_one<int*>();
+  test_one<const int*>();
+
+#if TEST_STD_VER > 17
+  test_one<contiguous_iterator<int*>>();
+  test_one<three_way_contiguous_iterator<int*>>();
+#endif
 
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p + 5);
-    constexpr MI it3 = std::make_move_iterator(p);
-    static_assert(!(it1 == it2), "");
-    static_assert( (it1 == it3), "");
-    static_assert(!(it2 == it3), "");
-    }
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gt.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gt.pass.cpp
index 3da38b3766641..7e599fe299028 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gt.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gt.pass.cpp
@@ -10,10 +10,8 @@
 
 // move_iterator
 
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-//   requires HasLess<Iter2, Iter1>
-//   bool
-//   operator>(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
+// template <class Iter1, class Iter2>
+//   bool operator>(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
 //
 //  constexpr in C++17
 
@@ -23,36 +21,51 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
+// move_iterator's operator> calls the underlying iterator's operator>
+struct CustomIt {
+  using value_type = int;
+  using 
diff erence_type = int;
+  using reference = int&;
+  using pointer = int*;
+  using iterator_category = std::input_iterator_tag;
+  CustomIt() = default;
+  TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {}
+  int& operator*() const;
+  CustomIt& operator++();
+  CustomIt operator++(int);
+  TEST_CONSTEXPR_CXX17 friend bool operator>(const CustomIt& a, const CustomIt& b) { return a.p_ > b.p_; }
+  int *p_ = nullptr;
+};
+
 template <class It>
-void
-test(It l, It r, bool x)
+TEST_CONSTEXPR_CXX17 void test_one()
 {
-    const std::move_iterator<It> r1(l);
-    const std::move_iterator<It> r2(r);
-    assert((r1 > r2) == x);
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> r1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r2 = std::move_iterator<It>(It(a+2));
+  ASSERT_SAME_TYPE(decltype(r1 > r2), bool);
+  assert(!(r1 > r1));
+  assert(!(r1 > r2));
+  assert( (r2 > r1));
 }
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    char s[] = "1234567890";
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s), false);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s+1), false);
-    test(random_access_iterator<char*>(s+1), random_access_iterator<char*>(s), true);
-    test(s, s, false);
-    test(s, s+1, false);
-    test(s+1, s, true);
+  test_one<CustomIt>();
+  test_one<int*>();
+  test_one<const int*>();
+  test_one<random_access_iterator<int*> >();
+#if TEST_STD_VER > 17
+  test_one<contiguous_iterator<int*>>();
+#endif
+  return true;
+}
 
+int main(int, char**)
+{
+  assert(test());
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p + 5);
-    constexpr MI it3 = std::make_move_iterator(p);
-    static_assert(!(it1 > it2), "");
-    static_assert(!(it1 > it3), "");
-    static_assert( (it2 > it3), "");
-    }
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gte.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gte.pass.cpp
index dceb411515252..04e081da217c7 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gte.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_gte.pass.cpp
@@ -10,10 +10,8 @@
 
 // move_iterator
 
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-//   requires HasLess<Iter1, Iter2>
-//   bool
-//   operator>=(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
+// template <class Iter1, class Iter2>
+//   bool operator>=(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
 //
 //  constexpr in C++17
 
@@ -23,36 +21,51 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
+// move_iterator's operator>= calls the underlying iterator's operator>=
+struct CustomIt {
+  using value_type = int;
+  using 
diff erence_type = int;
+  using reference = int&;
+  using pointer = int*;
+  using iterator_category = std::input_iterator_tag;
+  CustomIt() = default;
+  TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {}
+  int& operator*() const;
+  CustomIt& operator++();
+  CustomIt operator++(int);
+  TEST_CONSTEXPR_CXX17 friend bool operator>=(const CustomIt& a, const CustomIt& b) { return a.p_ >= b.p_; }
+  int *p_ = nullptr;
+};
+
 template <class It>
-void
-test(It l, It r, bool x)
+TEST_CONSTEXPR_CXX17 void test_one()
 {
-    const std::move_iterator<It> r1(l);
-    const std::move_iterator<It> r2(r);
-    assert((r1 >= r2) == x);
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> r1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r2 = std::move_iterator<It>(It(a+2));
+  ASSERT_SAME_TYPE(decltype(r1 >= r2), bool);
+  assert( (r1 >= r1));
+  assert(!(r1 >= r2));
+  assert( (r2 >= r1));
 }
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    char s[] = "1234567890";
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s), true);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s+1), false);
-    test(random_access_iterator<char*>(s+1), random_access_iterator<char*>(s), true);
-    test(s, s, true);
-    test(s, s+1, false);
-    test(s+1, s, true);
+  test_one<CustomIt>();
+  test_one<int*>();
+  test_one<const int*>();
+  test_one<random_access_iterator<int*> >();
+#if TEST_STD_VER > 17
+  test_one<contiguous_iterator<int*>>();
+#endif
+  return true;
+}
 
+int main(int, char**)
+{
+  assert(test());
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p + 5);
-    constexpr MI it3 = std::make_move_iterator(p);
-    static_assert(!(it1 >= it2), "");
-    static_assert( (it1 >= it3), "");
-    static_assert( (it2 >= it3), "");
-    }
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lt.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lt.pass.cpp
index 675e55f7881db..24ccfa63d0af2 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lt.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lt.pass.cpp
@@ -10,10 +10,8 @@
 
 // move_iterator
 
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-//   requires HasLess<Iter1, Iter2>
-//   bool
-//   operator<(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
+// template <class Iter1, class Iter2>
+//   bool operator<(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
 //
 //  constexpr in C++17
 
@@ -23,36 +21,51 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
+// move_iterator's operator< calls the underlying iterator's operator<
+struct CustomIt {
+  using value_type = int;
+  using 
diff erence_type = int;
+  using reference = int&;
+  using pointer = int*;
+  using iterator_category = std::input_iterator_tag;
+  CustomIt() = default;
+  TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {}
+  int& operator*() const;
+  CustomIt& operator++();
+  CustomIt operator++(int);
+  TEST_CONSTEXPR_CXX17 friend bool operator<(const CustomIt& a, const CustomIt& b) { return a.p_ < b.p_; }
+  int *p_ = nullptr;
+};
+
 template <class It>
-void
-test(It l, It r, bool x)
+TEST_CONSTEXPR_CXX17 void test_one()
 {
-    const std::move_iterator<It> r1(l);
-    const std::move_iterator<It> r2(r);
-    assert((r1 < r2) == x);
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> r1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r2 = std::move_iterator<It>(It(a + 2));
+  ASSERT_SAME_TYPE(decltype(r1 < r2), bool);
+  assert(!(r1 < r1));
+  assert( (r1 < r2));
+  assert(!(r2 < r1));
 }
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    char s[] = "1234567890";
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s), false);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s+1), true);
-    test(random_access_iterator<char*>(s+1), random_access_iterator<char*>(s), false);
-    test(s, s, false);
-    test(s, s+1, true);
-    test(s+1, s, false);
+  test_one<CustomIt>();
+  test_one<int*>();
+  test_one<const int*>();
+  test_one<random_access_iterator<int*> >();
+#if TEST_STD_VER > 17
+  test_one<contiguous_iterator<int*>>();
+#endif
+  return true;
+}
 
+int main(int, char**)
+{
+  assert(test());
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p + 5);
-    constexpr MI it3 = std::make_move_iterator(p);
-    static_assert( (it1 < it2), "");
-    static_assert(!(it1 < it3), "");
-    static_assert(!(it2 < it3), "");
-    }
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lte.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lte.pass.cpp
index a2ac0b7a6f7bb..6de8dae8aa96b 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lte.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_lte.pass.cpp
@@ -10,10 +10,8 @@
 
 // move_iterator
 
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-//   requires HasLess<Iter2, Iter1>
-//   bool
-//   operator<=(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
+// template <class Iter1, class Iter2>
+//   bool operator<=(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y);
 //
 //  constexpr in C++17
 
@@ -23,36 +21,51 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
+// move_iterator's operator<= calls the underlying iterator's operator<=
+struct CustomIt {
+  using value_type = int;
+  using 
diff erence_type = int;
+  using reference = int&;
+  using pointer = int*;
+  using iterator_category = std::input_iterator_tag;
+  CustomIt() = default;
+  TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {}
+  int& operator*() const;
+  CustomIt& operator++();
+  CustomIt operator++(int);
+  TEST_CONSTEXPR_CXX17 friend bool operator<=(const CustomIt& a, const CustomIt& b) { return a.p_ <= b.p_; }
+  int *p_ = nullptr;
+};
+
 template <class It>
-void
-test(It l, It r, bool x)
+TEST_CONSTEXPR_CXX17 void test_one()
 {
-    const std::move_iterator<It> r1(l);
-    const std::move_iterator<It> r2(r);
-    assert((r1 <= r2) == x);
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> r1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r2 = std::move_iterator<It>(It(a + 2));
+  ASSERT_SAME_TYPE(decltype(r1 <= r2), bool);
+  assert( (r1 <= r1));
+  assert( (r1 <= r2));
+  assert(!(r2 <= r1));
 }
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    char s[] = "1234567890";
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s), true);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s+1), true);
-    test(random_access_iterator<char*>(s+1), random_access_iterator<char*>(s), false);
-    test(s, s, true);
-    test(s, s+1, true);
-    test(s+1, s, false);
+  test_one<CustomIt>();
+  test_one<int*>();
+  test_one<const int*>();
+  test_one<random_access_iterator<int*> >();
+#if TEST_STD_VER > 17
+  test_one<contiguous_iterator<int*>>();
+#endif
+  return true;
+}
 
+int main(int, char**)
+{
+  assert(test());
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p + 5);
-    constexpr MI it3 = std::make_move_iterator(p);
-    static_assert( (it1 <= it2), "");
-    static_assert( (it1 <= it3), "");
-    static_assert(!(it2 <= it3), "");
-    }
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_neq.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_neq.pass.cpp
index 808ea7b8d267b..51982adeeb6bd 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_neq.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_neq.pass.cpp
@@ -23,40 +23,66 @@
 #include "test_macros.h"
 #include "test_iterators.h"
 
+// In C++17, move_iterator's operator!= calls the underlying iterator's operator!=
+// In C++20, move_iterator's operator== calls the underlying iterator's operator==
+struct CustomIt {
+  using value_type = int;
+  using 
diff erence_type = int;
+  using reference = int&;
+  using pointer = int*;
+  using iterator_category = std::input_iterator_tag;
+  CustomIt() = default;
+  TEST_CONSTEXPR_CXX17 explicit CustomIt(int* p) : p_(p) {}
+  int& operator*() const;
+  CustomIt& operator++();
+  CustomIt operator++(int);
+#if TEST_STD_VER > 17
+  friend constexpr bool operator==(const CustomIt& a, const CustomIt& b) { return a.p_ == b.p_; }
+  friend bool operator!=(const CustomIt& a, const CustomIt& b) = delete;
+#else
+  friend TEST_CONSTEXPR_CXX17 bool operator!=(const CustomIt& a, const CustomIt& b) { return a.p_ != b.p_; }
+#endif
+  int *p_ = nullptr;
+};
+
 template <class It>
-void
-test(It l, It r, bool x)
+TEST_CONSTEXPR_CXX17 void test_one()
 {
-    const std::move_iterator<It> r1(l);
-    const std::move_iterator<It> r2(r);
-    assert((r1 != r2) == x);
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> r1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r2 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> r3 = std::move_iterator<It>(It(a + 2));
+  ASSERT_SAME_TYPE(decltype(r1 != r2), bool);
+  assert(!(r1 != r1));
+  assert(!(r1 != r2));
+  assert(!(r2 != r1));
+  assert( (r1 != r3));
+  assert( (r3 != r1));
 }
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    char s[] = "1234567890";
-    test(cpp17_input_iterator<char*>(s), cpp17_input_iterator<char*>(s), false);
-    test(cpp17_input_iterator<char*>(s), cpp17_input_iterator<char*>(s+1), true);
-    test(forward_iterator<char*>(s), forward_iterator<char*>(s), false);
-    test(forward_iterator<char*>(s), forward_iterator<char*>(s+1), true);
-    test(bidirectional_iterator<char*>(s), bidirectional_iterator<char*>(s), false);
-    test(bidirectional_iterator<char*>(s), bidirectional_iterator<char*>(s+1), true);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s), false);
-    test(random_access_iterator<char*>(s), random_access_iterator<char*>(s+1), true);
-    test(s, s, false);
-    test(s, s+1, true);
+  test_one<CustomIt>();
+  test_one<cpp17_input_iterator<int*> >();
+  test_one<forward_iterator<int*> >();
+  test_one<bidirectional_iterator<int*> >();
+  test_one<random_access_iterator<int*> >();
+  test_one<int*>();
+  test_one<const int*>();
+
+#if TEST_STD_VER > 17
+  test_one<contiguous_iterator<int*>>();
+  test_one<three_way_contiguous_iterator<int*>>();
+#endif
 
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p + 5);
-    constexpr MI it3 = std::make_move_iterator(p);
-    static_assert( (it1 != it2), "");
-    static_assert(!(it1 != it3), "");
-    static_assert( (it2 != it3), "");
-    }
+  static_assert(test());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_spaceship.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_spaceship.pass.cpp
new file mode 100644
index 0000000000000..dc9238a529fb0
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_spaceship.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-concepts
+
+// <iterator>
+
+// move_iterator
+
+// template <class Iter1, three_way_comparable_with<Iter1> Iter2>
+//   constexpr auto operator<=>(const move_iterator<Iter1>& x, const move_iterator<Iter2>& y)
+//     -> compare_three_way_result_t<Iter1, Iter2>;
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+
+template<class T, class U> concept HasEquals = requires (T t, U u) { t == u; };
+template<class T, class U> concept HasSpaceship = requires (T t, U u) { t <=> u; };
+
+static_assert(!HasSpaceship<std::move_iterator<int*>, std::move_iterator<char*>>);
+static_assert( HasSpaceship<std::move_iterator<int*>, std::move_iterator<int*>>);
+static_assert( HasSpaceship<std::move_iterator<int*>, std::move_iterator<const int*>>);
+static_assert( HasSpaceship<std::move_iterator<const int*>, std::move_iterator<const int*>>);
+static_assert(!HasSpaceship<std::move_iterator<forward_iterator<int*>>, std::move_iterator<forward_iterator<int*>>>);
+static_assert(!HasSpaceship<std::move_iterator<random_access_iterator<int*>>, std::move_iterator<random_access_iterator<int*>>>);
+static_assert(!HasSpaceship<std::move_iterator<contiguous_iterator<int*>>, std::move_iterator<contiguous_iterator<int*>>>);
+static_assert( HasSpaceship<std::move_iterator<three_way_contiguous_iterator<int*>>, std::move_iterator<three_way_contiguous_iterator<int*>>>);
+
+static_assert(!HasSpaceship<std::move_iterator<int*>, std::move_sentinel<int*>>);
+static_assert(!HasSpaceship<std::move_iterator<three_way_contiguous_iterator<int*>>, std::move_sentinel<three_way_contiguous_iterator<int*>>>);
+
+void test_spaceshippable_but_not_three_way_comparable() {
+  struct A {
+    using value_type = int;
+    using 
diff erence_type = int;
+    int& operator*() const;
+    A& operator++();
+    A operator++(int);
+    std::strong_ordering operator<=>(const A&) const;
+  };
+  struct B {
+    using value_type = int;
+    using 
diff erence_type = int;
+    int& operator*() const;
+    B& operator++();
+    B operator++(int);
+    std::strong_ordering operator<=>(const B&) const;
+    bool operator==(const A&) const;
+    std::strong_ordering operator<=>(const A&) const;
+  };
+  static_assert( std::input_iterator<A>);
+  static_assert( std::input_iterator<B>);
+  static_assert( HasEquals<A, B>);
+  static_assert( HasSpaceship<A, B>);
+  static_assert(!std::three_way_comparable_with<A, B>);
+  static_assert( HasEquals<std::move_iterator<A>, std::move_iterator<B>>);
+  static_assert(!HasSpaceship<std::move_iterator<A>, std::move_iterator<B>>);
+}
+
+template <class It, class Jt>
+constexpr void test_two()
+{
+  int a[] = {3, 1, 4};
+  const std::move_iterator<It> i1 = std::move_iterator<It>(It(a));
+  const std::move_iterator<It> i2 = std::move_iterator<It>(It(a + 2));
+  const std::move_iterator<Jt> j1 = std::move_iterator<Jt>(Jt(a));
+  const std::move_iterator<Jt> j2 = std::move_iterator<Jt>(Jt(a + 2));
+  ASSERT_SAME_TYPE(decltype(i1 <=> i2), std::strong_ordering);
+  assert((i1 <=> i1) == std::strong_ordering::equal);
+  assert((i1 <=> i2) == std::strong_ordering::less);
+  assert((i2 <=> i1) == std::strong_ordering::greater);
+  ASSERT_SAME_TYPE(decltype(i1 <=> j2), std::strong_ordering);
+  assert((i1 <=> j1) == std::strong_ordering::equal);
+  assert((i1 <=> j2) == std::strong_ordering::less);
+  assert((i2 <=> j1) == std::strong_ordering::greater);
+}
+
+constexpr bool test()
+{
+  test_two<int*, int*>();
+  test_two<int*, const int*>();
+  test_two<const int*, int*>();
+  test_two<const int*, const int*>();
+  test_two<three_way_contiguous_iterator<int*>, three_way_contiguous_iterator<int*>>();
+  return true;
+}
+
+int main(int, char**)
+{
+  assert(test());
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/iter.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/iter.pass.cpp
index 64c1a43bd6e0e..f7830f73e5248 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/iter.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.const/iter.pass.cpp
@@ -16,33 +16,73 @@
 
 #include <iterator>
 #include <cassert>
+#include <utility>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 template <class It>
-void
-test(It i)
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    std::move_iterator<It> r(i);
-    assert(r.base() == i);
+  static_assert( std::is_constructible<std::move_iterator<It>, const It&>::value, "");
+  static_assert( std::is_constructible<std::move_iterator<It>, It&&>::value, "");
+  static_assert(!std::is_convertible<const It&, std::move_iterator<It> >::value, "");
+  static_assert(!std::is_convertible<It&&, std::move_iterator<It> >::value, "");
+
+  char s[] = "123";
+  {
+    It it = It(s);
+    std::move_iterator<It> r(it);
+    assert(base(r.base()) == s);
+  }
+  {
+    It it = It(s);
+    std::move_iterator<It> r(std::move(it));
+    assert(base(r.base()) == s);
+  }
+  return true;
+}
+
+template <class It>
+TEST_CONSTEXPR_CXX17 bool test_moveonly()
+{
+  static_assert(!std::is_constructible<std::move_iterator<It>, const It&>::value, "");
+  static_assert( std::is_constructible<std::move_iterator<It>, It&&>::value, "");
+  static_assert(!std::is_convertible<const It&, std::move_iterator<It> >::value, "");
+  static_assert(!std::is_convertible<It&&, std::move_iterator<It> >::value, "");
+
+  char s[] = "123";
+  {
+    It it = It(s);
+    std::move_iterator<It> r(std::move(it));
+    assert(base(r.base()) == s);
+  }
+  return true;
 }
 
 int main(int, char**)
 {
-    char s[] = "123";
-    test(cpp17_input_iterator<char*>(s));
-    test(forward_iterator<char*>(s));
-    test(bidirectional_iterator<char*>(s));
-    test(random_access_iterator<char*>(s));
-    test(s);
+  test<cpp17_input_iterator<char*> >();
+  test<forward_iterator<char*> >();
+  test<bidirectional_iterator<char*> >();
+  test<random_access_iterator<char*> >();
+  test<char*>();
+  test<const char*>();
 
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    constexpr std::move_iterator<const char *> it(p);
-    static_assert(it.base() == p);
-    }
+  static_assert(test<cpp17_input_iterator<char*>>());
+  static_assert(test<forward_iterator<char*>>());
+  static_assert(test<bidirectional_iterator<char*>>());
+  static_assert(test<random_access_iterator<char*>>());
+  static_assert(test<char*>());
+  static_assert(test<const char*>());
+#endif
+
+#if TEST_STD_VER > 17
+  test<contiguous_iterator<char*>>();
+  test_moveonly<cpp20_input_iterator<char*>>();
+  static_assert(test<contiguous_iterator<char*>>());
+  static_assert(test_moveonly<cpp20_input_iterator<char*>>());
 #endif
 
   return 0;

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
index 6024f99fec3f1..8be60bc349dfc 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
@@ -13,35 +13,41 @@
 // pointer operator->() const;
 //
 //  constexpr in C++17
+//  removed in C++20
 
 #include <iterator>
 #include <cassert>
 
 #include "test_macros.h"
 
-template <class It>
-void
-test(It i)
+#if TEST_STD_VER > 17
+template <class T>
+concept HasArrow = requires (T t) {
+    t.operator->();
+};
+static_assert(!HasArrow<std::move_iterator<int*>>);
+static_assert(!HasArrow<std::move_iterator<int*>&>);
+static_assert(!HasArrow<std::move_iterator<int*>&&>);
+#endif // TEST_STD_VER > 17
+
+TEST_CONSTEXPR_CXX17 bool test()
 {
-    std::move_iterator<It> r(i);
-    assert(r.operator->() == i);
+#if TEST_STD_VER <= 17
+    char a[] = "123456789";
+    std::move_iterator<char *> it1 = std::make_move_iterator(a);
+    std::move_iterator<char *> it2 = std::make_move_iterator(a + 1);
+    assert(it1.operator->() == a);
+    assert(it2.operator->() == a + 1);
+#endif
+    return true;
 }
 
 int main(int, char**)
 {
-    char s[] = "123";
-    test(s);
-
+    test();
 #if TEST_STD_VER > 14
-    {
-    constexpr const char *p = "123456789";
-    typedef std::move_iterator<const char *> MI;
-    constexpr MI it1 = std::make_move_iterator(p);
-    constexpr MI it2 = std::make_move_iterator(p+1);
-    static_assert(it1.operator->() == p, "");
-    static_assert(it2.operator->() == p + 1, "");
-    }
+    static_assert(test());
 #endif
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/iterator_concept_conformance.compile.pass.cpp
index 2df020c021ab4..844da40b8efcb 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/iterator_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/iterator_concept_conformance.compile.pass.cpp
@@ -12,16 +12,128 @@
 
 #include <iterator>
 
-using iterator = std::move_iterator<int*>;
-
-static_assert(std::input_iterator<iterator>);
-static_assert(!std::forward_iterator<iterator>);
-static_assert(!std::indirectly_writable<iterator, int>);
-static_assert(std::incrementable<iterator>);
-static_assert(std::sentinel_for<iterator, iterator>);
-static_assert(std::sized_sentinel_for<iterator, iterator>);
-static_assert(!std::indirectly_movable<int*, iterator>);
-static_assert(!std::indirectly_movable_storable<int*, iterator>);
-static_assert(!std::indirectly_copyable<int*, iterator>);
-static_assert(!std::indirectly_copyable_storable<int*, iterator>);
-static_assert(!std::indirectly_swappable<iterator, iterator>);
+#include "test_iterators.h"
+#include "test_macros.h"
+
+void test()
+{
+  {
+    using iterator = std::move_iterator<cpp17_input_iterator<int*>>;
+
+    LIBCPP_STATIC_ASSERT(!std::default_initializable<iterator>);
+    static_assert( std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert(!std::sentinel_for<iterator, iterator>); // not copyable
+    static_assert(!std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+  {
+    using iterator = std::move_iterator<cpp20_input_iterator<int*>>;
+
+    LIBCPP_STATIC_ASSERT(!std::default_initializable<iterator>);
+    static_assert(!std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert(!std::sentinel_for<iterator, iterator>); // not copyable
+    static_assert(!std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+  {
+    using iterator = std::move_iterator<forward_iterator<int*>>;
+
+    static_assert( std::default_initializable<iterator>);
+    static_assert( std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert( std::sentinel_for<iterator, iterator>);
+    static_assert(!std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+  {
+    using iterator = std::move_iterator<bidirectional_iterator<int*>>;
+
+    static_assert( std::default_initializable<iterator>);
+    static_assert( std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert( std::sentinel_for<iterator, iterator>);
+    static_assert(!std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+  {
+    using iterator = std::move_iterator<random_access_iterator<int*>>;
+
+    static_assert( std::default_initializable<iterator>);
+    static_assert( std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert( std::sentinel_for<iterator, iterator>);
+    static_assert( std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+  {
+    using iterator = std::move_iterator<contiguous_iterator<int*>>;
+
+    static_assert( std::default_initializable<iterator>);
+    static_assert( std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert( std::sentinel_for<iterator, iterator>);
+    static_assert( std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+  {
+    using iterator = std::move_iterator<int*>;
+
+    static_assert( std::default_initializable<iterator>);
+    static_assert( std::copyable<iterator>);
+    static_assert( std::input_iterator<iterator>);
+    static_assert(!std::forward_iterator<iterator>);
+    static_assert( std::sentinel_for<iterator, iterator>);
+    static_assert( std::sized_sentinel_for<iterator, iterator>);
+    static_assert(!std::indirectly_movable<int*, iterator>);
+    static_assert(!std::indirectly_movable_storable<int*, iterator>);
+    static_assert(!std::indirectly_copyable<int*, iterator>);
+    static_assert(!std::indirectly_copyable_storable<int*, iterator>);
+    static_assert( std::indirectly_readable<iterator>);
+    static_assert(!std::indirectly_writable<iterator, int>);
+    static_assert( std::indirectly_swappable<iterator, iterator>);
+  }
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp
new file mode 100644
index 0000000000000..cc033f660d395
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/assign.converting.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <iterator>
+
+// move_sentinel
+
+// template<class S2>
+//   requires assignable_from<S&, const S2&>
+//     constexpr move_sentinel& operator=(const move_sentinel<S2>& s);
+
+#include <iterator>
+#include <cassert>
+#include <concepts>
+
+struct NonAssignable {
+    NonAssignable& operator=(int i);
+};
+static_assert(std::semiregular<NonAssignable>);
+static_assert(std::is_assignable_v<NonAssignable, int>);
+static_assert(!std::assignable_from<NonAssignable, int>);
+
+constexpr bool test()
+{
+  {
+    std::move_sentinel<int> m(42);
+    std::move_sentinel<long> m2;
+    m2 = m;
+    assert(m2.base() == 42L);
+  }
+  {
+    std::move_sentinel<long> m2;
+    m2 = std::move_sentinel<int>(43);
+    assert(m2.base() == 43L);
+  }
+  {
+    static_assert( std::is_assignable_v<std::move_sentinel<int>, std::move_sentinel<long>>);
+    static_assert(!std::is_assignable_v<std::move_sentinel<int*>, std::move_sentinel<const int*>>);
+    static_assert( std::is_assignable_v<std::move_sentinel<const int*>, std::move_sentinel<int*>>);
+    static_assert(!std::is_assignable_v<std::move_sentinel<NonAssignable>, std::move_sentinel<int>>);
+  }
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp
new file mode 100644
index 0000000000000..2b8f17d4eebfd
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/base.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <iterator>
+
+// move_sentinel
+
+// constexpr S base() const;
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+
+constexpr bool test()
+{
+  {
+    auto m = std::move_sentinel<int>(42);
+    const auto& cm = m;
+    assert(m.base() == 42);
+    assert(cm.base() == 42);
+    assert(std::move(m).base() == 42);
+    assert(std::move(cm).base() == 42);
+    ASSERT_SAME_TYPE(decltype(m.base()), int);
+    ASSERT_SAME_TYPE(decltype(cm.base()), int);
+    ASSERT_SAME_TYPE(decltype(std::move(m).base()), int);
+    ASSERT_SAME_TYPE(decltype(std::move(cm).base()), int);
+  }
+  {
+    int a[] = {1, 2, 3};
+    auto m = std::move_sentinel<const int*>(a);
+    const auto& cm = m;
+    assert(m.base() == a);
+    assert(cm.base() == a);
+    assert(std::move(m).base() == a);
+    assert(std::move(cm).base() == a);
+    ASSERT_SAME_TYPE(decltype(m.base()), const int*);
+    ASSERT_SAME_TYPE(decltype(cm.base()), const int*);
+    ASSERT_SAME_TYPE(decltype(std::move(m).base()), const int*);
+    ASSERT_SAME_TYPE(decltype(std::move(cm).base()), const int*);
+  }
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp
new file mode 100644
index 0000000000000..6d4e6abf96842
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/concept_conformance.compile.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+// UNSUPPORTED: libcpp-no-incomplete-ranges
+
+// <iterator>
+
+// template<semiregular S>
+//   class move_sentinel;
+
+#include <iterator>
+
+#include "test_iterators.h"
+
+void test()
+{
+  {
+    using It = int*;
+    static_assert( std::sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = cpp17_input_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = cpp20_input_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = forward_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = bidirectional_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = random_access_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = contiguous_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+  {
+    using It = three_way_contiguous_iterator<int*>;
+    static_assert( std::sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<It>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert(!std::sized_sentinel_for<std::move_sentinel<sentinel_wrapper<It>>, std::move_iterator<It>>);
+    static_assert( std::sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+    static_assert( std::sized_sentinel_for<std::move_sentinel<sized_sentinel<It>>, std::move_iterator<It>>);
+  }
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/constraints.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/constraints.compile.pass.cpp
new file mode 100644
index 0000000000000..a07cf470cf2c5
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/constraints.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <iterator>
+
+// template<semiregular S>
+//   class move_sentinel;
+
+#include <iterator>
+
+template<class T>
+concept HasMoveSentinel = requires {
+  typename std::move_sentinel<T>;
+};
+
+struct Semiregular {};
+
+struct NotSemiregular {
+  NotSemiregular(int);
+};
+
+static_assert( HasMoveSentinel<int>);
+static_assert( HasMoveSentinel<int*>);
+static_assert( HasMoveSentinel<Semiregular>);
+static_assert(!HasMoveSentinel<NotSemiregular>);

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp
new file mode 100644
index 0000000000000..ffd88be561be6
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.converting.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <iterator>
+
+// move_sentinel
+
+// template<class S2>
+//    requires convertible_to<const S2&, S>
+//      constexpr move_sentinel(const move_sentinel<S2>& s);
+
+#include <iterator>
+#include <cassert>
+#include <concepts>
+
+struct NonConvertible {
+    explicit NonConvertible();
+    NonConvertible(int i);
+    explicit NonConvertible(long i) = delete;
+};
+static_assert(std::semiregular<NonConvertible>);
+static_assert(std::is_convertible_v<long, NonConvertible>);
+static_assert(!std::convertible_to<long, NonConvertible>);
+
+constexpr bool test()
+{
+  {
+    std::move_sentinel<int> m(42);
+    std::move_sentinel<long> m2 = m;
+    assert(m2.base() == 42L);
+  }
+  {
+    std::move_sentinel<long> m2 = std::move_sentinel<int>(43);
+    assert(m2.base() == 43L);
+  }
+  {
+    static_assert( std::is_convertible_v<std::move_sentinel<int>, std::move_sentinel<long>>);
+    static_assert( std::is_convertible_v<std::move_sentinel<int*>, std::move_sentinel<const int*>>);
+    static_assert(!std::is_convertible_v<std::move_sentinel<const int*>, std::move_sentinel<int*>>);
+    static_assert( std::is_convertible_v<std::move_sentinel<int>, std::move_sentinel<NonConvertible>>);
+    static_assert(!std::is_convertible_v<std::move_sentinel<long>, std::move_sentinel<NonConvertible>>);
+  }
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..82e91e9ac1d0e
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.default.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <iterator>
+
+// move_sentinel
+
+// constexpr move_sentinel();
+
+#include <iterator>
+#include <cassert>
+
+constexpr bool test()
+{
+  {
+    std::move_sentinel<int> m;
+    assert(m.base() == 0);
+  }
+  {
+    std::move_sentinel<int*> m;
+    assert(m.base() == nullptr);
+  }
+  {
+    struct S {
+      explicit S() = default;
+      int i = 3;
+    };
+    std::move_sentinel<S> m;
+    assert(m.base().i == 3);
+  }
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp
new file mode 100644
index 0000000000000..ad1c3d82cef85
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/ctor.sentinel.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// <iterator>
+
+// move_sentinel
+
+// constexpr explicit move_sentinel(S s);
+
+#include <iterator>
+#include <cassert>
+
+constexpr bool test()
+{
+  {
+    static_assert(!std::is_convertible_v<int, std::move_sentinel<int>>);
+    std::move_sentinel<int> m(42);
+    assert(m.base() == 42);
+  }
+  {
+    static_assert(!std::is_convertible_v<int*, std::move_sentinel<int*>>);
+    int i = 42;
+    std::move_sentinel<int*> m(&i);
+    assert(m.base() == &i);
+  }
+  {
+    struct S {
+      explicit S() = default;
+      constexpr explicit S(int j) : i(j) {}
+      int i = 3;
+    };
+    static_assert(!std::is_convertible_v<S, std::move_sentinel<S>>);
+    std::move_sentinel<S> m(S(42));
+    assert(m.base().i == 42);
+  }
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.pass.cpp
new file mode 100644
index 0000000000000..fef17c5d188bb
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.sentinel/op_eq.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: c++03, c++11, c++14, c++17
+
+// <iterator>
+
+// move_sentinel
+
+// template <class Iter, class Sent>
+//   constexpr bool operator==(const move_iterator<Iter>& x, const move_sentinel<Sent>& y);
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template<class T, class U> concept HasEquals = requires (T t, U u) { t == u; };
+template<class T, class U> concept HasNotEquals = requires (T t, U u) { t != u; };
+template<class T, class U> concept HasLess = requires (T t, U u) { t < u; };
+
+static_assert(!HasEquals<std::move_iterator<int*>, std::move_sentinel<char*>>);
+static_assert(!HasNotEquals<std::move_iterator<int*>, std::move_sentinel<char*>>);
+static_assert(!HasLess<std::move_iterator<int*>, std::move_sentinel<char*>>);
+
+static_assert( HasEquals<std::move_iterator<int*>, std::move_sentinel<const int*>>);
+static_assert( HasNotEquals<std::move_iterator<int*>, std::move_sentinel<const int*>>);
+static_assert(!HasLess<std::move_iterator<int*>, std::move_sentinel<const int*>>);
+
+static_assert( HasEquals<std::move_iterator<const int*>, std::move_sentinel<int*>>);
+static_assert( HasNotEquals<std::move_iterator<const int*>, std::move_sentinel<int*>>);
+static_assert(!HasLess<std::move_iterator<const int*>, std::move_sentinel<int*>>);
+
+template <class It>
+constexpr bool test_one()
+{
+  {
+    char s[] = "abc";
+    const auto it = std::move_iterator<It>(It(s));
+    const auto sent1 = std::move_sentinel<sentinel_wrapper<It>>(sentinel_wrapper<It>(It(s)));
+    const auto sent2 = std::move_sentinel<sentinel_wrapper<It>>(sentinel_wrapper<It>(It(s + 1)));
+    ASSERT_SAME_TYPE(decltype(it == sent1), bool);
+    assert( (it == sent1));
+    assert(!(it != sent1));
+    assert(!(it == sent2));
+    assert( (it != sent2));
+    assert( (sent1 == it));
+    assert(!(sent1 != it));
+    assert(!(sent2 == it));
+    assert( (sent2 != it));
+    static_assert(!HasEquals<decltype(sent1), decltype(sent1)>);
+    static_assert(!HasLess<decltype(sent1), decltype(sent1)>);
+  }
+  return true;
+}
+
+constexpr bool test()
+{
+  test_one<cpp17_input_iterator<char*>>();
+  test_one<cpp20_input_iterator<char*>>();
+  test_one<forward_iterator<char*>>();
+  test_one<bidirectional_iterator<char*>>();
+  test_one<random_access_iterator<char*>>();
+  test_one<contiguous_iterator<char*>>();
+  test_one<three_way_contiguous_iterator<char*>>();
+  test_one<char*>();
+  test_one<const char*>();
+
+  return true;
+}
+
+int main(int, char**)
+{
+  test();
+  static_assert(test());
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list