[libcxx-commits] [libcxx] 20a11cb - [libc++] Fix algorithms which use reverse_iterator

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 25 09:35:25 PDT 2022


Author: Nikolas Klauser
Date: 2022-07-25T18:35:20+02:00
New Revision: 20a11cb550ac6f4540bd320c8d8a255da4a9e3a9

URL: https://github.com/llvm/llvm-project/commit/20a11cb550ac6f4540bd320c8d8a255da4a9e3a9
DIFF: https://github.com/llvm/llvm-project/commit/20a11cb550ac6f4540bd320c8d8a255da4a9e3a9.diff

LOG: [libc++] Fix algorithms which use reverse_iterator

This adds a C++20-version of `reverse_iterator` which doesn't SFINAE away the operators for use inside the classic STL algorithms. Pre-C++20 `_AlgRevIter` is just an alias for `reverse_iterator`.

Reviewed By: var-const, #libc

Spies: huixie90, libcxx-commits

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

Added: 
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp
    libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp

Modified: 
    libcxx/include/__algorithm/copy.h
    libcxx/include/__algorithm/copy_backward.h
    libcxx/include/__algorithm/inplace_merge.h
    libcxx/include/__algorithm/ranges_copy_backward.h
    libcxx/include/__iterator/iterator_traits.h
    libcxx/include/__iterator/reverse_iterator.h
    libcxx/test/libcxx/algorithms/robust_against_cpp20_hostile_iterators.compile.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index 886a1ac6ce3e6..5428baa688592 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -58,20 +58,20 @@ pair<_InValueT*, _OutValueT*> __copy_impl(_InValueT* __first, _InValueT* __last,
 
 template <class _InIter, class _OutIter,
           __enable_if_t<is_same<typename remove_const<__iter_value_type<_InIter> >::type, __iter_value_type<_OutIter> >::value
-                      && __is_cpp17_contiguous_iterator<_InIter>::value
-                      && __is_cpp17_contiguous_iterator<_OutIter>::value
-                      && is_trivially_copy_assignable<__iter_value_type<_OutIter> >::value, int> = 0>
+                      && __is_cpp17_contiguous_iterator<typename _InIter::iterator_type>::value
+                      && __is_cpp17_contiguous_iterator<typename _OutIter::iterator_type>::value
+                      && is_trivially_copy_assignable<__iter_value_type<_OutIter> >::value
+                      && __is_reverse_iterator<_InIter>::value
+                      && __is_reverse_iterator<_OutIter>::value, int> = 0>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
-__copy_impl(reverse_iterator<_InIter> __first,
-            reverse_iterator<_InIter> __last,
-            reverse_iterator<_OutIter> __result) {
+pair<_InIter, _OutIter>
+__copy_impl(_InIter __first, _InIter __last, _OutIter __result) {
   auto __first_base = std::__unwrap_iter(__first.base());
   auto __last_base = std::__unwrap_iter(__last.base());
   auto __result_base = std::__unwrap_iter(__result.base());
   auto __result_first = __result_base - (__first_base - __last_base);
   std::__copy_impl(__last_base, __first_base, __result_first);
-  return std::make_pair(__last, reverse_iterator<_OutIter>(std::__rewrap_iter(__result.base(), __result_first)));
+  return std::make_pair(__last, _OutIter(std::__rewrap_iter(__result.base(), __result_first)));
 }
 
 template <class _InIter, class _Sent, class _OutIter,

diff  --git a/libcxx/include/__algorithm/copy_backward.h b/libcxx/include/__algorithm/copy_backward.h
index dd43a91ffa873..26b8c4d791fd3 100644
--- a/libcxx/include/__algorithm/copy_backward.h
+++ b/libcxx/include/__algorithm/copy_backward.h
@@ -10,10 +10,16 @@
 #define _LIBCPP___ALGORITHM_COPY_BACKWARD_H
 
 #include <__algorithm/copy.h>
+#include <__algorithm/iterator_operations.h>
+#include <__algorithm/ranges_copy.h>
 #include <__algorithm/unwrap_iter.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
+#include <__ranges/subrange.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
 #include <cstring>
 #include <type_traits>
 
@@ -23,29 +29,31 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Iter1, class _Sent1, class _Iter2>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-pair<_Iter1, _Iter2> __copy_backward_impl(_Iter1 __first, _Sent1 __last, _Iter2 __result) {
-  auto __ret = std::__copy(reverse_iterator<_Iter1>(__last),
-                           reverse_iterator<_Sent1>(__first),
-                           reverse_iterator<_Iter2>(__result));
-  return pair<_Iter1, _Iter2>(__ret.first.base(), __ret.second.base());
+template <class _AlgPolicy, class _InputIterator, class _OutputIterator,
+          __enable_if_t<is_same<_AlgPolicy, _ClassicAlgPolicy>::value, int> = 0>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 pair<_InputIterator, _OutputIterator>
+__copy_backward(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
+  auto __ret = std::__copy(
+      __unconstrained_reverse_iterator<_InputIterator>(__last),
+      __unconstrained_reverse_iterator<_InputIterator>(__first),
+      __unconstrained_reverse_iterator<_OutputIterator>(__result));
+  return pair<_InputIterator, _OutputIterator>(__ret.first.base(), __ret.second.base());
 }
 
-template <class _Iter1, class _Sent1, class _Iter2>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-pair<_Iter1, _Iter2> __copy_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result) {
-  auto __ret = std::__copy_backward_impl(std::__unwrap_iter(__first),
-                                         std::__unwrap_iter(__last),
-                                         std::__unwrap_iter(__result));
-  return pair<_Iter1, _Iter2>(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second));
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+template <class _AlgPolicy, class _Iter1, class _Sent1, class _Iter2,
+          __enable_if_t<is_same<_AlgPolicy, _RangeAlgPolicy>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr pair<_Iter1, _Iter2> __copy_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result) {
+  auto __reverse_range = std::__reverse_range(std::ranges::subrange(std::move(__first), std::move(__last)));
+  auto __ret           = ranges::copy(std::move(__reverse_range), std::make_reverse_iterator(__result));
+  return std::make_pair(__ret.in.base(), __ret.out.base());
 }
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
 
 template <class _BidirectionalIterator1, class _BidirectionalIterator2>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
-_BidirectionalIterator2
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 _BidirectionalIterator2
 copy_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last, _BidirectionalIterator2 __result) {
-  return std::__copy_backward(__first, __last, __result).second;
+  return std::__copy_backward<_ClassicAlgPolicy>(__first, __last, __result).second;
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h
index f4364969b8f9f..cb662e791872b 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -105,8 +105,8 @@ __buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator
         value_type* __p = __buff;
         for (_BidirectionalIterator __i = __middle; __i != __last; __d.template __incr<value_type>(), (void) ++__i, (void) ++__p)
             ::new ((void*)__p) value_type(_IterOps<_AlgPolicy>::__iter_move(__i));
-        typedef reverse_iterator<_BidirectionalIterator> _RBi;
-        typedef reverse_iterator<value_type*> _Rv;
+        typedef __unconstrained_reverse_iterator<_BidirectionalIterator> _RBi;
+        typedef __unconstrained_reverse_iterator<value_type*> _Rv;
         typedef __invert<_Compare> _Inverted;
         std::__half_inplace_merge<_AlgPolicy, _Inverted>(_Rv(__p), _Rv(__buff),
                                     _RBi(__middle), _RBi(__first),

diff  --git a/libcxx/include/__algorithm/ranges_copy_backward.h b/libcxx/include/__algorithm/ranges_copy_backward.h
index 49c1b26add6d1..673df8025fab6 100644
--- a/libcxx/include/__algorithm/ranges_copy_backward.h
+++ b/libcxx/include/__algorithm/ranges_copy_backward.h
@@ -11,6 +11,7 @@
 
 #include <__algorithm/copy_backward.h>
 #include <__algorithm/in_out_result.h>
+#include <__algorithm/iterator_operations.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__iterator/reverse_iterator.h>
@@ -39,7 +40,7 @@ struct __fn {
     requires indirectly_copyable<_InIter1, _InIter2>
   _LIBCPP_HIDE_FROM_ABI constexpr
   copy_backward_result<_InIter1, _InIter2> operator()(_InIter1 __first, _Sent1 __last, _InIter2 __result) const {
-    auto __ret = std::__copy_backward(std::move(__first), std::move(__last), std::move(__result));
+    auto __ret = std::__copy_backward<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::move(__result));
     return {std::move(__ret.first), std::move(__ret.second)};
   }
 
@@ -47,9 +48,7 @@ struct __fn {
     requires indirectly_copyable<iterator_t<_Range>, _Iter>
   _LIBCPP_HIDE_FROM_ABI constexpr
   copy_backward_result<borrowed_iterator_t<_Range>, _Iter> operator()(_Range&& __r, _Iter __result) const {
-    auto __ret = std::__copy_backward(ranges::begin(__r),
-                                      ranges::end(__r),
-                                      std::move(__result));
+    auto __ret = std::__copy_backward<_RangeAlgPolicy>(ranges::begin(__r), ranges::end(__r), std::move(__result));
     return {std::move(__ret.first), std::move(__ret.second)};
   }
 };

diff  --git a/libcxx/include/__iterator/iterator_traits.h b/libcxx/include/__iterator/iterator_traits.h
index 63525e230add8..254f8c2339e41 100644
--- a/libcxx/include/__iterator/iterator_traits.h
+++ b/libcxx/include/__iterator/iterator_traits.h
@@ -501,6 +501,12 @@ using __iter_to_alloc_type = pair<
     typename add_const<typename iterator_traits<_InputIterator>::value_type::first_type>::type,
     typename iterator_traits<_InputIterator>::value_type::second_type>;
 
+template <class _Iter>
+using __iterator_category_type = typename iterator_traits<_Iter>::iterator_category;
+
+template <class _Iter>
+using __iterator_pointer_type = typename iterator_traits<_Iter>::pointer;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ITERATOR_ITERATOR_TRAITS_H

diff  --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index 7f4ef3c3d503b..5c344c2ee3104 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -197,6 +197,12 @@ _LIBCPP_SUPPRESS_DEPRECATED_POP
 #endif // _LIBCPP_STD_VER > 17
 };
 
+template <class _Iter>
+struct __is_reverse_iterator : false_type {};
+
+template <class _Iter>
+struct __is_reverse_iterator<reverse_iterator<_Iter> > : true_type {};
+
 template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 bool
@@ -327,20 +333,162 @@ reverse_iterator<_Iter> make_reverse_iterator(_Iter __i)
 }
 #endif
 
