[libcxx-commits] [libcxx] 658957c - [libc++][ranges] Implement changes to reverse_iterator from One Ranges Proposal.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Thu Mar 17 19:58:54 PDT 2022


Author: Konstantin Varlamov
Date: 2022-03-17T19:58:03-07:00
New Revision: 658957c79afa77d306a9869ac669116e5e00109d

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

LOG: [libc++][ranges] Implement changes to reverse_iterator from One Ranges Proposal.

Changes in [P0896](https://wg21.link/p0896):
- add `disable_sized_sentinel_for`;
- add `iter_move` and `iter_swap`;
- add a `requires` clause to the `operator->`;
- add `iterator_concept`;
- check that the `Iterator` template parameter is a bidirectional
  iterator;
- add constraints to all comparison operators;
- change the definitions of `iterator_category`, `value_type`,
  `difference_type` and `reference` (changes to `iterator_category` were
  already implemented).

Also add a few forgotten things to the `reverse_iterator` synopsis
(notably the spaceship operator).

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

Added: 
    libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.verify.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.sfinae.compile.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/sized_sentinel.compile.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp

Modified: 
    libcxx/docs/Status/RangesPaper.csv
    libcxx/include/__iterator/iterator_traits.h
    libcxx/include/__iterator/reverse_iterator.h
    libcxx/include/iterator
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp

Removed: 
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp


################################################################################
diff  --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index ebbc5c10916cc..b70f0645efb6e 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -75,7 +75,7 @@ Section,Description,Dependencies,Assignee,Complete
 | `ranges::next <https://llvm.org/D102563>`_
 | `ranges::prev <https://llvm.org/D102564>`_",[iterator.concepts],Christopher Di Bella and Arthur O'Dwyer,✅
 `[predef.iterators] <https://wg21.link/predef.iterators>`_,"
-| Updates to reverse_iterator
+| `Updates to reverse_iterator <https://llvm.org/D120180>`_
 | `Updates to back_insert_iterator <https://llvm.org/D103273>`_
 | `Updates to front_insert_iterator <https://llvm.org/D103273>`_
 | `Updates to move_iterator <https://llvm.org/D117656>`_","| [iterator.concepts]

diff  --git a/libcxx/include/__iterator/iterator_traits.h b/libcxx/include/__iterator/iterator_traits.h
index 775f2270048da..6ce5595addb8b 100644
--- a/libcxx/include/__iterator/iterator_traits.h
+++ b/libcxx/include/__iterator/iterator_traits.h
@@ -141,8 +141,9 @@ struct __has_iterator_concept
 
 #if _LIBCPP_STD_VER > 17
 
-// The `cpp17-*-iterator` exposition-only concepts are easily confused with the Cpp17*Iterator tables,
-// so they've been banished to a namespace that makes it obvious they have a niche use-case.
+// The `cpp17-*-iterator` exposition-only concepts have very similar names to the `Cpp17*Iterator` named requirements
+// from `[iterator.cpp17]`. To avoid confusion between the two, the exposition-only concepts have been banished to
+// a "detail" namespace indicating they have a niche use-case.
 namespace __iterator_traits_detail {
 template<class _Ip>
 concept __cpp17_iterator =

diff  --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index c77f4618534b1..bc07cf33f701c 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -12,10 +12,18 @@
 
 #include <__compare/compare_three_way_result.h>
 #include <__compare/three_way_comparable.h>
+#include <__concepts/convertible_to.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.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/prev.h>
+#include <__iterator/readable_traits.h>
 #include <__memory/addressof.h>
+#include <__utility/move.h>
 #include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -41,22 +49,31 @@ _LIBCPP_SUPPRESS_DEPRECATED_POP
     _Iter __t; // no longer used as of LWG #2360, not removed due to ABI break
 #endif
 
+#if _LIBCPP_STD_VER > 17
+    static_assert(__is_cpp17_bidirectional_iterator<_Iter>::value || bidirectional_iterator<_Iter>,
+        "reverse_iterator<It> requires It to be a bidirectional iterator.");
+#endif // _LIBCPP_STD_VER > 17
+
 protected:
     _Iter current;
 public:
-    typedef _Iter                                            iterator_type;
-    typedef typename iterator_traits<_Iter>::
diff erence_type 
diff erence_type;
-    typedef typename iterator_traits<_Iter>::reference       reference;
-    typedef typename iterator_traits<_Iter>::pointer         pointer;
-    typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
-        random_access_iterator_tag,
-        typename iterator_traits<_Iter>::iterator_category>  iterator_category;
-    typedef typename iterator_traits<_Iter>::value_type      value_type;
+    using iterator_type = _Iter;
 
+    using iterator_category = _If<__is_cpp17_random_access_iterator<_Iter>::value,
+                                  random_access_iterator_tag,
+                                  typename iterator_traits<_Iter>::iterator_category>;
+    using pointer = typename iterator_traits<_Iter>::pointer;
 #if _LIBCPP_STD_VER > 17
-    typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
-        random_access_iterator_tag,
-        bidirectional_iterator_tag>                          iterator_concept;
+    using iterator_concept = _If<__is_cpp17_random_access_iterator<_Iter>::value,
+                                  random_access_iterator_tag,
+                                  bidirectional_iterator_tag>;
+    using value_type = iter_value_t<_Iter>;
+    using 
diff erence_type = iter_
diff erence_t<_Iter>;
+    using reference = iter_reference_t<_Iter>;
+#else
+    using value_type = typename iterator_traits<_Iter>::value_type;
+    using 
diff erence_type = typename iterator_traits<_Iter>::
diff erence_type;
+    using reference = typename iterator_traits<_Iter>::reference;
 #endif
 
 #ifndef _LIBCPP_ABI_NO_ITERATOR_BASES
@@ -114,32 +131,75 @@ _LIBCPP_SUPPRESS_DEPRECATED_POP
     _Iter base() const {return current;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reference operator*() const {_Iter __tmp = current; return *--__tmp;}
+
+#if _LIBCPP_STD_VER > 17
+    _LIBCPP_INLINE_VISIBILITY
+    constexpr pointer operator->() const
+      requires is_pointer_v<_Iter> || requires(const _Iter i) { i.operator->(); }
+    {
+      if constexpr (is_pointer_v<_Iter>) {
+        return std::prev(current);
+      } else {
+        return std::prev(current).operator->();
+      }
+    }
+#else
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    pointer  operator->() const {return _VSTD::addressof(operator*());}
+    pointer operator->() const {
+      return std::addressof(operator*());
+    }
+#endif // _LIBCPP_STD_VER > 17
+
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reverse_iterator& operator++() {--current; return *this;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    reverse_iterator  operator++(int) {reverse_iterator __tmp(*this); --current; return __tmp;}
+    reverse_iterator operator++(int) {reverse_iterator __tmp(*this); --current; return __tmp;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reverse_iterator& operator--() {++current; return *this;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    reverse_iterator  operator--(int) {reverse_iterator __tmp(*this); ++current; return __tmp;}
+    reverse_iterator operator--(int) {reverse_iterator __tmp(*this); ++current; return __tmp;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    reverse_iterator  operator+ (
diff erence_type __n) const {return reverse_iterator(current - __n);}
+    reverse_iterator operator+(
diff erence_type __n) const {return reverse_iterator(current - __n);}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reverse_iterator& operator+=(
diff erence_type __n) {current -= __n; return *this;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    reverse_iterator  operator- (
diff erence_type __n) const {return reverse_iterator(current + __n);}
+    reverse_iterator operator-(
diff erence_type __n) const {return reverse_iterator(current + __n);}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reverse_iterator& operator-=(
diff erence_type __n) {current += __n; return *this;}
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-    reference         operator[](
diff erence_type __n) const {return *(*this + __n);}
+    reference operator[](
diff erence_type __n) const {return *(*this + __n);}
+
+#if _LIBCPP_STD_VER > 17
+    _LIBCPP_HIDE_FROM_ABI friend constexpr
+    iter_rvalue_reference_t<_Iter> iter_move(const reverse_iterator& __i)
+      noexcept(is_nothrow_copy_constructible_v<_Iter> &&
+          noexcept(ranges::iter_move(--declval<_Iter&>()))) {
+      auto __tmp = __i.base();
+      return ranges::iter_move(--__tmp);
+    }
+
+    template <indirectly_swappable<_Iter> _Iter2>
+    _LIBCPP_HIDE_FROM_ABI friend constexpr
+    void iter_swap(const reverse_iterator& __x, const reverse_iterator<_Iter2>& __y)
+      noexcept(is_nothrow_copy_constructible_v<_Iter> &&
+          is_nothrow_copy_constructible_v<_Iter2> &&
+          noexcept(ranges::iter_swap(--declval<_Iter&>(), --declval<_Iter2&>()))) {
+      auto __xtmp = __x.base();
+      auto __ytmp = __y.base();
+      ranges::iter_swap(--__xtmp, --__ytmp);
+    }
+#endif // _LIBCPP_STD_VER > 17
 };
 
 template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
 operator==(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
+#if _LIBCPP_STD_VER > 17
+    requires requires {
+      { __x.base() == __y.base() } -> convertible_to<bool>;
+    }
+#endif // _LIBCPP_STD_VER > 17
 {
     return __x.base() == __y.base();
 }
@@ -148,6 +208,11 @@ template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
 operator<(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
+#if _LIBCPP_STD_VER > 17
+    requires requires {
+        { __x.base() > __y.base() } -> convertible_to<bool>;
+      }
+#endif // _LIBCPP_STD_VER > 17
 {
     return __x.base() > __y.base();
 }
@@ -156,6 +221,11 @@ template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
 operator!=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
+#if _LIBCPP_STD_VER > 17
+    requires requires {
+      { __x.base() != __y.base() } -> convertible_to<bool>;
+    }
+#endif // _LIBCPP_STD_VER > 17
 {
     return __x.base() != __y.base();
 }
@@ -164,6 +234,11 @@ template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
 operator>(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
+#if _LIBCPP_STD_VER > 17
+    requires requires {
+        { __x.base() < __y.base() } -> convertible_to<bool>;
+      }
+#endif // _LIBCPP_STD_VER > 17
 {
     return __x.base() < __y.base();
 }
@@ -172,6 +247,11 @@ template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
 operator>=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
+#if _LIBCPP_STD_VER > 17
+    requires requires {
+        { __x.base() <= __y.base() } -> convertible_to<bool>;
+      }
+#endif // _LIBCPP_STD_VER > 17
 {
     return __x.base() <= __y.base();
 }
@@ -180,6 +260,11 @@ template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
 operator<=(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
+#if _LIBCPP_STD_VER > 17
+    requires requires {
+        { __x.base() >= __y.base() } -> convertible_to<bool>;
+      }
+#endif // _LIBCPP_STD_VER > 17
 {
     return __x.base() >= __y.base();
 }
@@ -221,6 +306,12 @@ operator+(typename reverse_iterator<_Iter>::
diff erence_type __n, const reverse_i
     return reverse_iterator<_Iter>(__x.base() - __n);
 }
 
+#if _LIBCPP_STD_VER > 17
+template <class _Iter1, class _Iter2>
+  requires (!sized_sentinel_for<_Iter1, _Iter2>)
+inline constexpr bool disable_sized_sentinel_for<reverse_iterator<_Iter1>, reverse_iterator<_Iter2>> = true;
+#endif // _LIBCPP_STD_VER > 17
+
 #if _LIBCPP_STD_VER > 11
 template <class _Iter>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14

diff  --git a/libcxx/include/iterator b/libcxx/include/iterator
index 852fa353a9c2a..8ba4d0ccb17cf 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -225,10 +225,17 @@ class reverse_iterator
 protected:
     Iterator current;
 public:
-    typedef Iterator                                            iterator_type;
-    typedef typename iterator_traits<Iterator>::
diff erence_type 
diff erence_type;
-    typedef typename iterator_traits<Iterator>::reference       reference;
-    typedef typename iterator_traits<Iterator>::pointer         pointer;
+    using iterator_type     = Iterator;
+    using iterator_concept  = see below; // since C++20
+    using iterator_category = typename iterator_traits<Iterator>::iterator_category; // since C++17, until C++20
+    using iterator_category = see below; // since C++20
+    using value_type        = typename iterator_traits<Iterator>::value_type; // since C++17, until C++20
+    using value_type        = iter_value_t<Iterator>; // since C++20
+    using 
diff erence_type   = typename iterator_traits<Iterator>::
diff erence_type; // until C++20
+    using 
diff erence_type   = iter_
diff erence_t<Iterator>; // since C++20
+    using pointer           = typename iterator_traits<Iterator>::pointer;
+    using reference         = typename iterator_traits<Iterator>::reference; // until C++20
+    using reference         = iter_reference_t<Iterator>; // since C++20
 
     constexpr reverse_iterator();
     constexpr explicit reverse_iterator(Iterator x);
@@ -236,7 +243,8 @@ public:
     template <class U> constexpr reverse_iterator& operator=(const reverse_iterator<U>& u);
     constexpr Iterator base() const;
     constexpr reference operator*() const;
-    constexpr pointer   operator->() const;
+    constexpr pointer   operator->() const; // until C++20
+    constexpr pointer   operator->() const requires see below; // since C++20
     constexpr reverse_iterator& operator++();
     constexpr reverse_iterator  operator++(int);
     constexpr reverse_iterator& operator--();
@@ -245,7 +253,14 @@ public:
     constexpr reverse_iterator& operator+=(
diff erence_type n);
     constexpr reverse_iterator  operator- (
diff erence_type n) const;
     constexpr reverse_iterator& operator-=(
diff erence_type n);
-    constexpr reference         operator[](
diff erence_type n) const;
+    constexpr unspecified       operator[](
diff erence_type n) const;
+
+    friend constexpr iter_rvalue_reference_t<Iterator>
+      iter_move(const reverse_iterator& i) noexcept(see below);
+    template<indirectly_swappable<Iterator> Iterator2>
+      friend constexpr void
+        iter_swap(const reverse_iterator& x,
+                  const reverse_iterator<Iterator2>& y) noexcept(see below);
 };
 
 template <class Iterator1, class Iterator2>
@@ -254,11 +269,11 @@ operator==(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator
 
 template <class Iterator1, class Iterator2>
 constexpr bool                          // constexpr in C++17
-operator<(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+operator!=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
 
 template <class Iterator1, class Iterator2>
 constexpr bool                          // constexpr in C++17
-operator!=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+operator<(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
 
 template <class Iterator1, class Iterator2>
 constexpr bool                          // constexpr in C++17
@@ -266,11 +281,16 @@ operator>(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2
 
 template <class Iterator1, class Iterator2>
 constexpr bool                          // constexpr in C++17
-operator>=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+operator<=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
 
 template <class Iterator1, class Iterator2>
 constexpr bool                          // constexpr in C++17
-operator<=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+operator>=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+
+template<class Iterator1, three_way_comparable_with<Iterator1> Iterator2>
+  constexpr compare_three_way_result_t<Iterator1, Iterator2>
+    operator<=>(const reverse_iterator<Iterator1>& x,
+                const reverse_iterator<Iterator2>& y);
 
 template <class Iterator1, class Iterator2>
 constexpr auto
@@ -285,6 +305,11 @@ operator+(typename reverse_iterator<Iterator>::
diff erence_type n,
 template <class Iterator>
 constexpr reverse_iterator<Iterator> make_reverse_iterator(Iterator i); // C++14, constexpr in C++17
 
+template<class Iterator1, class Iterator2>
+    requires (!sized_sentinel_for<Iterator1, Iterator2>)
+  inline constexpr bool disable_sized_sentinel_for<reverse_iterator<Iterator1>,
+                                                   reverse_iterator<Iterator2>> = true;
+
 template <class Container>
 class back_insert_iterator
     : public iterator<output_iterator_tag, void, void, void, void> // until C++17

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.verify.cpp
new file mode 100644
index 0000000000000..9849173d11a8d
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/reverse.iterators/bad_template_argument.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// reverse_iterator
+
+#include <iterator>
+
+#include "test_iterators.h"
+
+int main(int, char**) {
+  using BadIter = std::reverse_iterator<forward_iterator<int*>>;
+  BadIter i; //expected-error-re@*:* {{static_assert failed{{.*}} "reverse_iterator<It> requires It to be a bidirectional iterator."}}
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp
index 66ddef131b5ac..4aae0447d4efb 100644
--- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/iterator_concept_conformance.compile.pass.cpp
@@ -18,8 +18,6 @@ template<class I1>
 constexpr bool common_reverse_iterator_checks() {
   static_assert(std::indirectly_writable<I1, int>);
   static_assert(std::sentinel_for<I1, I1>);
-  static_assert(std::sentinel_for<I1, std::reverse_iterator<float*>>);
-  static_assert(!std::sized_sentinel_for<I1, std::reverse_iterator<float*>>);
   return true;
 }
 
@@ -55,3 +53,10 @@ static_assert( std::indirectly_movable_storable<reverse_contiguous_iterator, rev
 static_assert( std::indirectly_copyable<reverse_contiguous_iterator, reverse_contiguous_iterator>);
 static_assert( std::indirectly_copyable_storable<reverse_contiguous_iterator, reverse_contiguous_iterator>);
 static_assert( std::indirectly_swappable<reverse_contiguous_iterator, reverse_contiguous_iterator>);
+
+static_assert( std::equality_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<const int*>>);
+static_assert(!std::equality_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<char*>>);
+static_assert( std::totally_ordered_with<std::reverse_iterator<int*>, std::reverse_iterator<const int*>>);
+static_assert(!std::totally_ordered_with<std::reverse_iterator<int*>, std::reverse_iterator<char*>>);
+static_assert( std::three_way_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<const int*>>);
+static_assert(!std::three_way_comparable_with<std::reverse_iterator<int*>, std::reverse_iterator<char*>>);

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp
new file mode 100644
index 0000000000000..9126a793f3658
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/sfinae.compile.pass.cpp
@@ -0,0 +1,196 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//
+// reverse_iterator
+//
+// template <class Iterator1, class Iterator2>
+// constexpr bool                          // constexpr in C++17
+// operator==(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+//
+// template <class Iterator1, class Iterator2>
+// constexpr bool                          // constexpr in C++17
+// operator!=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+//
+// template <class Iterator1, class Iterator2>
+// constexpr bool                          // constexpr in C++17
+// operator<(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+//
+// template <class Iterator1, class Iterator2>
+// constexpr bool                          // constexpr in C++17
+// operator>(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+//
+// template <class Iterator1, class Iterator2>
+// constexpr bool                          // constexpr in C++17
+// operator<=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+//
+// template <class Iterator1, class Iterator2>
+// constexpr bool                          // constexpr in C++17
+// operator>=(const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y);
+//
+// template<class Iterator1, three_way_comparable_with<Iterator1> Iterator2>
+//  constexpr compare_three_way_result_t<Iterator1, Iterator2>
+//    operator<=>(const reverse_iterator<Iterator1>& x,
+//                const reverse_iterator<Iterator2>& y);
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+
+struct IterBase {
+  using iterator_category = std::bidirectional_iterator_tag;
+  using value_type = int;
+  using 
diff erence_type = ptr
diff _t;
+  using pointer = int*;
+  using reference = int&;
+
+  reference operator*() const;
+  pointer operator->() const;
+};
+
+template<class T> concept HasEqual = requires (T t) { t == t; };
+template<class T> concept HasNotEqual = requires (T t) { t != t; };
+template<class T> concept HasLess = requires (T t) { t < t; };
+template<class T> concept HasLessOrEqual = requires (T t) { t <= t; };
+template<class T> concept HasGreater = requires (T t) { t > t; };
+template<class T> concept HasGreaterOrEqual = requires (T t) { t >= t; };
+template<class T> concept HasSpaceship = requires (T t) { t <=> t; };
+
+// operator ==
+
+struct NoEqualityCompIter : IterBase {
+  bool operator!=(NoEqualityCompIter) const;
+  bool operator<(NoEqualityCompIter) const;
+  bool operator>(NoEqualityCompIter) const;
+  bool operator<=(NoEqualityCompIter) const;
+  bool operator>=(NoEqualityCompIter) const;
+};
+
+static_assert( HasEqual<std::reverse_iterator<int*>>);
+static_assert(!HasEqual<std::reverse_iterator<NoEqualityCompIter>>);
+static_assert( HasNotEqual<std::reverse_iterator<NoEqualityCompIter>>);
+static_assert( HasLess<std::reverse_iterator<NoEqualityCompIter>>);
+static_assert( HasLessOrEqual<std::reverse_iterator<NoEqualityCompIter>>);
+static_assert( HasGreater<std::reverse_iterator<NoEqualityCompIter>>);
+static_assert( HasGreaterOrEqual<std::reverse_iterator<NoEqualityCompIter>>);
+
+void Foo() {
+  std::reverse_iterator<NoEqualityCompIter> i;
+  (void)i;
+}
+
+// operator !=
+
+struct NoInequalityCompIter : IterBase {
+  bool operator<(NoInequalityCompIter) const;
+  bool operator>(NoInequalityCompIter) const;
+  bool operator<=(NoInequalityCompIter) const;
+  bool operator>=(NoInequalityCompIter) const;
+};
+
+static_assert( HasNotEqual<std::reverse_iterator<int*>>);
+static_assert(!HasNotEqual<std::reverse_iterator<NoInequalityCompIter>>);
+static_assert(!HasEqual<std::reverse_iterator<NoInequalityCompIter>>);
+static_assert( HasLess<std::reverse_iterator<NoInequalityCompIter>>);
+static_assert( HasLessOrEqual<std::reverse_iterator<NoInequalityCompIter>>);
+static_assert( HasGreater<std::reverse_iterator<NoInequalityCompIter>>);
+static_assert( HasGreaterOrEqual<std::reverse_iterator<NoInequalityCompIter>>);
+
+// operator <
+
+struct NoGreaterCompIter : IterBase {
+  bool operator==(NoGreaterCompIter) const;
+  bool operator!=(NoGreaterCompIter) const;
+  bool operator<(NoGreaterCompIter) const;
+  bool operator<=(NoGreaterCompIter) const;
+  bool operator>=(NoGreaterCompIter) const;
+};
+
+static_assert( HasLess<std::reverse_iterator<int*>>);
+static_assert(!HasLess<std::reverse_iterator<NoGreaterCompIter>>);
+static_assert( HasEqual<std::reverse_iterator<NoGreaterCompIter>>);
+static_assert( HasNotEqual<std::reverse_iterator<NoGreaterCompIter>>);
+static_assert( HasLessOrEqual<std::reverse_iterator<NoGreaterCompIter>>);
+static_assert( HasGreater<std::reverse_iterator<NoGreaterCompIter>>);
+static_assert( HasGreaterOrEqual<std::reverse_iterator<NoGreaterCompIter>>);
+
+// operator >
+
+struct NoLessCompIter : IterBase {
+  bool operator==(NoLessCompIter) const;
+  bool operator!=(NoLessCompIter) const;
+  bool operator>(NoLessCompIter) const;
+  bool operator<=(NoLessCompIter) const;
+  bool operator>=(NoLessCompIter) const;
+};
+
+static_assert( HasGreater<std::reverse_iterator<int*>>);
+static_assert(!HasGreater<std::reverse_iterator<NoLessCompIter>>);
+static_assert( HasEqual<std::reverse_iterator<NoLessCompIter>>);
+static_assert( HasNotEqual<std::reverse_iterator<NoLessCompIter>>);
+static_assert( HasLess<std::reverse_iterator<NoLessCompIter>>);
+static_assert( HasLessOrEqual<std::reverse_iterator<NoLessCompIter>>);
+static_assert( HasGreaterOrEqual<std::reverse_iterator<NoLessCompIter>>);
+
+// operator <=
+
+struct NoGreaterOrEqualCompIter : IterBase {
+  bool operator==(NoGreaterOrEqualCompIter) const;
+  bool operator!=(NoGreaterOrEqualCompIter) const;
+  bool operator<(NoGreaterOrEqualCompIter) const;
+  bool operator>(NoGreaterOrEqualCompIter) const;
+  bool operator<=(NoGreaterOrEqualCompIter) const;
+};
+
+static_assert( HasLessOrEqual<std::reverse_iterator<int*>>);
+static_assert(!HasLessOrEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+static_assert( HasEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+static_assert( HasNotEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+static_assert( HasLess<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+static_assert( HasGreater<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+static_assert( HasGreaterOrEqual<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+
+// operator >=
+
+struct NoLessOrEqualCompIter : IterBase {
+  bool operator==(NoLessOrEqualCompIter) const;
+  bool operator!=(NoLessOrEqualCompIter) const;
+  bool operator<(NoLessOrEqualCompIter) const;
+  bool operator>(NoLessOrEqualCompIter) const;
+  bool operator>=(NoLessOrEqualCompIter) const;
+};
+
+static_assert( HasGreaterOrEqual<std::reverse_iterator<int*>>);
+static_assert(!HasGreaterOrEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
+static_assert( HasEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
+static_assert( HasNotEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
+static_assert( HasLess<std::reverse_iterator<NoLessOrEqualCompIter>>);
+static_assert( HasLessOrEqual<std::reverse_iterator<NoLessOrEqualCompIter>>);
+static_assert( HasGreater<std::reverse_iterator<NoLessOrEqualCompIter>>);
+
+// operator <=>
+
+static_assert( std::three_way_comparable_with<int*, int*>);
+static_assert( HasSpaceship<std::reverse_iterator<int*>>);
+static_assert(!std::three_way_comparable_with<NoEqualityCompIter, NoEqualityCompIter>);
+static_assert(!HasSpaceship<std::reverse_iterator<NoEqualityCompIter>>);
+static_assert(!std::three_way_comparable_with<NoInequalityCompIter, NoInequalityCompIter>);
+static_assert(!HasSpaceship<std::reverse_iterator<NoInequalityCompIter>>);
+static_assert(!std::three_way_comparable_with<NoGreaterCompIter, NoGreaterCompIter>);
+static_assert(!HasSpaceship<std::reverse_iterator<NoGreaterCompIter>>);
+static_assert(!std::three_way_comparable_with<NoLessCompIter, NoLessCompIter>);
+static_assert(!HasSpaceship<std::reverse_iterator<NoLessCompIter>>);
+static_assert(!std::three_way_comparable_with<NoGreaterOrEqualCompIter, NoGreaterOrEqualCompIter>);
+static_assert(!HasSpaceship<std::reverse_iterator<NoGreaterOrEqualCompIter>>);
+static_assert(!std::three_way_comparable_with<NoLessOrEqualCompIter, NoLessOrEqualCompIter>);
+static_assert(!HasSpaceship<std::reverse_iterator<NoLessOrEqualCompIter>>);

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp
index a1c40eac1569d..bbe9c3ca5802e 100644
--- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp
@@ -76,43 +76,43 @@ TEST_CONSTEXPR  C gC;
 
 int main(int, char**)
 {
-    A a;
-    test(&a+1, A());
+  A a;
+  test(&a+1, A());
 
-    {
+  {
     std::list<B> l;
     l.push_back(B(0));
     l.push_back(B(1));
     l.push_back(B(2));
 
     {
-    std::list<B>::const_iterator i = l.begin();
-    assert ( i->get() == 0 );  ++i;
-    assert ( i->get() == 1 );  ++i;
-    assert ( i->get() == 2 );  ++i;
-    assert ( i == l.end ());
+      std::list<B>::const_iterator i = l.begin();
+      assert ( i->get() == 0 );  ++i;
+      assert ( i->get() == 1 );  ++i;
+      assert ( i->get() == 2 );  ++i;
+      assert ( i == l.end ());
     }
 
     {
-    std::list<B>::const_reverse_iterator ri = l.rbegin();
-    assert ( ri->get() == 2 );  ++ri;
-    assert ( ri->get() == 1 );  ++ri;
-    assert ( ri->get() == 0 );  ++ri;
-    assert ( ri == l.rend ());
-    }
+      std::list<B>::const_reverse_iterator ri = l.rbegin();
+      assert ( ri->get() == 2 );  ++ri;
+      assert ( ri->get() == 1 );  ++ri;
+      assert ( ri->get() == 0 );  ++ri;
+      assert ( ri == l.rend ());
     }
+  }
 
 #if TEST_STD_VER > 14
-    {
-        typedef std::reverse_iterator<const C *> RI;
-        constexpr RI it1 = std::make_reverse_iterator(&gC+1);
+  {
+    typedef std::reverse_iterator<const C *> RI;
+    constexpr RI it1 = std::make_reverse_iterator(&gC+1);
 
-        static_assert(it1->get() == gC.get(), "");
-    }
+    static_assert(it1->get() == gC.get(), "");
+  }
 #endif
-    {
-        ((void)gC);
-    }
+  {
+    ((void)gC);
+  }
 
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.sfinae.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.sfinae.compile.pass.cpp
new file mode 100644
index 0000000000000..b723176a19c45
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.sfinae.compile.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
+
+// <iterator>
+//
+// reverse_iterator
+//
+// pointer operator->() const;
+
+#include <iterator>
+
+#include <type_traits>
+#include "test_iterators.h"
+
+template <class T>
+concept HasArrow = requires(T t) { t.operator->(); };
+
+struct simple_bidirectional_iterator {
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = int;
+    using 
diff erence_type = int;
+    using pointer = int*;
+    using reference = int&;
+
+    reference operator*() const;
+    pointer operator->() const;
+
+    simple_bidirectional_iterator& operator++();
+    simple_bidirectional_iterator& operator--();
+    simple_bidirectional_iterator operator++(int);
+    simple_bidirectional_iterator operator--(int);
+
+    friend bool operator==(const simple_bidirectional_iterator&, const simple_bidirectional_iterator&);
+};
+static_assert( std::bidirectional_iterator<simple_bidirectional_iterator>);
+static_assert(!std::random_access_iterator<simple_bidirectional_iterator>);
+
+using PtrRI = std::reverse_iterator<int*>;
+static_assert( HasArrow<PtrRI>);
+
+using PtrLikeRI = std::reverse_iterator<simple_bidirectional_iterator>;
+static_assert( HasArrow<PtrLikeRI>);
+
+// `bidirectional_iterator` from `test_iterators.h` doesn't define `operator->`.
+using NonPtrRI = std::reverse_iterator<bidirectional_iterator<int*>>;
+static_assert(!HasArrow<NonPtrRI>);

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp
new file mode 100644
index 0000000000000..acf91784ef9b4
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_move.pass.cpp
@@ -0,0 +1,178 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//
+// reverse_iterator
+//
+// friend constexpr iter_rvalue_reference_t<Iterator>
+//   iter_move(const reverse_iterator& i) noexcept(see below);
+
+#include <iterator>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include "test_macros.h"
+
+namespace adl {
+
+struct Iterator {
+  using value_type = int;
+  using 
diff erence_type = ptr
diff _t;
+
+  value_type* ptr_ = nullptr;
+  int* iter_move_invocations_ = nullptr;
+
+  constexpr Iterator() = default;
+  constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {}
+
+  constexpr value_type& operator*() const { return *ptr_; }
+
+  Iterator& operator++() { ++ptr_; return *this; }
+  Iterator operator++(int) {
+    Iterator prev = *this;
+    ++ptr_;
+    return prev;
+  }
+
+  constexpr Iterator& operator--() { --ptr_; return *this; }
+  constexpr Iterator operator--(int) {
+    Iterator prev = *this;
+    --ptr_;
+    return prev;
+  }
+
+  constexpr friend value_type&& iter_move(Iterator iter) {
+    if (iter.iter_move_invocations_) {
+      ++(*iter.iter_move_invocations_);
+    }
+    return std::move(*iter);
+  }
+
+  friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; }
+};
+
+} // namespace adl
+
+constexpr bool test() {
+  // Can use `iter_move` with a regular array.
+  {
+    constexpr int N = 3;
+    int a[N] = {0, 1, 2};
+
+    std::reverse_iterator<int*> ri(a + N);
+    static_assert(std::same_as<decltype(iter_move(ri)), int&&>);
+    assert(iter_move(ri) == 2);
+
+    ++ri;
+    assert(iter_move(ri) == 1);
+  }
+
+  // Ensure the `iter_move` customization point is being used.
+  {
+    constexpr int N = 3;
+    int a[N] = {0, 1, 2};
+
+    int iter_move_invocations = 0;
+    adl::Iterator i(a + N, iter_move_invocations);
+    std::reverse_iterator<adl::Iterator> ri(i);
+    int x = iter_move(ri);
+    assert(x == 2);
+    assert(iter_move_invocations == 1);
+  }
+
+  // Check the `noexcept` specification.
+  {
+    {
+      struct ThrowingCopyNoexceptDecrement {
+        using value_type = int;
+        using 
diff erence_type = ptr
diff _t;
+
+        ThrowingCopyNoexceptDecrement();
+        ThrowingCopyNoexceptDecrement(const ThrowingCopyNoexceptDecrement&);
+
+        int& operator*() const noexcept { static int x; return x; }
+
+        ThrowingCopyNoexceptDecrement& operator++();
+        ThrowingCopyNoexceptDecrement operator++(int);
+        ThrowingCopyNoexceptDecrement& operator--() noexcept;
+        ThrowingCopyNoexceptDecrement operator--(int) noexcept;
+
+        bool operator==(const ThrowingCopyNoexceptDecrement&) const = default;
+      };
+      static_assert(std::bidirectional_iterator<ThrowingCopyNoexceptDecrement>);
+
+      static_assert(!std::is_nothrow_copy_constructible_v<ThrowingCopyNoexceptDecrement>);
+      ASSERT_NOEXCEPT(std::ranges::iter_move(--std::declval<ThrowingCopyNoexceptDecrement&>()));
+      using RI = std::reverse_iterator<ThrowingCopyNoexceptDecrement>;
+      ASSERT_NOT_NOEXCEPT(iter_move(std::declval<RI>()));
+    }
+
+    {
+      struct NoexceptCopyThrowingDecrement {
+        using value_type = int;
+        using 
diff erence_type = ptr
diff _t;
+
+        NoexceptCopyThrowingDecrement();
+        NoexceptCopyThrowingDecrement(const NoexceptCopyThrowingDecrement&) noexcept;
+
+        int& operator*() const { static int x; return x; }
+
+        NoexceptCopyThrowingDecrement& operator++();
+        NoexceptCopyThrowingDecrement operator++(int);
+        NoexceptCopyThrowingDecrement& operator--();
+        NoexceptCopyThrowingDecrement operator--(int);
+
+        bool operator==(const NoexceptCopyThrowingDecrement&) const = default;
+      };
+      static_assert(std::bidirectional_iterator<NoexceptCopyThrowingDecrement>);
+
+      static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyThrowingDecrement>);
+      ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(--std::declval<NoexceptCopyThrowingDecrement&>()));
+      using RI = std::reverse_iterator<NoexceptCopyThrowingDecrement>;
+      ASSERT_NOT_NOEXCEPT(iter_move(std::declval<RI>()));
+    }
+
+    {
+      struct NoexceptCopyAndDecrement {
+        using value_type = int;
+        using 
diff erence_type = ptr
diff _t;
+
+        NoexceptCopyAndDecrement();
+        NoexceptCopyAndDecrement(const NoexceptCopyAndDecrement&) noexcept;
+
+        int& operator*() const noexcept { static int x; return x; }
+
+        NoexceptCopyAndDecrement& operator++();
+        NoexceptCopyAndDecrement operator++(int);
+        NoexceptCopyAndDecrement& operator--() noexcept;
+        NoexceptCopyAndDecrement operator--(int) noexcept;
+
+        bool operator==(const NoexceptCopyAndDecrement&) const = default;
+      };
+      static_assert(std::bidirectional_iterator<NoexceptCopyAndDecrement>);
+
+      static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyAndDecrement>);
+      ASSERT_NOEXCEPT(std::ranges::iter_move(--std::declval<NoexceptCopyAndDecrement&>()));
+      using RI = std::reverse_iterator<NoexceptCopyAndDecrement>;
+      ASSERT_NOEXCEPT(iter_move(std::declval<RI>()));
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..3ae27368007ab
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nonmember/iter_swap.pass.cpp
@@ -0,0 +1,190 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//
+// reverse_iterator
+//
+// template<indirectly_swappable<Iterator> Iterator2>
+//   friend constexpr void
+//     iter_swap(const reverse_iterator& x,
+//               const reverse_iterator<Iterator2>& y) noexcept(see below);
+
+#include <iterator>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include "test_macros.h"
+
+namespace adl {
+
+struct Iterator {
+  using value_type = int;
+  using 
diff erence_type = ptr
diff _t;
+
+  value_type* ptr_ = nullptr;
+  int* iter_swap_invocations_ = nullptr;
+
+  constexpr Iterator() = default;
+  constexpr explicit Iterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {}
+
+  value_type& operator*() const { return *ptr_; }
+
+  Iterator& operator++() { ++ptr_; return *this; }
+  Iterator operator++(int) {
+    Iterator prev = *this;
+    ++ptr_;
+    return prev;
+  }
+
+  Iterator& operator--() { --ptr_; return *this; }
+  Iterator operator--(int) {
+    Iterator prev = *this;
+    --ptr_;
+    return prev;
+  }
+
+  constexpr friend void iter_swap(Iterator a, Iterator) {
+    if (a.iter_swap_invocations_) {
+      ++(*a.iter_swap_invocations_);
+    }
+  }
+
+  friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; }
+};
+
+} // namespace adl
+
+constexpr bool test() {
+  // Can use `iter_swap` with a regular array.
+  {
+    constexpr int N = 3;
+    int a[N] = {0, 1, 2};
+
+    std::reverse_iterator rb(a + N);
+    std::reverse_iterator re(a + 1);
+    assert(a[0] == 0);
+    assert(a[2] == 2);
+
+    static_assert(std::same_as<decltype(iter_swap(rb, re)), void>);
+    iter_swap(rb, re);
+    assert(a[0] == 2);
+    assert(a[2] == 0);
+  }
+
+  // Ensure the `iter_swap` customization point is being used.
+  {
+    int iter_swap_invocations = 0;
+    adl::Iterator i1(iter_swap_invocations), i2(iter_swap_invocations);
+    std::reverse_iterator<adl::Iterator> ri1(i1), ri2(i2);
+    iter_swap(i1, i2);
+    assert(iter_swap_invocations == 1);
+
+    iter_swap(i2, i1);
+    assert(iter_swap_invocations == 2);
+  }
+
+  // Check the `noexcept` specification.
+  {
+    {
+      struct ThrowingCopyNoexceptDecrement {
+        using value_type = int;
+        using 
diff erence_type = ptr
diff _t;
+
+        ThrowingCopyNoexceptDecrement();
+        ThrowingCopyNoexceptDecrement(const ThrowingCopyNoexceptDecrement&);
+
+        int& operator*() const noexcept { static int x; return x; }
+
+        ThrowingCopyNoexceptDecrement& operator++();
+        ThrowingCopyNoexceptDecrement operator++(int);
+        ThrowingCopyNoexceptDecrement& operator--() noexcept;
+        ThrowingCopyNoexceptDecrement operator--(int) noexcept;
+
+        bool operator==(const ThrowingCopyNoexceptDecrement&) const = default;
+      };
+      static_assert(std::bidirectional_iterator<ThrowingCopyNoexceptDecrement>);
+
+      static_assert(!std::is_nothrow_copy_constructible_v<ThrowingCopyNoexceptDecrement>);
+      static_assert( std::is_nothrow_copy_constructible_v<int*>);
+      ASSERT_NOEXCEPT(std::ranges::iter_swap(--std::declval<ThrowingCopyNoexceptDecrement&>(), --std::declval<int*&>()));
+      using RI1 = std::reverse_iterator<ThrowingCopyNoexceptDecrement>;
+      using RI2 = std::reverse_iterator<int*>;
+      ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI1>(), std::declval<RI2>()));
+      ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI2>(), std::declval<RI1>()));
+    }
+
+    {
+      struct NoexceptCopyThrowingDecrement {
+        using value_type = int;
+        using 
diff erence_type = ptr
diff _t;
+
+        NoexceptCopyThrowingDecrement();
+        NoexceptCopyThrowingDecrement(const NoexceptCopyThrowingDecrement&) noexcept;
+
+        int& operator*() const { static int x; return x; }
+
+        NoexceptCopyThrowingDecrement& operator++();
+        NoexceptCopyThrowingDecrement operator++(int);
+        NoexceptCopyThrowingDecrement& operator--();
+        NoexceptCopyThrowingDecrement operator--(int);
+
+        bool operator==(const NoexceptCopyThrowingDecrement&) const = default;
+      };
+      static_assert(std::bidirectional_iterator<NoexceptCopyThrowingDecrement>);
+
+      static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyThrowingDecrement>);
+      static_assert( std::is_nothrow_copy_constructible_v<int*>);
+      ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap(--std::declval<NoexceptCopyThrowingDecrement&>(), --std::declval<int*&>()));
+      using RI1 = std::reverse_iterator<NoexceptCopyThrowingDecrement>;
+      using RI2 = std::reverse_iterator<int*>;
+      ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI1>(), std::declval<RI2>()));
+      ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<RI2>(), std::declval<RI1>()));
+    }
+
+    {
+      struct NoexceptCopyAndDecrement {
+        using value_type = int;
+        using 
diff erence_type = ptr
diff _t;
+
+        NoexceptCopyAndDecrement();
+        NoexceptCopyAndDecrement(const NoexceptCopyAndDecrement&) noexcept;
+
+        int& operator*() const noexcept { static int x; return x; }
+
+        NoexceptCopyAndDecrement& operator++();
+        NoexceptCopyAndDecrement operator++(int);
+        NoexceptCopyAndDecrement& operator--() noexcept;
+        NoexceptCopyAndDecrement operator--(int) noexcept;
+
+        bool operator==(const NoexceptCopyAndDecrement&) const = default;
+      };
+      static_assert(std::bidirectional_iterator<NoexceptCopyAndDecrement>);
+
+      static_assert( std::is_nothrow_copy_constructible_v<NoexceptCopyAndDecrement>);
+      static_assert( std::is_nothrow_copy_constructible_v<int*>);
+      ASSERT_NOEXCEPT(std::ranges::iter_swap(--std::declval<NoexceptCopyAndDecrement&>(), --std::declval<int*&>()));
+      using RI1 = std::reverse_iterator<NoexceptCopyAndDecrement>;
+      using RI2 = std::reverse_iterator<int*>;
+      ASSERT_NOEXCEPT(iter_swap(std::declval<RI1>(), std::declval<RI2>()));
+      ASSERT_NOEXCEPT(iter_swap(std::declval<RI2>(), std::declval<RI1>()));
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/sized_sentinel.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/sized_sentinel.compile.pass.cpp
new file mode 100644
index 0000000000000..df53b8ff4de3a
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/sized_sentinel.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// reverse_iterator
+
+#include <iterator>
+
+#include "test_iterators.h"
+
+template<class T> concept HasMinus = requires (T t) { t - t; };
+
+using sized_it = random_access_iterator<int*>;
+static_assert( std::sized_sentinel_for<sized_it, sized_it>);
+static_assert( std::sized_sentinel_for<std::reverse_iterator<sized_it>, std::reverse_iterator<sized_it>>);
+static_assert( HasMinus<std::reverse_iterator<sized_it>>);
+
+// Check that `sized_sentinel_for` is false for `reverse_iterator`s if it is false for the underlying iterators.
+using unsized_it = bidirectional_iterator<int*>;
+static_assert(!std::sized_sentinel_for<unsized_it, unsized_it>);
+static_assert(!std::sized_sentinel_for<std::reverse_iterator<unsized_it>, std::reverse_iterator<unsized_it>>);
+static_assert(!HasMinus<std::reverse_iterator<unsized_it>>);

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp
new file mode 100644
index 0000000000000..419df883f1026
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.compile.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <iterator>
+
+// reverse_iterator
+
+// Test nested types and data member:
+
+// template <BidirectionalIterator Iter>
+// class reverse_iterator {
+// protected:
+//   Iter current;
+// public:
+//   iterator<typename iterator_traits<Iterator>::iterator_category,
+//   typename iterator_traits<Iterator>::value_type,
+//   typename iterator_traits<Iterator>::
diff erence_type,
+//   typename iterator_traits<Iterator>::pointer,
+//   typename iterator_traits<Iterator>::reference> {
+// };
+
+#include <iterator>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+struct find_current
+    : private std::reverse_iterator<It>
+{
+    void test() { (void)this->current; }
+};
+
+template <class It>
+void test() {
+  typedef std::reverse_iterator<It> R;
+  typedef std::iterator_traits<It> T;
+  find_current<It> q; q.test(); // Just test that we can access `.current` from derived classes
+  static_assert((std::is_same<typename R::iterator_type, It>::value), "");
+  static_assert((std::is_same<typename R::value_type, typename T::value_type>::value), "");
+  static_assert((std::is_same<typename R::
diff erence_type, typename T::
diff erence_type>::value), "");
+  static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
+  static_assert((std::is_same<typename R::pointer, typename std::iterator_traits<It>::pointer>::value), "");
+
+#if TEST_STD_VER <= 14
+  typedef std::iterator<typename T::iterator_category, typename T::value_type> iterator_base;
+  static_assert((std::is_base_of<iterator_base, R>::value), "");
+#endif
+#if TEST_STD_VER > 17
+  if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
+    static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
+  } else {
+    static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
+  }
+#else
+  static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
+#endif
+}
+
+#if TEST_STD_VER > 17
+
+struct FooIter {
+  using iterator_category = std::bidirectional_iterator_tag;
+  using value_type = void*;
+  using 
diff erence_type = void*;
+  using pointer = void*;
+  using reference = int&;
+  int& operator*() const;
+};
+template <>
+struct std::indirectly_readable_traits<FooIter> {
+  using value_type = int;
+};
+template <>
+struct std::incrementable_traits<FooIter> {
+  using 
diff erence_type = char;
+};
+
+static_assert(std::is_same_v<typename std::reverse_iterator<FooIter>::value_type, int>);
+static_assert(std::is_same_v<typename std::reverse_iterator<FooIter>::
diff erence_type, char>);
+
+#endif
+
+struct BarIter {
+  bool& operator*() const;
+};
+template <>
+struct std::iterator_traits<BarIter> {
+  using 
diff erence_type = char;
+  using value_type = char;
+  using pointer = char*;
+  using reference = char&;
+  using iterator_category = std::bidirectional_iterator_tag;
+};
+
+#if TEST_STD_VER > 17
+  static_assert(std::is_same_v<typename std::reverse_iterator<BarIter>::reference, bool&>);
+#else
+  static_assert(std::is_same<typename std::reverse_iterator<BarIter>::reference, char&>::value, "");
+#endif
+
+void test_all() {
+  test<bidirectional_iterator<char*> >();
+  test<random_access_iterator<char*> >();
+  test<char*>();
+
+#if TEST_STD_VER > 17
+  test<contiguous_iterator<char*>>();
+  static_assert(std::is_same_v<typename std::reverse_iterator<bidirectional_iterator<char*>>::iterator_concept, std::bidirectional_iterator_tag>);
+  static_assert(std::is_same_v<typename std::reverse_iterator<random_access_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
+  static_assert(std::is_same_v<typename std::reverse_iterator<contiguous_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
+  static_assert(std::is_same_v<typename std::reverse_iterator<char*>::iterator_concept, std::random_access_iterator_tag>);
+#endif
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp
deleted file mode 100644
index 939cf87a5d464..0000000000000
--- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/types.pass.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// reverse_iterator
-
-// Test nested types and data member:
-
-// template <BidirectionalIterator Iter>
-// class reverse_iterator {
-// protected:
-//   Iter current;
-// public:
-//   iterator<typename iterator_traits<Iterator>::iterator_category,
-//   typename iterator_traits<Iterator>::value_type,
-//   typename iterator_traits<Iterator>::
diff erence_type,
-//   typename iterator_traits<Iterator>::pointer,
-//   typename iterator_traits<Iterator>::reference> {
-// };
-
-#include <iterator>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-struct find_current
-    : private std::reverse_iterator<It>
-{
-    void test() { (void)this->current; }
-};
-
-template <class It>
-void
-test()
-{
-    typedef std::reverse_iterator<It> R;
-    typedef std::iterator_traits<It> T;
-    find_current<It> q; q.test(); // Just test that we can access `.current` from derived classes
-    static_assert((std::is_same<typename R::iterator_type, It>::value), "");
-    static_assert((std::is_same<typename R::value_type, typename T::value_type>::value), "");
-    static_assert((std::is_same<typename R::
diff erence_type, typename T::
diff erence_type>::value), "");
-    static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
-    static_assert((std::is_same<typename R::pointer, typename std::iterator_traits<It>::pointer>::value), "");
-
-#if TEST_STD_VER <= 14
-    typedef std::iterator<typename T::iterator_category, typename T::value_type> iterator_base;
-    static_assert((std::is_base_of<iterator_base, R>::value), "");
-#endif
-#if TEST_STD_VER > 17
-    if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
-        static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
-    } else {
-        static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
-    }
-#else
-    static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
-#endif
-}
-
-int main(int, char**)
-{
-    test<bidirectional_iterator<char*> >();
-    test<random_access_iterator<char*> >();
-    test<char*>();
-
-#if TEST_STD_VER > 17
-    test<contiguous_iterator<char*>>();
-    static_assert(std::is_same_v<typename std::reverse_iterator<bidirectional_iterator<char*>>::iterator_concept, std::bidirectional_iterator_tag>);
-    static_assert(std::is_same_v<typename std::reverse_iterator<random_access_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
-    static_assert(std::is_same_v<typename std::reverse_iterator<contiguous_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
-    static_assert(std::is_same_v<typename std::reverse_iterator<char*>::iterator_concept, std::random_access_iterator_tag>);
-#endif
-
-    return 0;
-}


        


More information about the libcxx-commits mailing list