+#if _LIBCPP_STD_VER <= 17
 template <class _Iter>
-using _ReverseWrapper = reverse_iterator<reverse_iterator<_Iter> >;
+using __unconstrained_reverse_iterator = reverse_iterator<_Iter>;
+#else
 
-template <class _Iter, bool __b>
-struct __unwrap_iter_impl<_ReverseWrapper<_Iter>, __b> {
+// __unconstrained_reverse_iterator allows us to use reverse iterators in the implementation of algorithms by working
+// around a language issue in C++20.
+// In C++20, when a reverse iterator wraps certain C++20-hostile iterators, calling comparison operators on it will
+// result in a compilation error. However, calling comparison operators on the pristine hostile iterator is not
+// an error. Thus, we cannot use reverse_iterators in the implementation of an algorithm that accepts a
+// C++20-hostile iterator. This class is an internal workaround -- it is a copy of reverse_iterator with
+// tweaks to make it support hostile iterators.
+//
+// A C++20-hostile iterator is one that defines a comparison operator where one of the arguments is an exact match
+// and the other requires an implicit conversion, for example:
+//   friend bool operator==(const BaseIter&, const DerivedIter&);
+//
+// C++20 rules for rewriting equality operators create another overload of this function with parameters reversed:
+//   friend bool operator==(const DerivedIter&, const BaseIter&);
+//
+// This creates an ambiguity in overload resolution.
+//
+// Clang treats this ambiguity 
diff erently in 
diff erent contexts. When operator== is actually called in the function
+// body, the code is accepted with a warning. When a concept requires operator== to be a valid expression, however,
+// it evaluates to false. Thus, the implementation of reverse_iterator::operator== can actually call operator== on its
+// base iterators, but the constraints on reverse_iterator::operator== prevent it from being considered during overload
+// resolution. This class simply removes the problematic constraints from comparison functions.
+template <class _Iter>
+class __unconstrained_reverse_iterator {
+  _Iter __iter_;
+
+public:
+  static_assert(__is_cpp17_bidirectional_iterator<_Iter>::value);
+
+  using iterator_type = _Iter;
+  using iterator_category =
+      _If<__is_cpp17_random_access_iterator<_Iter>::value, random_access_iterator_tag, __iterator_category_type<_Iter>>;
+  using pointer = __iterator_pointer_type<_Iter>;
+  using value_type = iter_value_t<_Iter>;
+  using 
diff erence_type = iter_
diff erence_t<_Iter>;
+  using reference = iter_reference_t<_Iter>;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator() = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator(const __unconstrained_reverse_iterator&) = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __unconstrained_reverse_iterator(_Iter __iter) : __iter_(__iter) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() const { return __iter_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr reference operator*() const {
+    auto __tmp = __iter_;
+    return *--__tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr pointer operator->() const {
+    if constexpr (is_pointer_v<_Iter>) {
+      return std::prev(__iter_);
+    } else {
+      return std::prev(__iter_).operator->();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator++() {
+    --__iter_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator++(int) {
+    auto __tmp = *this;
+    --__iter_;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator--() {
+    ++__iter_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator--(int) {
+    auto __tmp = *this;
+    ++__iter_;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator+=(
diff erence_type __n) {
+    __iter_ -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator-=(
diff erence_type __n) {
+    __iter_ += __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator+(
diff erence_type __n) const {
+    return __unconstrained_reverse_iterator(__iter_ - __n);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator-(
diff erence_type __n) const {
+    return __unconstrained_reverse_iterator(__iter_ + __n);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr 
diff erence_type operator-(const __unconstrained_reverse_iterator& __other) const {
+    return __other.__iter_ - __iter_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](
diff erence_type __n) const { return *(*this + __n); }
+
+  // Deliberately unconstrained unlike the comparison functions in `reverse_iterator` -- see the class comment for the
+  // rationale.
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator==(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
+    return __lhs.base() == __rhs.base();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator!=(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
+    return __lhs.base() != __rhs.base();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator<(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
+    return __lhs.base() > __rhs.base();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator>(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
+    return __lhs.base() < __rhs.base();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator<=(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
+    return __lhs.base() >= __rhs.base();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+  operator>=(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
+    return __lhs.base() <= __rhs.base();
+  }
+};
+
+template <class _Iter>
+struct __is_reverse_iterator<__unconstrained_reverse_iterator<_Iter>> : true_type {};
+
+#endif // _LIBCPP_STD_VER <= 17
+
+template <template <class> class _RevIter1, template <class> class _RevIter2, class _Iter>
+struct __unwrap_reverse_iter_impl {
   using _UnwrappedIter = decltype(__unwrap_iter_impl<_Iter>::__unwrap(std::declval<_Iter>()));
+  using _ReverseWrapper = _RevIter1<_RevIter2<_Iter> >;
 
-  static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ReverseWrapper<_Iter>
-  __rewrap(_ReverseWrapper<_Iter> __orig_iter, _UnwrappedIter __unwrapped_iter) {
-    return _ReverseWrapper<_Iter>(
-        reverse_iterator<_Iter>(__unwrap_iter_impl<_Iter>::__rewrap(__orig_iter.base().base(), __unwrapped_iter)));
+  static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ReverseWrapper
+  __rewrap(_ReverseWrapper __orig_iter, _UnwrappedIter __unwrapped_iter) {
+    return _ReverseWrapper(
+        _RevIter2<_Iter>(__unwrap_iter_impl<_Iter>::__rewrap(__orig_iter.base().base(), __unwrapped_iter)));
   }
 
-  static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _UnwrappedIter __unwrap(_ReverseWrapper<_Iter> __i) _NOEXCEPT {
+  static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _UnwrappedIter __unwrap(_ReverseWrapper __i) _NOEXCEPT {
     return __unwrap_iter_impl<_Iter>::__unwrap(__i.base().base());
   }
 };
@@ -355,6 +503,26 @@ _LIBCPP_HIDE_FROM_ABI constexpr ranges::
 }
 #endif
 
+template <class _Iter, bool __b>
+struct __unwrap_iter_impl<reverse_iterator<reverse_iterator<_Iter> >, __b>
+    : __unwrap_reverse_iter_impl<reverse_iterator, reverse_iterator, _Iter> {};
+
+#if _LIBCPP_STD_VER > 17
+
+template <class _Iter, bool __b>
+struct __unwrap_iter_impl<reverse_iterator<__unconstrained_reverse_iterator<_Iter>>, __b>
+    : __unwrap_reverse_iter_impl<reverse_iterator, __unconstrained_reverse_iterator, _Iter> {};
+
+template <class _Iter, bool __b>
+struct __unwrap_iter_impl<__unconstrained_reverse_iterator<reverse_iterator<_Iter>>, __b>
+    : __unwrap_reverse_iter_impl<__unconstrained_reverse_iterator, reverse_iterator, _Iter> {};
+
+template <class _Iter, bool __b>
+struct __unwrap_iter_impl<__unconstrained_reverse_iterator<__unconstrained_reverse_iterator<_Iter>>, __b>
+    : __unwrap_reverse_iter_impl<__unconstrained_reverse_iterator, __unconstrained_reverse_iterator, _Iter> {};
+
+#endif // _LIBCPP_STD_VER > 17
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ITERATOR_REVERSE_ITERATOR_H

diff  --git a/libcxx/test/libcxx/algorithms/robust_against_cpp20_hostile_iterators.compile.pass.cpp b/libcxx/test/libcxx/algorithms/robust_against_cpp20_hostile_iterators.compile.pass.cpp
index fc137e5571d1a..a7ddf22e29aa7 100644
--- a/libcxx/test/libcxx/algorithms/robust_against_cpp20_hostile_iterators.compile.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/robust_against_cpp20_hostile_iterators.compile.pass.cpp
@@ -88,6 +88,7 @@ void test() {
   (void) std::any_of(it, it, pred);
   (void) std::binary_search(it, it, 0);
   (void) std::binary_search(it, it, 0, pred);
+  (void) std::copy_backward(it, it, it);
   (void) std::copy_if(it, it, it, pred);
   (void) std::copy_n(it, 0, it);
   (void) std::copy(it, it, it);
@@ -118,8 +119,8 @@ void test() {
   (void) std::generate(it, it, pred);
   (void) std::includes(it, it, it, it);
   (void) std::includes(it, it, it, it, pred);
-  // (void) std::inplace_merge(it, it, it);
-  // (void) std::inplace_merge(it, it, it, pred);
+  (void) std::inplace_merge(it, it, it);
+  (void) std::inplace_merge(it, it, it, pred);
   (void) std::is_heap_until(it, it);
   (void) std::is_heap_until(it, it, pred);
   (void) std::is_heap(it, it);
@@ -209,7 +210,7 @@ void test() {
   (void) std::sort(it, it);
   (void) std::sort(it, it, pred);
   (void) std::stable_partition(it, it, pred);
-  // (void) std::stable_sort(it, it);
+  (void) std::stable_sort(it, it);
   (void) std::swap_ranges(it, it, it);
   (void) std::transform(it, it, it, pred);
   (void) std::transform(it, it, it, it, pred);

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp
new file mode 100644
index 0000000000000..583e733c07cb0
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <BidirectionalIterator Iter1, BidirectionalIterator Iter2>
+//   requires HasEqualTo<Iter1, Iter2>
+// bool operator==(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr since C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
+    const std::__unconstrained_reverse_iterator<It> r1(l);
+    const std::__unconstrained_reverse_iterator<It> r2(r);
+    assert((r1 == r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s), true);
+    test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+1), false);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
+    test(s, s, true);
+    test(s, s+1, false);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp
new file mode 100644
index 0000000000000..9e908418d0756
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
+//   requires HasGreater<Iter1, Iter2>
+// bool operator>=(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr since C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
+    const std::__unconstrained_reverse_iterator<It> r1(l);
+    const std::__unconstrained_reverse_iterator<It> r2(r);
+    assert((r1 >= r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), false);
+    test(s, s, true);
+    test(s, s+1, true);
+    test(s+1, s, false);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp
new file mode 100644
index 0000000000000..f1afd23bab133
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
+//   requires HasGreater<Iter1, Iter2>
+// bool operator>(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
+    const std::__unconstrained_reverse_iterator<It> r1(l);
+    const std::__unconstrained_reverse_iterator<It> r2(r);
+    assert((r1 > r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), false);
+    test(s, s, false);
+    test(s, s+1, true);
+    test(s+1, s, false);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp
new file mode 100644
index 0000000000000..c710212308fac
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
+//   requires HasGreater<Iter1, Iter2>
+// bool operator<=(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
+    const std::__unconstrained_reverse_iterator<It> r1(l);
+    const std::__unconstrained_reverse_iterator<It> r2(r);
+    assert((r1 <= r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), true);
+    test(s, s, true);
+    test(s, s+1, false);
+    test(s+1, s, true);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp
new file mode 100644
index 0000000000000..ffd3a0323373f
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
+//   requires HasGreater<Iter1, Iter2>
+// bool operator<(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
+    const std::__unconstrained_reverse_iterator<It> r1(l);
+    const std::__unconstrained_reverse_iterator<It> r2(r);
+    assert((r1 < r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), true);
+    test(s, s, false);
+    test(s, s+1, false);
+    test(s+1, s, true);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp
new file mode 100644
index 0000000000000..614f159cc8052
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <BidirectionalIterator Iter1, BidirectionalIterator Iter2>
+//   requires HasEqualTo<Iter1, Iter2>
+// bool operator!=(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
+    const std::__unconstrained_reverse_iterator<It> r1(l);
+    const std::__unconstrained_reverse_iterator<It> r2(r);
+    assert((r1 != r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s), false);
+    test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+1), true);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
+    test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
+    test(s, s, false);
+    test(s, s+1, true);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp
new file mode 100644
index 0000000000000..835e2b65c19c2
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// template <class U>
+//  requires !same_as<U, Iter> && convertible_to<const U&, Iter> && assignable_from<Iter&, const U&>
+// __unconstrained_reverse_iterator& operator=(const __unconstrained_reverse_iterator<U>& u);
+
+#include <iterator>
+
+struct Base { };
+struct Derived : Base { };
+
+void test() {
+    std::__unconstrained_reverse_iterator<Base*> base;
+    std::__unconstrained_reverse_iterator<Derived*> derived;
+    derived = base; // expected-error {{no viable overloaded '='}}
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..66972d7243cc8
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// __unconstrained_reverse_iterator(); // constexpr since C++17
+
+#include <iterator>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test() {
+    std::__unconstrained_reverse_iterator<It> r;
+    (void)r;
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    test<bidirectional_iterator<const char*> >();
+    test<random_access_iterator<char*> >();
+    test<char*>();
+    test<const char*>();
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp
new file mode 100644
index 0000000000000..95626bef2e89b
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// explicit __unconstrained_reverse_iterator(Iter x);
+
+// test explicitness
+
+#include <iterator>
+
+int main(int, char**) {
+    char const* it = "";
+    std::__unconstrained_reverse_iterator<char const*> r = it; // expected-error{{no viable conversion from 'const char *' to 'std::__unconstrained_reverse_iterator<const char *>'}}
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp
new file mode 100644
index 0000000000000..e4d0874d50b5e
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// explicit __unconstrained_reverse_iterator(Iter x); // constexpr since C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    assert(r.base() == i);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char s[] = "123";
+    test(bidirectional_iterator<const char*>(s));
+    test(random_access_iterator<const char*>(s));
+    test(s);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp
new file mode 100644
index 0000000000000..7ea4a61ce6602
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iterator>
+
+// __unconstrained_reverse_iterator
+
+// template <class U>
+//  requires !same_as<U, Iter> && convertible_to<const U&, Iter>
+// __unconstrained_reverse_iterator(const __unconstrained_reverse_iterator<U> &);
+
+#include <iterator>
+
+struct Base { };
+struct Derived : Base { };
+
+void test() {
+    std::__unconstrained_reverse_iterator<Base*> base;
+    std::__unconstrained_reverse_iterator<Derived*> derived(base); // expected-error {{no matching constructor for initialization of 'std::__unconstrained_reverse_iterator<Derived *>'}}
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp
new file mode 100644
index 0000000000000..7fd85c92b3277
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// iterator_type base() const; // constexpr since C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+TEST_CONSTEXPR_CXX17 bool test() {
+    typedef bidirectional_iterator<int*> Iter;
+    int i = 0;
+    Iter iter(&i);
+    std::__unconstrained_reverse_iterator<Iter> const reverse(iter);
+    std::__unconstrained_reverse_iterator<Iter>::iterator_type base = reverse.base();
+    assert(base == Iter(&i));
+    return true;
+}
+
+int main(int, char**) {
+    test();
+#if TEST_STD_VER > 14
+    static_assert(test(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp
new file mode 100644
index 0000000000000..8b61df6d432a0
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp
@@ -0,0 +1,118 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// pointer operator->() const; // constexpr in C++17
+
+// Be sure to respect LWG 198:
+//    http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#198
+// LWG 198 was superseded by LWG 2360
+//    http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2360
+
+
+#include <iterator>
+#include <list>
+#include <cassert>
+
+#include "test_macros.h"
+
+class A
+{
+    int data_;
+public:
+    A() : data_(1) {}
+    ~A() {data_ = -1;}
+
+    int get() const {return data_;}
+
+    friend bool operator==(const A& x, const A& y)
+        {return x.data_ == y.data_;}
+};
+
+template <class It>
+void
+test(It i, typename std::iterator_traits<It>::value_type x)
+{
+    std::__unconstrained_reverse_iterator<It> r(i);
+    assert(r->get() == x.get());
+}
+
+class B
+{
+    int data_;
+public:
+    B(int d=1) : data_(d) {}
+    ~B() {data_ = -1;}
+
+    int get() const {return data_;}
+
+    friend bool operator==(const B& x, const B& y)
+        {return x.data_ == y.data_;}
+    const B *operator&() const { return nullptr; }
+    B       *operator&()       { return nullptr; }
+};
+
+class C
+{
+    int data_;
+public:
+    TEST_CONSTEXPR C() : data_(1) {}
+
+    TEST_CONSTEXPR int get() const {return data_;}
+
+    friend TEST_CONSTEXPR bool operator==(const C& x, const C& y)
+        {return x.data_ == y.data_;}
+};
+
+TEST_CONSTEXPR  C gC;
+
+int main(int, char**)
+{
+  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_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::__unconstrained_reverse_iterator<const C *> RI;
+    constexpr RI it1 = RI(&gC+1);
+
+    static_assert(it1->get() == gC.get(), "");
+  }
+#endif
+  {
+    ((void)gC);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp
new file mode 100644
index 0000000000000..f9beada9e4e64
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// requires RandomAccessIterator<Iter>
+// unspecified operator[](
diff erence_type n) const; // constexpr since C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i,
+                               typename std::iterator_traits<It>::
diff erence_type n,
+                               typename std::iterator_traits<It>::value_type x) {
+    typedef typename std::iterator_traits<It>::value_type value_type;
+    const std::__unconstrained_reverse_iterator<It> r(i);
+    value_type rr = r[n];
+    assert(rr == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s+5), 4, '1');
+    test(random_access_iterator<const char*>(s+5), 0, '5');
+    test(s+5, 4, '1');
+    test(s+5, 0, '5');
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp
new file mode 100644
index 0000000000000..5698d8ddedb9d
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// reference operator*() const; // constexpr in C++17
+
+// Be sure to respect LWG 198:
+//    http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#198
+// LWG 198 was superseded by LWG 2360
+//    http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2360
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+
+class A
+{
+    int data_;
+public:
+    A() : data_(1) {}
+    ~A() {data_ = -1;}
+
+    friend bool operator==(const A& x, const A& y)
+        {return x.data_ == y.data_;}
+};
+
+template <class It>
+void
+test(It i, typename std::iterator_traits<It>::value_type x)
+{
+    std::__unconstrained_reverse_iterator<It> r(i);
+    assert(*r == x);
+}
+
+int main(int, char**)
+{
+    A a;
+    test(&a+1, A());
+
+#if TEST_STD_VER > 14
+    {
+        constexpr const char *p = "123456789";
+        typedef std::__unconstrained_reverse_iterator<const char *> RI;
+        constexpr RI it1 = RI(p+1);
+        constexpr RI it2 = RI(p+2);
+        static_assert(*it1 == p[0], "");
+        static_assert(*it2 == p[1], "");
+    }
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp
new file mode 100644
index 0000000000000..48be8a7399e42
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// requires RandomAccessIterator<Iter>
+// __unconstrained_reverse_iterator& operator-=(
diff erence_type n); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::
diff erence_type n, It x) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It>& rr = r -= n;
+    assert(r.base() == x);
+    assert(&rr == &r);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s+10));
+    test(s+5, 5, s+10);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp
new file mode 100644
index 0000000000000..115d95e1485cd
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// requires RandomAccessIterator<Iter>
+// __unconstrained_reverse_iterator& operator+=(
diff erence_type n); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::
diff erence_type n, It x) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It>& rr = r += n;
+    assert(r.base() == x);
+    assert(&rr == &r);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    char const* s = "1234567890";
+    test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s));
+    test(s+5, 5, s);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp
new file mode 100644
index 0000000000000..c3a4d1fd9e36f
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// requires RandomAccessIterator<Iter>
+// __unconstrained_reverse_iterator operator-(
diff erence_type n) const; // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::
diff erence_type n, It x) {
+    const std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It> rr = r - n;
+    assert(rr.base() == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s+10));
+    test(s+5, 5, s+10);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp
new file mode 100644
index 0000000000000..164c5abe8a353
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// requires RandomAccessIterator<Iter>
+// __unconstrained_reverse_iterator operator+(
diff erence_type n) const; // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::
diff erence_type n, It x) {
+    const std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It> rr = r + n;
+    assert(rr.base() == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "1234567890";
+    test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s));
+    test(s+5, 5, s);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp
new file mode 100644
index 0000000000000..3220c1f9b1eb1
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// __unconstrained_reverse_iterator operator--(int); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, It x) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It> rr = r--;
+    assert(r.base() == x);
+    assert(rr.base() == i);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "123";
+    test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s+2));
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s+2));
+    test(s+1, s+2);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp
new file mode 100644
index 0000000000000..47477fe89545b
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// __unconstrained_reverse_iterator operator++(int); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, It x) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It> rr = r++;
+    assert(r.base() == x);
+    assert(rr.base() == i);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "123";
+    test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s));
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s));
+    test(s+1, s);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp
new file mode 100644
index 0000000000000..6ad41aeaf17a2
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// __unconstrained_reverse_iterator& operator--(); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, It x) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It>& rr = --r;
+    assert(r.base() == x);
+    assert(&rr == &r);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "123";
+    test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s+2));
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s+2));
+    test(s+1, s+2);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp
new file mode 100644
index 0000000000000..9c7e5b41738e9
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// __unconstrained_reverse_iterator& operator++(); // constexpr in C++17
+
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class It>
+TEST_CONSTEXPR_CXX17 void test(It i, It x) {
+    std::__unconstrained_reverse_iterator<It> r(i);
+    std::__unconstrained_reverse_iterator<It>& rr = ++r;
+    assert(r.base() == x);
+    assert(&rr == &r);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    const char* s = "123";
+    test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s));
+    test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s));
+    test(s+1, s);
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp
new file mode 100644
index 0000000000000..632e2655dea07
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.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
+//
+//===----------------------------------------------------------------------===//
+
+// <iterator>
+
+// __unconstrained_reverse_iterator
+
+// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
+//   requires HasMinus<Iter2, Iter1>
+// auto operator-(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y) // constexpr in C++17
+//  -> decltype(y.base() - x.base());
+
+#include <iterator>
+#include <cstddef>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+template <class, class, class = void> struct HasMinus : std::false_type {};
+template <class R1, class R2> struct HasMinus<R1, R2, decltype((R1() - R2(), void()))> : std::true_type {};
+
+template <class It1, class It2>
+TEST_CONSTEXPR_CXX17 void test(It1 l, It2 r, std::ptr
diff _t x) {
+    const std::__unconstrained_reverse_iterator<It1> r1(l);
+    const std::__unconstrained_reverse_iterator<It2> r2(r);
+    assert((r1 - r2) == x);
+}
+
+TEST_CONSTEXPR_CXX17 bool tests() {
+    char s[3] = {0};
+
+    // Test same base iterator type
+    test(s, s, 0);
+    test(s, s+1, 1);
+    test(s+1, s, -1);
+
+    // Test non-subtractable base iterator types
+    static_assert( HasMinus<std::__unconstrained_reverse_iterator<int*>, std::__unconstrained_reverse_iterator<int*> >::value, "");
+#if TEST_STD_VER >= 11
+    static_assert(!HasMinus<std::__unconstrained_reverse_iterator<int*>, std::__unconstrained_reverse_iterator<char*> >::value, "");
+#endif
+
+    return true;
+}
+
+int main(int, char**) {
+    tests();
+#if TEST_STD_VER > 14
+    static_assert(tests(), "");
+#endif
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp
new file mode 100644
index 0000000000000..f8ffef364f37a
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// __unconstrained_reverse_iterator
+
+// Test nested types and data member:
+
+// template <BidirectionalIterator Iter>
+// class __unconstrained_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>
+void test() {
+  typedef std::__unconstrained_reverse_iterator<It> R;
+  typedef std::iterator_traits<It> T;
+  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;
+};
+
+// Not using `FooIter::value_type`.
+static_assert(std::is_same_v<typename std::__unconstrained_reverse_iterator<FooIter>::value_type, int>);
+// Not using `FooIter::
diff erence_type`.
+static_assert(std::is_same_v<typename std::__unconstrained_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::__unconstrained_reverse_iterator<BarIter>::reference, bool&>);
+#else
+  static_assert(std::is_same<typename std::__unconstrained_reverse_iterator<BarIter>::reference, char&>::value, "");
+#endif
+
+void test_all() {
+  test<bidirectional_iterator<char*> >();
+  test<random_access_iterator<char*> >();
+  test<char*>();
+}

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
index 0d3ccf6ab2ffc..b75eced30b793 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
@@ -56,7 +56,7 @@ static_assert(!HasCopyBackwardR<InputRangeNotSentinelEqualityComparableWith, int
 
 static_assert(std::is_same_v<std::ranges::copy_result<int, long>, std::ranges::in_out_result<int, long>>);
 
-template <class In, class Out, class Sent = In>
+template <class In, class Out, class Sent>
 constexpr void test_iterators() {
   { // simple test
     {
@@ -100,18 +100,25 @@ constexpr void test_iterators() {
   }
 }
 
+template <class InIter, class OutIter>
+constexpr void test_sentinels() {
+  test_iterators<InIter, OutIter, InIter>();
+  test_iterators<InIter, OutIter, sentinel_wrapper<InIter>>();
+  test_iterators<InIter, OutIter, sized_sentinel<InIter>>();
+}
+
 template <class Out>
 constexpr void test_in_iterators() {
-  test_iterators<bidirectional_iterator<int*>, Out>();
-  test_iterators<random_access_iterator<int*>, Out>();
-  test_iterators<contiguous_iterator<int*>, Out>();
+  test_sentinels<bidirectional_iterator<int*>, Out>();
+  test_sentinels<random_access_iterator<int*>, Out>();
+  test_sentinels<contiguous_iterator<int*>, Out>();
 }
 
 template <class Out>
 constexpr void test_proxy_in_iterators() {
-  test_iterators<ProxyIterator<bidirectional_iterator<int*>>, Out>();
-  test_iterators<ProxyIterator<random_access_iterator<int*>>, Out>();
-  test_iterators<ProxyIterator<contiguous_iterator<int*>>, Out>();
+  test_sentinels<ProxyIterator<bidirectional_iterator<int*>>, Out>();
+  test_sentinels<ProxyIterator<random_access_iterator<int*>>, Out>();
+  test_sentinels<ProxyIterator<contiguous_iterator<int*>>, Out>();
 }
 
 constexpr bool test() {


        


More information about the libcxx-commits mailing list