[libcxx-commits] [libcxx] [libc++][ranges] Implement `ranges::stride_view`. (PR #65200)

Will Hawkins via libcxx-commits libcxx-commits at lists.llvm.org
Wed Oct 4 16:44:51 PDT 2023


https://github.com/hawkinsw updated https://github.com/llvm/llvm-project/pull/65200

>From 3273c43c57549056aa9661e8dff61adf4e07a8fb Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Tue, 8 Aug 2023 01:04:17 -0400
Subject: [PATCH 01/13] WIP: [libc++][ranges] Implement `ranges::stride_view`.

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

Signed-off-by: Will Hawkins <hawkinsw at obs.cr>
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__ranges/stride_view.h         | 421 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/include/ranges                         |   1 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std/ranges.inc                 |   9 +-
 .../ranges.version.compile.pass.cpp           |  31 ++
 .../version.version.compile.pass.cpp          |  31 ++
 .../range.stride.view/adaptor.pass.cpp        | 193 ++++++++
 .../range.stride.view/base.pass.cpp           |  70 +++
 .../range.stride.view/begin.pass.cpp          |  22 +
 .../range.stride.view/ctad.compile.pass.cpp   |  22 +
 .../enable_borrowed_range.compile.pass.cpp    |  32 ++
 .../range.stride.view/end.pass.cpp            |  22 +
 .../iterator/ctor.default.pass.cpp            |  49 ++
 .../range.stride.view/iterator/equal.pass.cpp |  89 ++++
 .../sentinel/ctor.default.pass.cpp            |  22 +
 .../range.stride.view/sentinel/equal.pass.cpp |  22 +
 .../range.stride.view/size.pass.cpp           |  46 ++
 .../range.adaptors/range.stride.view/test.h   | 183 ++++++++
 .../generate_feature_test_macro_components.py |   5 +
 22 files changed, 1275 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__ranges/stride_view.h
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index e1b4172b22c53da..aff2ee340901193 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -358,6 +358,8 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_starts_ends_with``               *unimplemented*
     --------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_stride``                         ``202207L``
+    --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_to_container``                   ``202202L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_zip``                            *unimplemented*
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 7af9b85b974211f..163f6fc6dc9ecef 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -636,6 +636,7 @@ set(files
   __ranges/single_view.h
   __ranges/size.h
   __ranges/split_view.h
+  __ranges/stride_view.h
   __ranges/subrange.h
   __ranges/take_view.h
   __ranges/take_while_view.h
diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
new file mode 100644
index 000000000000000..880c8ce2950e470
--- /dev/null
+++ b/libcxx/include/__ranges/stride_view.h
@@ -0,0 +1,421 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_STRIDE_VIEW_H
+#define _LIBCPP___RANGES_STRIDE_VIEW_H
+
+#include <__config>
+
+#include <__compare/three_way_comparable.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
+#include <__iterator/reverse_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/subrange.h>
+#include <__ranges/view_interface.h>
+#include <__ranges/views.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/maybe_const.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Value>
+_LIBCPP_HIDE_FROM_ABI constexpr _Value __div_ceil(_Value __left, _Value __right) {
+  _Value __r = __left / __right;
+  if (__left % __right) {
+    __r++;
+  }
+  return __r;
+}
+
+template <input_range _View>
+  requires view<_View>
+class stride_view : public view_interface<stride_view<_View>> {
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+  range_difference_t<_View> __stride_     = 0;
+
+  template <bool _Const>
+  class __iterator;
+  template <bool _Const>
+  class __sentinel;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride)
+      : __base_(std::move(__base)), __stride_(__stride) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+    requires copy_constructible<_View>
+  {
+    return __base_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto stride() { return __stride_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires ranges::sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires ranges::sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>{*this, ranges::begin(__base_)};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires ranges::range<const _View>
+  {
+    return __iterator<true>{*this, ranges::begin(__base_)};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View> && common_range<_View> && sized_range<_View> && forward_range<_View>)
+  {
+    auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
+    return __iterator<false>{*this, ranges::end(__base_), __missing};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View> && common_range<_View> && !bidirectional_range<_View>)
+  {
+    return __iterator<false>{*this, ranges::end(__base_)};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    return std::default_sentinel;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires(range<const _View> && common_range<const _View> && sized_range<const _View> && forward_range<const _View>)
+  {
+    auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
+    return __iterator<true>{*this, ranges::end(__base_), __missing};
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires(range<const _View> && common_range<_View> && !bidirectional_range<_View>)
+  {
+    return __iterator<true>{*this, ranges::end(__base_)};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires(range<const _View>)
+  {
+    return std::default_sentinel;
+  }
+}; // class stride_view
+
+template <class _View>
+struct __stride_iterator_category {};
+
+template <forward_range _View>
+struct __stride_iterator_category<_View> {
+  using _Cat = typename iterator_traits<iterator_t<_View>>::iterator_category;
+  using iterator_category =
+      _If<derived_from<_Cat, random_access_iterator_tag>,
+          random_access_iterator_tag,
+          /* else */ _Cat >;
+};
+
+template <input_range _View>
+  requires view<_View>
+template <bool _Const>
+class stride_view<_View>::__iterator : public __stride_iterator_category<_View> {
+  using _Parent = __maybe_const<_Const, stride_view<_View>>;
+  using _Base   = __maybe_const<_Const, _View>;
+
+public:
+  using difference_type = range_difference_t<_Base>;
+  using value_type      = range_value_t<_Base>;
+  using iterator_concept =
+      _If<random_access_range<_Base>,
+          random_access_iterator_tag,
+          _If<bidirectional_range<_Base>,
+              bidirectional_iterator_tag,
+              _If<forward_range<_Base>,
+                  forward_iterator_tag,
+                  /* else */ input_iterator_tag >>>;
+
+  _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_     = iterator_t<_Base>();
+  _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>();
+  difference_type __stride_                                  = 0;
+  difference_type __missing_                                 = 0;
+
+  // using iterator_category = inherited;
+  _LIBCPP_HIDE_FROM_ABI __iterator()
+    requires default_initializable<iterator_t<_Base>>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && std::convertible_to<ranges::iterator_t<_View>, ranges::iterator_t<_Base>> &&
+                 std::convertible_to<ranges::sentinel_t<_View>, ranges::sentinel_t<_Base>>
+      : __current_(std::move(__i.__current_)),
+        __end_(std::move(__i.__end_)),
+        __stride_(__i.__stride_),
+        __missing_(__i.__missing_) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent& __parent, ranges::iterator_t<_Base> __current, difference_type __missing = 0)
+      : __current_(std::move(__current)),
+        __end_(ranges::end(__parent.__base_)),
+        __stride_(__parent.__stride_),
+        __missing_(__missing) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__current_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    __missing_ = ranges::advance(__current_, __stride_, __end_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+    requires forward_range<_Base>
+  {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    ranges::advance(__current_, __missing_ - __stride_);
+    __missing_ = 0;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+    requires bidirectional_range<_Base>
+  {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __s)
+    requires random_access_range<_Base>
+  {
+    if (__s > 0) {
+      ranges::advance(__current_, __stride_ * (__s - 1));
+      __missing_ = ranges::advance(__current_, __stride_, __end_);
+    } else if (__s < 0) {
+      ranges::advance(__current_, __stride_ * __s + __missing_);
+      __missing_ = 0;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __s)
+    requires random_access_range<_Base>
+  {
+    return *this += -__s;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator[](difference_type __s) const
+    requires random_access_range<_Base>
+  {
+    return *(*this + __s);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) {
+    return __x.__current_ == __x.__end_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __iterator const& __y)
+    requires equality_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ == __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(__iterator const& __x, __iterator const& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(__iterator const& __x, __iterator const& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator const& __i, difference_type __s)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __s;
+    return __r;
+  }
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __s, __iterator const& __i)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __s;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator const& __i, difference_type __s)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __s;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(__iterator const& __x, __iterator const& __y)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>> && forward_range<_Base>
+  {
+    auto __n = __x.__current_ - __y.__current_;
+    return (__n + __x.__missing_ - __y.__missing_) / __x.__stride_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(__iterator const& __x, __iterator const& __y)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    auto __n = __x.__current_ - __y.__current_;
+    if (__n < 0) {
+      return -ranges::__div_ceil(-__n, __x.__stride_);
+    }
+    return ranges::__div_ceil(__n, __x.__stride_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(default_sentinel_t, __iterator const& __x)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_);
+  }
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(__iterator const& __x, default_sentinel_t __y)
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+  {
+    return -(__y - __x);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base>
+  iter_move(__iterator const& __it) noexcept(noexcept(ranges::iter_move(__it.__current_))) {
+    return ranges::iter_move(__it.__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void
+  iter_swap(__iterator const& __x,
+            __iterator const& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_)))
+    requires indirectly_swappable<iterator_t<_Base>>
+  {
+    return ranges::iter_swap(__x.__current_, __y.__current_);
+  }
+}; // class stride_view::__iterator
+
+template <input_range _View>
+  requires view<_View>
+template <bool _Const>
+class stride_view<_View>::__sentinel {
+public:
+  sentinel_t<_View> __end_ = sentinel_t<_View>();
+
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(stride_view<_View>& __parent)
+      : __end_(ranges::end(__parent.__base_)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator<true> const& __x, __sentinel const& __y) {
+    return __x.__current_ == __y.__end_;
+  }
+}; // class stride_view::__sentinel
+
+template <class _Range>
+stride_view(_Range&&) -> stride_view<views::all_t<_Range>>;
+
+namespace views {
+namespace __stride {
+// removed this.
+struct __fn {
+  template <viewable_range _Range>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const
+      noexcept(noexcept(stride_view{std::forward<_Range>(__range), __n}))
+          -> decltype(stride_view{std::forward<_Range>(__range), __n}) {
+    return stride_view{std::forward<_Range>(__range), __n};
+  }
+
+  template <class _Np>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Np&& __n) const {
+    return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n)));
+  }
+};
+} // namespace __stride
+
+inline namespace __cpo {
+inline constexpr auto stride = __stride::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_STRIDE_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index e349386e0fba133..97b1d27db3337d7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1720,6 +1720,7 @@ module std_private_ranges_size                       [system] {
   export std_private_type_traits_make_unsigned
 }
 module std_private_ranges_split_view                 [system] { header "__ranges/split_view.h" }
+module std_private_ranges_stride_view                 [system] { header "__ranges/stride_view.h" }
 module std_private_ranges_subrange                   [system] {
   header "__ranges/subrange.h"
   export std_private_ranges_subrange_fwd
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index db592fd5cb360c4..1cbcfd81e0353ab 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -406,6 +406,7 @@ namespace std {
 #include <__ranges/single_view.h>
 #include <__ranges/size.h>
 #include <__ranges/split_view.h>
+#include <__ranges/stride_view.h>
 #include <__ranges/subrange.h>
 #include <__ranges/take_view.h>
 #include <__ranges/take_while_view.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index e5a995366a7aa48..4ee6115353e777b 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -161,6 +161,7 @@ __cpp_lib_ranges_join_with                              202202L <ranges>
 __cpp_lib_ranges_repeat                                 202207L <ranges>
 __cpp_lib_ranges_slide                                  202202L <ranges>
 __cpp_lib_ranges_starts_ends_with                       202106L <algorithm>
+__cpp_lib_ranges_stride                                 202207L <ranges>
 __cpp_lib_ranges_to_container                           202202L <deque> <forward_list> <list>
                                                                 <map> <queue> <ranges>
                                                                 <set> <stack> <string>
@@ -441,6 +442,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_ranges_repeat                        202207L
 // # define __cpp_lib_ranges_slide                         202202L
 // # define __cpp_lib_ranges_starts_ends_with              202106L
+# define __cpp_lib_ranges_stride                        202207L
 # define __cpp_lib_ranges_to_container                  202202L
 // # define __cpp_lib_ranges_zip                           202110L
 // # define __cpp_lib_reference_from_temporary             202202L
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index a883103d812588b..0f418971c700da7 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -276,6 +276,13 @@ export namespace std {
     } // namespace views
 #endif // _LIBCPP_STD_VER >= 23
 
+    // [range.stride], stride view
+    using std::ranges::stride_view;
+
+    namespace views {
+      using std::ranges::views::stride;
+    }
+
 #if 0
     // [range.zip.transform], zip transform view
     using std::ranges::zip_transform_view;
@@ -322,7 +329,6 @@ export namespace std {
     }
 #endif // _LIBCPP_STD_VER >= 23
 
-#if 0
     // [range.stride], stride view
     using std::ranges::stride_view;
 
@@ -330,6 +336,7 @@ export namespace std {
       using std::ranges::views::stride;
     }
 
+#if 0
     using std::ranges::cartesian_product_view;
 
     namespace views {
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 582e0c7dfe5e65d..ce73f5955649ddf 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -23,6 +23,7 @@
     __cpp_lib_ranges_join_with       202202L [C++23]
     __cpp_lib_ranges_repeat          202207L [C++23]
     __cpp_lib_ranges_slide           202202L [C++23]
+    __cpp_lib_ranges_stride          202207L [C++23]
     __cpp_lib_ranges_to_container    202202L [C++23]
     __cpp_lib_ranges_zip             202110L [C++23]
 */
@@ -60,6 +61,10 @@
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -98,6 +103,10 @@
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -136,6 +145,10 @@
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -177,6 +190,10 @@
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -254,6 +271,13 @@
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+#   error "__cpp_lib_ranges_stride should have the value 202207L in c++23"
+# endif
+
 # ifndef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should be defined in c++23"
 # endif
@@ -343,6 +367,13 @@
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+#   error "__cpp_lib_ranges_stride should have the value 202207L in c++26"
+# endif
+
 # ifndef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should be defined in c++26"
 # endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 566c595d8c30823..2fcfd6ad90ea78f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -149,6 +149,7 @@
     __cpp_lib_ranges_repeat                          202207L [C++23]
     __cpp_lib_ranges_slide                           202202L [C++23]
     __cpp_lib_ranges_starts_ends_with                202106L [C++23]
+    __cpp_lib_ranges_stride                          202207L [C++23]
     __cpp_lib_ranges_to_container                    202202L [C++23]
     __cpp_lib_ranges_zip                             202110L [C++23]
     __cpp_lib_ratio                                  202306L [C++26]
@@ -721,6 +722,10 @@
 #   error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -1479,6 +1484,10 @@
 #   error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -2408,6 +2417,10 @@
 #   error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -3607,6 +3620,10 @@
 #   error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should not be defined before c++23"
 # endif
@@ -5037,6 +5054,13 @@
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+#   error "__cpp_lib_ranges_stride should have the value 202207L in c++23"
+# endif
+
 # ifndef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should be defined in c++23"
 # endif
@@ -6578,6 +6602,13 @@
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_stride
+#   error "__cpp_lib_ranges_stride should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_stride != 202207L
+#   error "__cpp_lib_ranges_stride should have the value 202207L in c++26"
+# endif
+
 # ifndef __cpp_lib_ranges_to_container
 #   error "__cpp_lib_ranges_to_container should be defined in c++26"
 # endif
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
new file mode 100644
index 000000000000000..8ad758a953426dd
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -0,0 +1,193 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <iterator>
+#include <ranges>
+#include <utility>
+
+template <class View, class T>
+concept CanBePiped = requires(View&& view, T&& t) {
+  { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+constexpr bool test() {
+  int arr[] = {1, 2, 3};
+
+  // Simple use cases.
+  {
+    {
+      BidirRange view(arr, arr + 3);
+      std::ranges::stride_view<BidirRange> strided(view, 1);
+      auto strided_iter = strided.begin();
+
+      assert(*strided_iter == arr[0]);
+
+      std::ranges::advance(strided_iter, 2);
+      assert(*strided_iter == arr[2]);
+    }
+    {
+      BidirRange view(arr, arr + 3);
+      std::ranges::stride_view<BidirRange> strided(view, 2);
+      auto strided_iter = strided.begin();
+
+      assert(*strided_iter == arr[0]);
+
+      std::ranges::advance(strided_iter, 1);
+      assert(*strided_iter == arr[2]);
+    }
+  }
+
+#if 0
+  // views::reverse(x) is equivalent to subrange{end, begin, size} if x is a
+  // sized subrange over reverse iterators
+  {
+    using It = bidirectional_iterator<int*>;
+    using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::sized>;
+
+    using ReverseIt = std::reverse_iterator<It>;
+    using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::sized>;
+
+    {
+      BidirRange view(buf, buf + 3);
+      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
+      std::same_as<Subrange> auto result = std::views::reverse(subrange);
+      assert(base(result.begin()) == buf);
+      assert(base(result.end()) == buf + 3);
+    }
+    {
+      // std::move into views::reverse
+      BidirRange view(buf, buf + 3);
+      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
+      std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
+      assert(base(result.begin()) == buf);
+      assert(base(result.end()) == buf + 3);
+    }
+    {
+      // with a const subrange
+      BidirRange view(buf, buf + 3);
+      ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
+      std::same_as<Subrange> auto result = std::views::reverse(subrange);
+      assert(base(result.begin()) == buf);
+      assert(base(result.end()) == buf + 3);
+    }
+  }
+
+  // views::reverse(x) is equivalent to subrange{end, begin} if x is an
+  // unsized subrange over reverse iterators
+  {
+    using It = bidirectional_iterator<int*>;
+    using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::unsized>;
+
+    using ReverseIt = std::reverse_iterator<It>;
+    using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::unsized>;
+
+    {
+      BidirRange view(buf, buf + 3);
+      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
+      std::same_as<Subrange> auto result = std::views::reverse(subrange);
+      assert(base(result.begin()) == buf);
+      assert(base(result.end()) == buf + 3);
+    }
+    {
+      // std::move into views::reverse
+      BidirRange view(buf, buf + 3);
+      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
+      std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
+      assert(base(result.begin()) == buf);
+      assert(base(result.end()) == buf + 3);
+    }
+    {
+      // with a const subrange
+      BidirRange view(buf, buf + 3);
+      ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
+      std::same_as<Subrange> auto result = std::views::reverse(subrange);
+      assert(base(result.begin()) == buf);
+      assert(base(result.end()) == buf + 3);
+    }
+  }
+
+  // Otherwise, views::reverse(x) is equivalent to ranges::reverse_view{x}
+  {
+    BidirRange view(buf, buf + 3);
+    std::same_as<std::ranges::reverse_view<BidirRange>> auto result = std::views::reverse(view);
+    assert(base(result.begin().base()) == buf + 3);
+    assert(base(result.end().base()) == buf);
+  }
+
+  // Test that std::views::reverse is a range adaptor
+  {
+    // Test `v | views::reverse`
+    {
+      BidirRange view(buf, buf + 3);
+      std::same_as<std::ranges::reverse_view<BidirRange>> auto result = view | std::views::reverse;
+      assert(base(result.begin().base()) == buf + 3);
+      assert(base(result.end().base()) == buf);
+    }
+
+    // Test `adaptor | views::reverse`
+    {
+      BidirRange view(buf, buf + 3);
+      auto f = [](int i) { return i; };
+      auto const partial = std::views::transform(f) | std::views::reverse;
+      using Result = std::ranges::reverse_view<std::ranges::transform_view<BidirRange, decltype(f)>>;
+      std::same_as<Result> auto result = partial(view);
+      assert(base(result.begin().base().base()) == buf + 3);
+      assert(base(result.end().base().base()) == buf);
+    }
+
+    // Test `views::reverse | adaptor`
+    {
+      BidirRange view(buf, buf + 3);
+      auto f = [](int i) { return i; };
+      auto const partial = std::views::reverse | std::views::transform(f);
+      using Result = std::ranges::transform_view<std::ranges::reverse_view<BidirRange>, decltype(f)>;
+      std::same_as<Result> auto result = partial(view);
+      assert(base(result.begin().base().base()) == buf + 3);
+      assert(base(result.end().base().base()) == buf);
+    }
+  }
+#endif // big block
+
+  // From:
+  // Test that std::views::reverse is a range adaptor
+  // Check SFINAE friendliness
+  {
+    struct NotAViewableRange {};
+    struct NotABidirRange {};
+    // Not invocable because there is no parameter.
+    static_assert(!std::is_invocable_v<decltype(std::views::stride)>);
+    // Not invocable because NotAViewableRange is, well, not a viewable range.
+    static_assert(!std::is_invocable_v<decltype(std::views::reverse), NotAViewableRange>);
+    // Is invocable because BidirRange is a viewable range.
+    static_assert(std::is_invocable_v<decltype(std::views::reverse), BidirRange>);
+
+    // Make sure that pipe operations work!
+    static_assert(CanBePiped<BidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+    static_assert(CanBePiped<BidirRange&, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+    static_assert(
+        !CanBePiped<NotABidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+  }
+  // A final sanity check.
+  { static_assert(std::same_as<decltype(std::views::stride), decltype(std::ranges::views::stride)>); }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
new file mode 100644
index 000000000000000..badfd532453158c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <cassert>
+#include <ranges>
+
+template <typename T>
+concept can_call_base_on = requires(T t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+  int buff[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  // Check the const& overload
+  {
+    Range range(buff, buff + 8);
+    std::ranges::stride_view<Range<int>> const view(range, 3);
+    std::same_as<Range<int>> decltype(auto) result = view.base();
+    assert(result.wasCopyInitialized);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 8);
+  }
+
+  // Check the && overload
+  {
+    Range range(buff, buff + 8);
+    std::ranges::stride_view<Range<int>> view(range, 3);
+    std::same_as<Range<int>> decltype(auto) result = std::move(view).base();
+    assert(result.wasMoveInitialized);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 8);
+  }
+
+  // Check the && overload (again)
+  {
+    Range range(buff, buff + 8);
+    std::same_as<Range<int>> decltype(auto) result = std::ranges::stride_view<Range<int>>(range, 3).base();
+    assert(result.wasMoveInitialized);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 8);
+  }
+
+  // Ensure the const& overload is not considered when the base is not copy-constructible
+  {
+    static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyRange> const&>);
+    static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyRange>&>);
+    static_assert(can_call_base_on<std::ranges::stride_view<NoCopyRange>&&>);
+    static_assert(can_call_base_on<std::ranges::stride_view<NoCopyRange>>);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
new file mode 100644
index 000000000000000..646a9423f4523c7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <ranges>
+
+constexpr bool test() {
+  using std::ranges::enable_borrowed_range;
+  // Make sure that a stride_view over neither a borrowable nor an unborrowable view
+  // is itself borrowable.
+  static_assert(!enable_borrowed_range<std::ranges::stride_view<Range<int>>>);
+  static_assert(!enable_borrowed_range<std::ranges::stride_view<BorrowedRange<int>>>);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..cfc38b32926805a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "../test.h"
+#include <cassert>
+#include <ranges>
+
+bool non_simple_view_iter_ctor_test() {
+  using StrideView             = std::ranges::stride_view<NotSimpleView>;
+  using StrideViewIterNonConst = std::ranges::iterator_t<StrideView>;
+  using StrideViewIterConst    = std::ranges::iterator_t<const StrideView>;
+
+  StrideView sv{NotSimpleView{}, 1};
+  StrideViewIterNonConst iter = {sv, sv.base().begin(), 0};
+  StrideViewIterConst iterb   = {iter};
+  assert(iterb.__end_.moved_from_a == true);
+  return true;
+}
+
+constexpr bool simpleview_iter_ctor_test() {
+  using StrideView     = std::ranges::stride_view<ForwardTracedMoveView>;
+  using StrideViewIter = std::ranges::iterator_t<StrideView>;
+
+  StrideView sv{ForwardTracedMoveView{}, 1};
+  StrideViewIter iter = {sv, sv.base().begin(), 0};
+  // Guarantee that when the iterator is given to the constructor that
+  // it is moved there.
+  assert(iter.base().moved);
+
+  return true;
+}
+
+int main(int, char**) {
+  simpleview_iter_ctor_test();
+  non_simple_view_iter_ctor_test();
+  static_assert(simpleview_iter_ctor_test());
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
new file mode 100644
index 000000000000000..7f2711adc5179db
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include <cassert>
+#include <ranges>
+
+#include "test_iterators.h"
+
+template <class Iter>
+constexpr void testOne() {
+  using Range      = std::ranges::subrange<Iter>;
+  using StrideView = std::ranges::stride_view<Range>;
+  static_assert(std::ranges::common_range<StrideView>);
+
+  {
+    // simple test
+    {
+      int buffer[] = {0, 1, 2, -1, 4, 5, 6};
+      Range input(Iter{buffer}, Iter{buffer + 7});
+      StrideView sv(input, 1);
+      StrideView sv_too(input, 2);
+      auto b = sv.begin(), e = sv.end();
+      auto b_too = sv_too.begin();
+
+      assert(b == b);
+      assert(!(b != b));
+
+      assert(e == e);
+      assert(!(e != e));
+
+      assert(!(b == e));
+      assert(b != e);
+
+      std::advance(b, 8);
+      std::advance(b_too, 4);
+
+      assert(b == b_too);
+      assert(!(b != b_too));
+
+      assert(b == b);
+      assert(!(b != b));
+
+      assert(e == e);
+      assert(!(e != e));
+
+      assert(b == e);
+      assert(!(b != e));
+    }
+
+    // Default-constructed iterators compare equal.
+    {
+      int buffer[] = {0, 1, 2, -1, 4, 5, 6};
+      Range input(Iter{buffer}, Iter{buffer + 7});
+      std::ranges::stride_view sv(input, 1);
+      using StrideViewIter = decltype(sv.begin());
+      StrideViewIter i1, i2;
+      assert(i1 == i2);
+      assert(!(i1 != i2));
+    }
+  }
+}
+
+constexpr bool test() {
+  testOne<forward_iterator<int*>>();
+  //testOne<bidirectional_iterator<int*>>();
+  //testOne<random_access_iterator<int*>>();
+  //testOne<contiguous_iterator<int*>>();
+  testOne<int*>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp
new file mode 100644
index 000000000000000..68556f32f875b12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/sentinel/equal.pass.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
new file mode 100644
index 000000000000000..981395f9e2c3290
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.pass.cpp
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::ranges::stride_view
+
+#include <cassert>
+#include <ranges>
+
+bool runtime_test() {
+  auto iot    = std::views::iota(1, 22);
+  auto str    = std::views::stride(iot, 3);
+  auto result = str.size();
+  assert(result == 7);
+  return true;
+}
+
+constexpr bool test() {
+  {
+    constexpr auto iot = std::views::iota(1, 12);
+    constexpr auto str = std::views::stride(iot, 3);
+    assert(4 == str.size());
+    static_assert(4 == str.size(), "Striding by 3 through a 12 member list has size 4.");
+  }
+  {
+    constexpr auto iot = std::views::iota(1, 22);
+    constexpr auto str = std::views::stride(iot, 3);
+    assert(7 == str.size());
+    static_assert(7 == str.size(), "Striding by 3 through a 12 member list has size 4.");
+  }
+  return true;
+}
+
+int main(int, char**) {
+  runtime_test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
new file mode 100644
index 000000000000000..4d13f05d48c01ba
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
@@ -0,0 +1,183 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H
+
+#include "test_iterators.h"
+#include <iterator>
+#include <ranges>
+
+template <typename T = int>
+struct Range : std::ranges::view_base {
+  constexpr explicit Range(T* b, T* e) : begin_(b), end_(e) {}
+  constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
+  constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
+  Range& operator=(Range const&) = default;
+  Range& operator=(Range&&)      = default;
+  constexpr T* begin() const { return begin_; }
+  constexpr T* end() const { return end_; }
+
+  T* begin_;
+  T* end_;
+  bool wasCopyInitialized = false;
+  bool wasMoveInitialized = false;
+};
+
+template <typename T>
+Range(T, T) -> Range<T>;
+
+template <typename T>
+struct BorrowedRange : public Range<T> {};
+
+template <typename T>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange<T>> = true;
+
+struct NoCopyRange : std::ranges::view_base {
+  explicit NoCopyRange(int*, int*);
+  NoCopyRange(NoCopyRange const&)            = delete;
+  NoCopyRange(NoCopyRange&&)                 = default;
+  NoCopyRange& operator=(NoCopyRange const&) = default;
+  NoCopyRange& operator=(NoCopyRange&&)      = default;
+  int* begin() const;
+  int* end() const;
+};
+
+template <class Derived>
+struct ForwardIterBase {
+  using iterator_concept = std::forward_iterator_tag;
+  using value_type       = int;
+  using difference_type  = std::intptr_t;
+
+  constexpr int operator*() const { return 5; }
+
+  constexpr Derived& operator++() { return static_cast<Derived&>(*this); }
+  constexpr Derived operator++(int) { return {}; }
+
+  friend constexpr bool operator==(const ForwardIterBase&, const ForwardIterBase&) { return true; }
+};
+
+template <class Derived>
+struct InputIterBase {
+  using iterator_concept = std::input_iterator_tag;
+  using value_type       = int;
+  using difference_type  = std::intptr_t;
+
+  constexpr int operator*() const { return 5; }
+
+  constexpr Derived& operator++() { return static_cast<Derived&>(*this); }
+  constexpr Derived operator++(int) { return {}; }
+
+  friend constexpr bool operator==(const InputIterBase&, const InputIterBase&) { return true; }
+};
+
+struct NotSimpleViewIterB : ForwardIterBase<NotSimpleViewIterB> {
+  bool moved = false;
+
+  constexpr NotSimpleViewIterB()                          = default;
+  constexpr NotSimpleViewIterB(const NotSimpleViewIterB&) = default;
+  constexpr NotSimpleViewIterB(NotSimpleViewIterB&&) : moved{true} {}
+  constexpr NotSimpleViewIterB& operator=(NotSimpleViewIterB&&)      = default;
+  constexpr NotSimpleViewIterB& operator=(const NotSimpleViewIterB&) = default;
+};
+
+struct NotSimpleViewIterA : ForwardIterBase<NotSimpleViewIterA> {
+  bool moved         = false;
+  bool moved_from_a  = false;
+  bool copied_from_a = false;
+
+  constexpr NotSimpleViewIterA()                          = default;
+  constexpr NotSimpleViewIterA(const NotSimpleViewIterA&) = default;
+  constexpr NotSimpleViewIterA(const NotSimpleViewIterB&) : copied_from_a{true} {}
+  constexpr NotSimpleViewIterA(NotSimpleViewIterA&&) : moved{true} {}
+  constexpr NotSimpleViewIterA(NotSimpleViewIterB&&) : moved_from_a{true} {}
+  constexpr NotSimpleViewIterA& operator=(NotSimpleViewIterA&&)      = default;
+  constexpr NotSimpleViewIterA& operator=(const NotSimpleViewIterA&) = default;
+};
+
+struct NotSimpleView : std::ranges::view_base {
+  constexpr NotSimpleViewIterA begin() const { return {}; }
+  constexpr NotSimpleViewIterB begin() { return {}; }
+  constexpr NotSimpleViewIterA end() const { return {}; }
+  constexpr NotSimpleViewIterB end() { return {}; }
+
+  int* begin_;
+  int* end_;
+  bool wasCopyInitialized = false;
+  bool wasMoveInitialized = false;
+};
+
+struct ForwardTracedMoveIter : ForwardIterBase<ForwardTracedMoveIter> {
+  bool moved = false;
+
+  constexpr ForwardTracedMoveIter()                             = default;
+  constexpr ForwardTracedMoveIter(const ForwardTracedMoveIter&) = default;
+  constexpr ForwardTracedMoveIter(ForwardTracedMoveIter&&) : moved{true} {}
+  constexpr ForwardTracedMoveIter& operator=(ForwardTracedMoveIter&&)      = default;
+  constexpr ForwardTracedMoveIter& operator=(const ForwardTracedMoveIter&) = default;
+};
+
+struct ForwardTracedMoveView : std::ranges::view_base {
+  constexpr ForwardTracedMoveIter begin() const { return {}; }
+  constexpr ForwardTracedMoveIter end() const { return {}; }
+};
+
+struct BidirRange : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+
+  constexpr BidirRange(int* b, int* e) : begin_(b), end_(e) {}
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{begin_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{begin_}; }
+  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{end_}; }
+  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{end_}; }
+};
+static_assert(std::ranges::bidirectional_range<BidirRange>);
+static_assert(std::ranges::common_range<BidirRange>);
+static_assert(std::ranges::view<BidirRange>);
+static_assert(std::copyable<BidirRange>);
+
+enum CopyCategory { MoveOnly, Copyable };
+template <CopyCategory CC>
+struct BidirSentRange : std::ranges::view_base {
+  using sent_t       = sentinel_wrapper<bidirectional_iterator<int*>>;
+  using sent_const_t = sentinel_wrapper<bidirectional_iterator<const int*>>;
+
+  int* begin_;
+  int* end_;
+
+  constexpr BidirSentRange(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr BidirSentRange(const BidirSentRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirSentRange(BidirSentRange&&)
+    requires(CC == MoveOnly)
+  = default;
+  constexpr BidirSentRange& operator=(const BidirSentRange&)
+    requires(CC == Copyable)
+  = default;
+  constexpr BidirSentRange& operator=(BidirSentRange&&)
+    requires(CC == MoveOnly)
+  = default;
+
+  constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{begin_}; }
+  constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{begin_}; }
+  constexpr sent_t end() { return sent_t{bidirectional_iterator<int*>{end_}}; }
+  constexpr sent_const_t end() const { return sent_const_t{bidirectional_iterator<const int*>{end_}}; }
+};
+static_assert(std::ranges::bidirectional_range<BidirSentRange<MoveOnly>>);
+static_assert(!std::ranges::common_range<BidirSentRange<MoveOnly>>);
+static_assert(std::ranges::view<BidirSentRange<MoveOnly>>);
+static_assert(!std::copyable<BidirSentRange<MoveOnly>>);
+static_assert(std::ranges::bidirectional_range<BidirSentRange<Copyable>>);
+static_assert(!std::ranges::common_range<BidirSentRange<Copyable>>);
+static_assert(std::ranges::view<BidirSentRange<Copyable>>);
+static_assert(std::copyable<BidirSentRange<Copyable>>);
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index ac342aff0beb701..9a48b2315c90b09 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -831,6 +831,11 @@ def add_version_header(tc):
             "headers": ["algorithm"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_ranges_stride",
+            "values": {"c++23": 202207},
+            "headers": ["ranges"],
+        },
         {
             "name": "__cpp_lib_ranges_to_container",
             "values": {"c++23": 202202},

>From e6fc862631c83b000367eeb71d71c54583904999 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Mon, 4 Sep 2023 10:20:04 -0400
Subject: [PATCH 02/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Add CTAD testing and refactor test classes.
---
 .../range.stride.view/ctad.compile.pass.cpp   | 33 ++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
index 68556f32f875b12..7831d792d64e770 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.compile.pass.cpp
@@ -12,7 +12,37 @@
 
 // std::views::stride_view
 
-constexpr bool test() { return true; }
+#include <concepts>
+#include <ranges>
+#include <utility>
+#include "test.h"
+
+constexpr bool test() {
+  int arr[]{1, 2, 3};
+
+  InstrumentedBasicView<int> bv{arr, arr + 3};
+  InstrumentedBasicRange<int> br{};
+
+  static_assert(std::same_as<
+      decltype(std::ranges::stride_view(bv, 2)),
+      std::ranges::stride_view<decltype(bv)>
+  >);
+  static_assert(std::same_as<
+      decltype(std::ranges::stride_view(std::move(bv), 2)),
+      std::ranges::stride_view<decltype(bv)>
+  >);
+
+  static_assert(std::same_as<
+      decltype(std::ranges::drop_view(br, 0)),
+      std::ranges::drop_view<std::ranges::ref_view<InstrumentedBasicRange<int>>>
+  >);
+
+  static_assert(std::same_as<
+      decltype(std::ranges::drop_view(std::move(br), 0)),
+      std::ranges::drop_view<std::ranges::owning_view<InstrumentedBasicRange<int>>>
+  >);
+  return true;
+}
 
 int main(int, char**) {
   test();
@@ -20,3 +50,4 @@ int main(int, char**) {
 
   return 0;
 }
+

>From 89715f9cfe01c95763402618c89b8fa3328261d6 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Mon, 4 Sep 2023 10:23:04 -0400
Subject: [PATCH 03/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Refactor test classes. (previous fixup message was incorrect)
---
 .../range.stride.view/adaptor.pass.cpp        | 18 ++--
 .../range.stride.view/base.pass.cpp           | 24 +++---
 .../enable_borrowed_range.compile.pass.cpp    |  4 +-
 .../iterator/ctor.default.pass.cpp            |  4 +-
 .../range.adaptors/range.stride.view/test.h   | 82 ++++++++++---------
 5 files changed, 70 insertions(+), 62 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
index 8ad758a953426dd..0de98ba259517fc 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -28,8 +28,8 @@ constexpr bool test() {
   // Simple use cases.
   {
     {
-      BidirRange view(arr, arr + 3);
-      std::ranges::stride_view<BidirRange> strided(view, 1);
+      BidirView view(arr, arr + 3);
+      std::ranges::stride_view<BidirView> strided(view, 1);
       auto strided_iter = strided.begin();
 
       assert(*strided_iter == arr[0]);
@@ -38,8 +38,8 @@ constexpr bool test() {
       assert(*strided_iter == arr[2]);
     }
     {
-      BidirRange view(arr, arr + 3);
-      std::ranges::stride_view<BidirRange> strided(view, 2);
+      BidirView view(arr, arr + 3);
+      std::ranges::stride_view<BidirView> strided(view, 2);
       auto strided_iter = strided.begin();
 
       assert(*strided_iter == arr[0]);
@@ -170,14 +170,14 @@ constexpr bool test() {
     static_assert(!std::is_invocable_v<decltype(std::views::stride)>);
     // Not invocable because NotAViewableRange is, well, not a viewable range.
     static_assert(!std::is_invocable_v<decltype(std::views::reverse), NotAViewableRange>);
-    // Is invocable because BidirRange is a viewable range.
-    static_assert(std::is_invocable_v<decltype(std::views::reverse), BidirRange>);
+    // Is invocable because BidirView is a viewable range.
+    static_assert(std::is_invocable_v<decltype(std::views::reverse), BidirView>);
 
     // Make sure that pipe operations work!
-    static_assert(CanBePiped<BidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
-    static_assert(CanBePiped<BidirRange&, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+    static_assert(CanBePiped<BidirView, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
+    static_assert(CanBePiped<BidirView&, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
     static_assert(
-        !CanBePiped<NotABidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirRange>{}))>);
+        !CanBePiped<NotABidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
   }
   // A final sanity check.
   { static_assert(std::same_as<decltype(std::views::stride), decltype(std::ranges::views::stride)>); }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
index badfd532453158c..6b22b7e89c01581 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp
@@ -24,9 +24,9 @@ constexpr bool test() {
 
   // Check the const& overload
   {
-    Range range(buff, buff + 8);
-    std::ranges::stride_view<Range<int>> const view(range, 3);
-    std::same_as<Range<int>> decltype(auto) result = view.base();
+    InstrumentedBasicView range(buff, buff + 8);
+    std::ranges::stride_view<InstrumentedBasicView<int>> const view(range, 3);
+    std::same_as<InstrumentedBasicView<int>> decltype(auto) result = view.base();
     assert(result.wasCopyInitialized);
     assert(result.begin() == buff);
     assert(result.end() == buff + 8);
@@ -34,9 +34,9 @@ constexpr bool test() {
 
   // Check the && overload
   {
-    Range range(buff, buff + 8);
-    std::ranges::stride_view<Range<int>> view(range, 3);
-    std::same_as<Range<int>> decltype(auto) result = std::move(view).base();
+    InstrumentedBasicView<int> range(buff, buff + 8);
+    std::ranges::stride_view<InstrumentedBasicView<int>> view(range, 3);
+    std::same_as<InstrumentedBasicView<int>> decltype(auto) result = std::move(view).base();
     assert(result.wasMoveInitialized);
     assert(result.begin() == buff);
     assert(result.end() == buff + 8);
@@ -44,8 +44,8 @@ constexpr bool test() {
 
   // Check the && overload (again)
   {
-    Range range(buff, buff + 8);
-    std::same_as<Range<int>> decltype(auto) result = std::ranges::stride_view<Range<int>>(range, 3).base();
+    InstrumentedBasicView range(buff, buff + 8);
+    std::same_as<InstrumentedBasicView<int>> decltype(auto) result = std::ranges::stride_view<InstrumentedBasicView<int>>(range, 3).base();
     assert(result.wasMoveInitialized);
     assert(result.begin() == buff);
     assert(result.end() == buff + 8);
@@ -53,10 +53,10 @@ constexpr bool test() {
 
   // Ensure the const& overload is not considered when the base is not copy-constructible
   {
-    static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyRange> const&>);
-    static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyRange>&>);
-    static_assert(can_call_base_on<std::ranges::stride_view<NoCopyRange>&&>);
-    static_assert(can_call_base_on<std::ranges::stride_view<NoCopyRange>>);
+    static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyView> const&>);
+    static_assert(!can_call_base_on<std::ranges::stride_view<NoCopyView>&>);
+    static_assert(can_call_base_on<std::ranges::stride_view<NoCopyView>&&>);
+    static_assert(can_call_base_on<std::ranges::stride_view<NoCopyView>>);
   }
 
   return true;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
index 646a9423f4523c7..7c28842fe65f699 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/enable_borrowed_range.compile.pass.cpp
@@ -19,8 +19,8 @@ constexpr bool test() {
   using std::ranges::enable_borrowed_range;
   // Make sure that a stride_view over neither a borrowable nor an unborrowable view
   // is itself borrowable.
-  static_assert(!enable_borrowed_range<std::ranges::stride_view<Range<int>>>);
-  static_assert(!enable_borrowed_range<std::ranges::stride_view<BorrowedRange<int>>>);
+  static_assert(!enable_borrowed_range<std::ranges::stride_view<InstrumentedBasicView<int>>>);
+  static_assert(!enable_borrowed_range<std::ranges::stride_view<InstrumentedBorrowedRange<int>>>);
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
index cfc38b32926805a..d0f466d3adeaef0 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -17,11 +17,11 @@
 #include <ranges>
 
 bool non_simple_view_iter_ctor_test() {
-  using StrideView             = std::ranges::stride_view<NotSimpleView>;
+  using StrideView             = std::ranges::stride_view<InstrumentedNotSimpleView>;
   using StrideViewIterNonConst = std::ranges::iterator_t<StrideView>;
   using StrideViewIterConst    = std::ranges::iterator_t<const StrideView>;
 
-  StrideView sv{NotSimpleView{}, 1};
+  StrideView sv{InstrumentedNotSimpleView{}, 1};
   StrideViewIterNonConst iter = {sv, sv.base().begin(), 0};
   StrideViewIterConst iterb   = {iter};
   assert(iterb.__end_.moved_from_a == true);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
index 4d13f05d48c01ba..d9931cb6e63202f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
@@ -14,12 +14,18 @@
 #include <ranges>
 
 template <typename T = int>
-struct Range : std::ranges::view_base {
-  constexpr explicit Range(T* b, T* e) : begin_(b), end_(e) {}
-  constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
-  constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
-  Range& operator=(Range const&) = default;
-  Range& operator=(Range&&)      = default;
+struct InstrumentedBasicRange {
+  T *begin() const;
+  T *end() const;
+};
+
+template <typename T = int>
+struct InstrumentedBasicView : std::ranges::view_base {
+  constexpr explicit InstrumentedBasicView(T* b, T* e) : begin_(b), end_(e) {}
+  constexpr InstrumentedBasicView(InstrumentedBasicView const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
+  constexpr InstrumentedBasicView(InstrumentedBasicView&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
+  InstrumentedBasicView& operator=(InstrumentedBasicView const&) = default;
+  InstrumentedBasicView& operator=(InstrumentedBasicView&&)      = default;
   constexpr T* begin() const { return begin_; }
   constexpr T* end() const { return end_; }
 
@@ -30,20 +36,20 @@ struct Range : std::ranges::view_base {
 };
 
 template <typename T>
-Range(T, T) -> Range<T>;
+InstrumentedBasicView(T, T) -> InstrumentedBasicView<T>;
 
 template <typename T>
-struct BorrowedRange : public Range<T> {};
+struct InstrumentedBorrowedRange : public InstrumentedBasicView<T> {};
 
 template <typename T>
-inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange<T>> = true;
-
-struct NoCopyRange : std::ranges::view_base {
-  explicit NoCopyRange(int*, int*);
-  NoCopyRange(NoCopyRange const&)            = delete;
-  NoCopyRange(NoCopyRange&&)                 = default;
-  NoCopyRange& operator=(NoCopyRange const&) = default;
-  NoCopyRange& operator=(NoCopyRange&&)      = default;
+inline constexpr bool std::ranges::enable_borrowed_range<InstrumentedBorrowedRange<T>> = true;
+
+struct NoCopyView : std::ranges::view_base {
+  explicit NoCopyView(int*, int*);
+  NoCopyView(NoCopyView const&)            = delete;
+  NoCopyView(NoCopyView&&)                 = default;
+  NoCopyView& operator=(NoCopyView const&) = default;
+  NoCopyView& operator=(NoCopyView&&)      = default;
   int* begin() const;
   int* end() const;
 };
@@ -100,7 +106,7 @@ struct NotSimpleViewIterA : ForwardIterBase<NotSimpleViewIterA> {
   constexpr NotSimpleViewIterA& operator=(const NotSimpleViewIterA&) = default;
 };
 
-struct NotSimpleView : std::ranges::view_base {
+struct InstrumentedNotSimpleView : std::ranges::view_base {
   constexpr NotSimpleViewIterA begin() const { return {}; }
   constexpr NotSimpleViewIterB begin() { return {}; }
   constexpr NotSimpleViewIterA end() const { return {}; }
@@ -127,42 +133,42 @@ struct ForwardTracedMoveView : std::ranges::view_base {
   constexpr ForwardTracedMoveIter end() const { return {}; }
 };
 
-struct BidirRange : std::ranges::view_base {
+struct BidirView : std::ranges::view_base {
   int* begin_;
   int* end_;
 
-  constexpr BidirRange(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr BidirView(int* b, int* e) : begin_(b), end_(e) {}
 
   constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{begin_}; }
   constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{begin_}; }
   constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{end_}; }
   constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{end_}; }
 };
-static_assert(std::ranges::bidirectional_range<BidirRange>);
-static_assert(std::ranges::common_range<BidirRange>);
-static_assert(std::ranges::view<BidirRange>);
-static_assert(std::copyable<BidirRange>);
 
+static_assert(std::ranges::view<BidirView>);
+static_assert(std::copyable<BidirView>);
+
+/*
 enum CopyCategory { MoveOnly, Copyable };
 template <CopyCategory CC>
-struct BidirSentRange : std::ranges::view_base {
+struct BidirSentView : std::ranges::view_base {
   using sent_t       = sentinel_wrapper<bidirectional_iterator<int*>>;
   using sent_const_t = sentinel_wrapper<bidirectional_iterator<const int*>>;
 
   int* begin_;
   int* end_;
 
-  constexpr BidirSentRange(int* b, int* e) : begin_(b), end_(e) {}
-  constexpr BidirSentRange(const BidirSentRange&)
+  constexpr BidirSentView(int* b, int* e) : begin_(b), end_(e) {}
+  constexpr BidirSentView(const BidirSentView&)
     requires(CC == Copyable)
   = default;
-  constexpr BidirSentRange(BidirSentRange&&)
+  constexpr BidirSentView(BidirSentView&&)
     requires(CC == MoveOnly)
   = default;
-  constexpr BidirSentRange& operator=(const BidirSentRange&)
+  constexpr BidirSentView& operator=(const BidirSentView&)
     requires(CC == Copyable)
   = default;
-  constexpr BidirSentRange& operator=(BidirSentRange&&)
+  constexpr BidirSentView& operator=(BidirSentView&&)
     requires(CC == MoveOnly)
   = default;
 
@@ -171,13 +177,15 @@ struct BidirSentRange : std::ranges::view_base {
   constexpr sent_t end() { return sent_t{bidirectional_iterator<int*>{end_}}; }
   constexpr sent_const_t end() const { return sent_const_t{bidirectional_iterator<const int*>{end_}}; }
 };
-static_assert(std::ranges::bidirectional_range<BidirSentRange<MoveOnly>>);
-static_assert(!std::ranges::common_range<BidirSentRange<MoveOnly>>);
-static_assert(std::ranges::view<BidirSentRange<MoveOnly>>);
-static_assert(!std::copyable<BidirSentRange<MoveOnly>>);
-static_assert(std::ranges::bidirectional_range<BidirSentRange<Copyable>>);
-static_assert(!std::ranges::common_range<BidirSentRange<Copyable>>);
-static_assert(std::ranges::view<BidirSentRange<Copyable>>);
-static_assert(std::copyable<BidirSentRange<Copyable>>);
+// TODO: Clean up.
+static_assert(std::ranges::bidirectional_range<BidirSentView<MoveOnly>>);
+static_assert(!std::ranges::common_range<BidirSentView<MoveOnly>>);
+static_assert(std::ranges::view<BidirSentView<MoveOnly>>);
+static_assert(!std::copyable<BidirSentView<MoveOnly>>);
+static_assert(std::ranges::bidirectional_range<BidirSentView<Copyable>>);
+static_assert(!std::ranges::common_range<BidirSentView<Copyable>>);
+static_assert(std::ranges::view<BidirSentView<Copyable>>);
+static_assert(std::copyable<BidirSentView<Copyable>>);
+*/
 
 #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H

>From 6adb80b5552a5d0ef514f7c984919ceb3cbdba73 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Fri, 8 Sep 2023 23:26:02 -0400
Subject: [PATCH 04/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Cleanup adaptor tests and fix typo in category definition.
---
 .../range.stride.view/adaptor.pass.cpp        | 186 ++++++------------
 .../range.adaptors/range.stride.view/test.h   |  56 +++++-
 2 files changed, 111 insertions(+), 131 deletions(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
index 0de98ba259517fc..ba0efe36b98243f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp
@@ -12,7 +12,9 @@
 
 // std::views::stride_view
 
+#include "__ranges/stride_view.h"
 #include "test.h"
+#include <concepts>
 #include <iterator>
 #include <ranges>
 #include <utility>
@@ -23,149 +25,84 @@ concept CanBePiped = requires(View&& view, T&& t) {
 };
 
 constexpr bool test() {
-  int arr[] = {1, 2, 3};
+  constexpr int array_n = 3;
+  int arr[array_n]      = {1, 2, 3};
 
-  // Simple use cases.
+  // Test that `std::views::stride` is a range adaptor.
   {
+    // Check various forms of
+    // view | stride
     {
-      BidirView view(arr, arr + 3);
-      std::ranges::stride_view<BidirView> strided(view, 1);
-      auto strided_iter = strided.begin();
-
-      assert(*strided_iter == arr[0]);
-
-      std::ranges::advance(strided_iter, 2);
-      assert(*strided_iter == arr[2]);
-    }
-    {
-      BidirView view(arr, arr + 3);
-      std::ranges::stride_view<BidirView> strided(view, 2);
-      auto strided_iter = strided.begin();
-
-      assert(*strided_iter == arr[0]);
-
-      std::ranges::advance(strided_iter, 1);
-      assert(*strided_iter == arr[2]);
+      {
+        BidirView view(arr, arr + array_n);
+        //std::ranges::stride_view<BidirView> strided(view, 1);
+        std::same_as<std::ranges::stride_view<BidirView>> decltype(auto) strided = view | std::views::stride(1);
+        auto strided_iter                                                        = strided.begin();
+
+        // Check that the begin() iter views arr[0]
+        assert(*strided_iter == arr[0]);
+
+        // Check that the strided_iter, after advancing it 2 * 1 steps, views arr[2].
+        std::ranges::advance(strided_iter, 2);
+        assert(*strided_iter == arr[2]);
+      }
+      {
+        BidirView view(arr, arr + array_n);
+        std::same_as<std::ranges::stride_view<BidirView>> decltype(auto) strided = view | std::views::stride(2);
+        auto strided_iter                                                        = strided.begin();
+
+        assert(*strided_iter == arr[0]);
+
+        // Same test as above, just advance one time with a bigger step (1 * 2 steps).
+        std::ranges::advance(strided_iter, 1);
+        assert(*strided_iter == arr[2]);
+      }
     }
   }
 
-#if 0
-  // views::reverse(x) is equivalent to subrange{end, begin, size} if x is a
-  // sized subrange over reverse iterators
+  // Check various forms of
+  // adaptor | stride
   {
-    using It = bidirectional_iterator<int*>;
-    using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::sized>;
-
-    using ReverseIt = std::reverse_iterator<It>;
-    using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::sized>;
-
+    // Parallels the two tests from above.
+    constexpr auto identity_lambda = [](int i) { return i * 2; };
     {
-      BidirRange view(buf, buf + 3);
-      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
-      std::same_as<Subrange> auto result = std::views::reverse(subrange);
-      assert(base(result.begin()) == buf);
-      assert(base(result.end()) == buf + 3);
+      BidirView view(arr, arr + array_n);
+      const auto transform_stride_partial = std::views::transform(identity_lambda) | std::views::stride(1);
+
+      const auto transform_stride_applied = transform_stride_partial(view);
+      auto transform_stride_applied_iter  = transform_stride_applied.begin();
+      assert(*transform_stride_applied_iter == std::invoke(identity_lambda, arr[0]));
+      std::ranges::advance(transform_stride_applied_iter, 2);
+      assert(*transform_stride_applied_iter == std::invoke(identity_lambda, arr[2]));
     }
-    {
-      // std::move into views::reverse
-      BidirRange view(buf, buf + 3);
-      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
-      std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
-      assert(base(result.begin()) == buf);
-      assert(base(result.end()) == buf + 3);
-    }
-    {
-      // with a const subrange
-      BidirRange view(buf, buf + 3);
-      ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)), /* size */3);
-      std::same_as<Subrange> auto result = std::views::reverse(subrange);
-      assert(base(result.begin()) == buf);
-      assert(base(result.end()) == buf + 3);
-    }
-  }
-
-  // views::reverse(x) is equivalent to subrange{end, begin} if x is an
-  // unsized subrange over reverse iterators
-  {
-    using It = bidirectional_iterator<int*>;
-    using Subrange = std::ranges::subrange<It, It, std::ranges::subrange_kind::unsized>;
-
-    using ReverseIt = std::reverse_iterator<It>;
-    using ReverseSubrange = std::ranges::subrange<ReverseIt, ReverseIt, std::ranges::subrange_kind::unsized>;
 
     {
-      BidirRange view(buf, buf + 3);
-      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
-      std::same_as<Subrange> auto result = std::views::reverse(subrange);
-      assert(base(result.begin()) == buf);
-      assert(base(result.end()) == buf + 3);
-    }
-    {
-      // std::move into views::reverse
-      BidirRange view(buf, buf + 3);
-      ReverseSubrange subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
-      std::same_as<Subrange> auto result = std::views::reverse(std::move(subrange));
-      assert(base(result.begin()) == buf);
-      assert(base(result.end()) == buf + 3);
-    }
-    {
-      // with a const subrange
-      BidirRange view(buf, buf + 3);
-      ReverseSubrange const subrange(ReverseIt(std::ranges::end(view)), ReverseIt(std::ranges::begin(view)));
-      std::same_as<Subrange> auto result = std::views::reverse(subrange);
-      assert(base(result.begin()) == buf);
-      assert(base(result.end()) == buf + 3);
+      BidirView view(arr, arr + array_n);
+      const auto transform_stride_partial = std::views::transform(identity_lambda) | std::views::stride(2);
+
+      const auto transform_stride_applied = transform_stride_partial(view);
+      auto transform_stride_applied_iter  = transform_stride_applied.begin();
+      assert(*transform_stride_applied_iter == std::invoke(identity_lambda, arr[0]));
+      std::ranges::advance(transform_stride_applied_iter, 1);
+      assert(*transform_stride_applied_iter == std::invoke(identity_lambda, arr[2]));
     }
   }
 
-  // Otherwise, views::reverse(x) is equivalent to ranges::reverse_view{x}
   {
-    BidirRange view(buf, buf + 3);
-    std::same_as<std::ranges::reverse_view<BidirRange>> auto result = std::views::reverse(view);
-    assert(base(result.begin().base()) == buf + 3);
-    assert(base(result.end().base()) == buf);
-  }
-
-  // Test that std::views::reverse is a range adaptor
-  {
-    // Test `v | views::reverse`
-    {
-      BidirRange view(buf, buf + 3);
-      std::same_as<std::ranges::reverse_view<BidirRange>> auto result = view | std::views::reverse;
-      assert(base(result.begin().base()) == buf + 3);
-      assert(base(result.end().base()) == buf);
-    }
-
-    // Test `adaptor | views::reverse`
-    {
-      BidirRange view(buf, buf + 3);
-      auto f = [](int i) { return i; };
-      auto const partial = std::views::transform(f) | std::views::reverse;
-      using Result = std::ranges::reverse_view<std::ranges::transform_view<BidirRange, decltype(f)>>;
-      std::same_as<Result> auto result = partial(view);
-      assert(base(result.begin().base().base()) == buf + 3);
-      assert(base(result.end().base().base()) == buf);
-    }
-
-    // Test `views::reverse | adaptor`
-    {
-      BidirRange view(buf, buf + 3);
-      auto f = [](int i) { return i; };
-      auto const partial = std::views::reverse | std::views::transform(f);
-      using Result = std::ranges::transform_view<std::ranges::reverse_view<BidirRange>, decltype(f)>;
-      std::same_as<Result> auto result = partial(view);
-      assert(base(result.begin().base().base()) == buf + 3);
-      assert(base(result.end().base().base()) == buf);
-    }
+    using ForwardStrideView      = std::ranges::stride_view<ForwardView>;
+    using BidirStrideView        = std::ranges::stride_view<BidirView>;
+    using RandomAccessStrideView = std::ranges::stride_view<RandomAccessView>;
+
+    static_assert(std::ranges::forward_range<ForwardStrideView>);
+    static_assert(std::ranges::bidirectional_range<BidirStrideView>);
+    static_assert(std::ranges::random_access_range<RandomAccessStrideView>);
+    // TODO: check sized_range
   }
-#endif // big block
 
-  // From:
-  // Test that std::views::reverse is a range adaptor
   // Check SFINAE friendliness
   {
     struct NotAViewableRange {};
-    struct NotABidirRange {};
+    struct NotARange {};
     // Not invocable because there is no parameter.
     static_assert(!std::is_invocable_v<decltype(std::views::stride)>);
     // Not invocable because NotAViewableRange is, well, not a viewable range.
@@ -176,8 +113,7 @@ constexpr bool test() {
     // Make sure that pipe operations work!
     static_assert(CanBePiped<BidirView, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
     static_assert(CanBePiped<BidirView&, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
-    static_assert(
-        !CanBePiped<NotABidirRange, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
+    static_assert(!CanBePiped<NotARange, decltype(std::views::stride(std::ranges::range_difference_t<BidirView>{}))>);
   }
   // A final sanity check.
   { static_assert(std::same_as<decltype(std::views::stride), decltype(std::ranges::views::stride)>); }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
index d9931cb6e63202f..d06ef2c3b923c8a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
@@ -15,15 +15,17 @@
 
 template <typename T = int>
 struct InstrumentedBasicRange {
-  T *begin() const;
-  T *end() const;
+  T* begin() const;
+  T* end() const;
 };
 
 template <typename T = int>
 struct InstrumentedBasicView : std::ranges::view_base {
   constexpr explicit InstrumentedBasicView(T* b, T* e) : begin_(b), end_(e) {}
-  constexpr InstrumentedBasicView(InstrumentedBasicView const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
-  constexpr InstrumentedBasicView(InstrumentedBasicView&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
+  constexpr InstrumentedBasicView(InstrumentedBasicView const& other)
+      : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
+  constexpr InstrumentedBasicView(InstrumentedBasicView&& other)
+      : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
   InstrumentedBasicView& operator=(InstrumentedBasicView const&) = default;
   InstrumentedBasicView& operator=(InstrumentedBasicView&&)      = default;
   constexpr T* begin() const { return begin_; }
@@ -141,13 +143,55 @@ struct BidirView : std::ranges::view_base {
 
   constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>{begin_}; }
   constexpr bidirectional_iterator<const int*> begin() const { return bidirectional_iterator<const int*>{begin_}; }
-  constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>{end_}; }
-  constexpr bidirectional_iterator<const int*> end() const { return bidirectional_iterator<const int*>{end_}; }
+  constexpr sentinel_wrapper<bidirectional_iterator<int*>> end() {
+    return sentinel_wrapper<bidirectional_iterator<int*>>{bidirectional_iterator<int*>{end_}};
+  }
+  constexpr sentinel_wrapper<bidirectional_iterator<const int*>> end() const {
+    return sentinel_wrapper<bidirectional_iterator<const int*>>{bidirectional_iterator<const int*>{end_}};
+  }
 };
 
 static_assert(std::ranges::view<BidirView>);
 static_assert(std::copyable<BidirView>);
 
+struct ForwardView : public std::ranges::view_base {
+  int* begin_;
+  int* end_;
+
+  constexpr ForwardView(int* b, int* e) : begin_(b), end_(e) {}
+
+  constexpr forward_iterator<int*> begin() { return forward_iterator<int*>{begin_}; }
+  constexpr forward_iterator<const int*> begin() const { return forward_iterator<const int*>{begin_}; }
+  constexpr sentinel_wrapper<forward_iterator<int*>> end() {
+    return sentinel_wrapper<forward_iterator<int*>>{forward_iterator<int*>{end_}};
+  }
+  constexpr sentinel_wrapper<forward_iterator<const int*>> end() const {
+    return sentinel_wrapper<forward_iterator<const int*>>{forward_iterator<const int*>{end_}};
+  }
+};
+
+static_assert(std::ranges::view<ForwardView>);
+static_assert(std::copyable<ForwardView>);
+
+struct RandomAccessView : std::ranges::view_base {
+  int* begin_;
+  int* end_;
+
+  constexpr RandomAccessView(int* b, int* e) : begin_(b), end_(e) {}
+
+  constexpr random_access_iterator<int*> begin() { return random_access_iterator<int*>{begin_}; }
+  //constexpr random_access_iterator<const int*> begin() const { return random_access_iterator<const int*>{begin_}; }
+  constexpr sentinel_wrapper<random_access_iterator<int*>> end() {
+    return sentinel_wrapper<random_access_iterator<int*>>{random_access_iterator<int*>{end_}};
+  }
+  //constexpr sentinel_wrapper<random_access_iterator<const int*>> end() const { return sentinel_wrapper<random_access_iterator<const int*>>{random_access_iterator<const int*>{end_}}; }
+  constexpr std::size_t size() const { return end_ - begin_; }
+};
+
+static_assert(std::ranges::view<RandomAccessView>);
+static_assert(std::ranges::random_access_range<RandomAccessView>);
+static_assert(std::copyable<RandomAccessView>);
+
 /*
 enum CopyCategory { MoveOnly, Copyable };
 template <CopyCategory CC>

>From c0516286212d64798732d0f8f867d748e0887cef Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Sun, 10 Sep 2023 14:01:14 -0400
Subject: [PATCH 05/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Fix typo in begin for random_access_range.
---
 libcxx/include/__ranges/stride_view.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 880c8ce2950e470..f85247c825d815a 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -257,7 +257,7 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
     return *this += -__s;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator[](difference_type __s) const
+  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __s) const
     requires random_access_range<_Base>
   {
     return *(*this + __s);

>From ed0c5c3ef95ddcec9edb3d573b84245536e5a3b5 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Mon, 11 Sep 2023 10:03:21 -0400
Subject: [PATCH 06/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Add test for no default ctor.
---
 .../range.stride.view/ctor.default.pass.cpp   | 29 +++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.default.pass.cpp

diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..09cb8c20ea31857
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.default.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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <type_traits>
+
+constexpr bool test() {
+  // There is no default ctor for stride_view.
+  static_assert(!std::is_default_constructible_v<std::ranges::stride_view<BidirView>>);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From 68226ec30d6484dbf42b620336b068b98c8ca94c Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Wed, 13 Sep 2023 09:29:41 -0400
Subject: [PATCH 08/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Address comments from @EricWF.
---
 libcxx/include/__ranges/stride_view.h | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index f85247c825d815a..ff3b36073efa182 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -148,6 +148,18 @@ class stride_view : public view_interface<stride_view<_View>> {
   }
 }; // class stride_view
 
+template<class _View>
+struct __stride_view_iterator_concept { using type = input_iterator_tag; };
+
+template<random_access_range _View>
+struct __stride_view_iterator_concept<_View> { using type = random_access_iterator_tag; };
+
+template<bidirectional_range _View>
+struct __stride_view_iterator_concept<_View> { using type = bidirectional_iterator_tag; };
+
+template<forward_range _View>
+struct __stride_view_iterator_concept<_View> { using type = forward_iterator_tag; };
+
 template <class _View>
 struct __stride_iterator_category {};
 
@@ -170,14 +182,7 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
 public:
   using difference_type = range_difference_t<_Base>;
   using value_type      = range_value_t<_Base>;
-  using iterator_concept =
-      _If<random_access_range<_Base>,
-          random_access_iterator_tag,
-          _If<bidirectional_range<_Base>,
-              bidirectional_iterator_tag,
-              _If<forward_range<_Base>,
-                  forward_iterator_tag,
-                  /* else */ input_iterator_tag >>>;
+  using iterator_concept = typename __stride_view_iterator_concept<_View>::type;
 
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_     = iterator_t<_Base>();
   _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>();
@@ -392,7 +397,6 @@ stride_view(_Range&&) -> stride_view<views::all_t<_Range>>;
 
 namespace views {
 namespace __stride {
-// removed this.
 struct __fn {
   template <viewable_range _Range>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const

>From 47ea3c7a1b127d88f41afb1dffc1db501e58c4ae Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Wed, 13 Sep 2023 09:47:11 -0400
Subject: [PATCH 09/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Forgot to run clang-format before commiting.
---
 libcxx/include/__ranges/stride_view.h | 28 +++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index ff3b36073efa182..fc1fb89b3ba0ed1 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -148,17 +148,25 @@ class stride_view : public view_interface<stride_view<_View>> {
   }
 }; // class stride_view
 
-template<class _View>
-struct __stride_view_iterator_concept { using type = input_iterator_tag; };
+template <class _View>
+struct __stride_view_iterator_concept {
+  using type = input_iterator_tag;
+};
 
-template<random_access_range _View>
-struct __stride_view_iterator_concept<_View> { using type = random_access_iterator_tag; };
+template <random_access_range _View>
+struct __stride_view_iterator_concept<_View> {
+  using type = random_access_iterator_tag;
+};
 
-template<bidirectional_range _View>
-struct __stride_view_iterator_concept<_View> { using type = bidirectional_iterator_tag; };
+template <bidirectional_range _View>
+struct __stride_view_iterator_concept<_View> {
+  using type = bidirectional_iterator_tag;
+};
 
-template<forward_range _View>
-struct __stride_view_iterator_concept<_View> { using type = forward_iterator_tag; };
+template <forward_range _View>
+struct __stride_view_iterator_concept<_View> {
+  using type = forward_iterator_tag;
+};
 
 template <class _View>
 struct __stride_iterator_category {};
@@ -180,8 +188,8 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
   using _Base   = __maybe_const<_Const, _View>;
 
 public:
-  using difference_type = range_difference_t<_Base>;
-  using value_type      = range_value_t<_Base>;
+  using difference_type  = range_difference_t<_Base>;
+  using value_type       = range_value_t<_Base>;
   using iterator_concept = typename __stride_view_iterator_concept<_View>::type;
 
   _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_     = iterator_t<_Base>();

>From 80591a0844cd87c069cff39fbc6de6a92c47b8b0 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Fri, 15 Sep 2023 09:54:24 -0400
Subject: [PATCH 10/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Respond to JMazurkiewicz's helpful comments.
---
 libcxx/include/__ranges/stride_view.h         | 34 ++++---------------
 libcxx/include/module.modulemap.in            |  2 +-
 libcxx/modules/std/ranges.inc                 |  2 ++
 .../range.stride.view/stride.pass.cpp         | 32 +++++++++++++++++
 4 files changed, 41 insertions(+), 29 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp

diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index fc1fb89b3ba0ed1..9bf30e6c2815b9e 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -25,7 +25,6 @@
 #include <__iterator/iter_swap.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
-#include <__iterator/reverse_iterator.h>
 #include <__ranges/access.h>
 #include <__ranges/all.h>
 #include <__ranges/concepts.h>
@@ -69,8 +68,6 @@ class stride_view : public view_interface<stride_view<_View>> {
 
   template <bool _Const>
   class __iterator;
-  template <bool _Const>
-  class __sentinel;
 
 public:
   _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride)
@@ -84,16 +81,16 @@ class stride_view : public view_interface<stride_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto stride() { return __stride_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr range_difference_t<_View> stride() const noexcept { return __stride_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto size()
-    requires ranges::sized_range<_View>
+    requires sized_range<_View>
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
-    requires ranges::sized_range<const _View>
+    requires sized_range<const _View>
   {
     return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
@@ -105,7 +102,7 @@ class stride_view : public view_interface<stride_view<_View>> {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
-    requires ranges::range<const _View>
+    requires range<const _View>
   {
     return __iterator<true>{*this, ranges::begin(__base_)};
   }
@@ -203,8 +200,8 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
   = default;
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
-    requires _Const && std::convertible_to<ranges::iterator_t<_View>, ranges::iterator_t<_Base>> &&
-                 std::convertible_to<ranges::sentinel_t<_View>, ranges::sentinel_t<_Base>>
+    requires _Const && std::convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
+                 std::convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __current_(std::move(__i.__current_)),
         __end_(std::move(__i.__end_)),
         __stride_(__i.__stride_),
@@ -381,25 +378,6 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
   }
 }; // class stride_view::__iterator
 
-template <input_range _View>
-  requires view<_View>
-template <bool _Const>
-class stride_view<_View>::__sentinel {
-public:
-  sentinel_t<_View> __end_ = sentinel_t<_View>();
-
-  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(stride_view<_View>& __parent)
-      : __end_(ranges::end(__parent.__base_)) {}
-
-  _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_View> base() const { return __end_; }
-
-  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator<true> const& __x, __sentinel const& __y) {
-    return __x.__current_ == __y.__end_;
-  }
-}; // class stride_view::__sentinel
-
 template <class _Range>
 stride_view(_Range&&) -> stride_view<views::all_t<_Range>>;
 
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 97b1d27db3337d7..f052e35a37aefd7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1720,7 +1720,7 @@ module std_private_ranges_size                       [system] {
   export std_private_type_traits_make_unsigned
 }
 module std_private_ranges_split_view                 [system] { header "__ranges/split_view.h" }
-module std_private_ranges_stride_view                 [system] { header "__ranges/stride_view.h" }
+module std_private_ranges_stride_view                [system] { header "__ranges/stride_view.h" }
 module std_private_ranges_subrange                   [system] {
   header "__ranges/subrange.h"
   export std_private_ranges_subrange_fwd
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 0f418971c700da7..85384202e4758ce 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -276,12 +276,14 @@ export namespace std {
     } // namespace views
 #endif // _LIBCPP_STD_VER >= 23
 
+#if _LIBCPP_STD_VER >= 23
     // [range.stride], stride view
     using std::ranges::stride_view;
 
     namespace views {
       using std::ranges::views::stride;
     }
+#endif // _LIBCPP_STD_VER >= 23
 
 #if 0
     // [range.zip.transform], zip transform view
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
new file mode 100644
index 000000000000000..80092fc6738703a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+constexpr bool test() {
+  static_assert(noexcept(std::declval<std::ranges::stride_view<BidirView>>().stride()));
+  static_assert(std::is_same_v<std::ranges::range_difference_t<BidirView>,
+                               decltype(std::declval<std::ranges::stride_view<BidirView>>().stride())>);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

>From a3279a36d79125a4c1b6d3d2d6d23a56edf5ea97 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Fri, 15 Sep 2023 18:21:54 -0400
Subject: [PATCH 11/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Make aspects of std::ranges::stride_view::__iterator private.
---
 libcxx/include/__ranges/stride_view.h         | 12 ++--
 .../iterator/ctor.default.pass.cpp            | 13 +++--
 .../range.adaptors/range.stride.view/test.h   | 58 +++++++++----------
 3 files changed, 44 insertions(+), 39 deletions(-)

diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index 9bf30e6c2815b9e..e2ef0cdc30ee541 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -184,16 +184,18 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
   using _Parent = __maybe_const<_Const, stride_view<_View>>;
   using _Base   = __maybe_const<_Const, _View>;
 
+  _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_     = iterator_t<_Base>();
+  _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>();
+  range_difference_t<_Base> __stride_                        = 0;
+  range_difference_t<_Base> __missing_                       = 0;
+
+  friend stride_view;
+
 public:
   using difference_type  = range_difference_t<_Base>;
   using value_type       = range_value_t<_Base>;
   using iterator_concept = typename __stride_view_iterator_concept<_View>::type;
 
-  _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_     = iterator_t<_Base>();
-  _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>();
-  difference_type __stride_                                  = 0;
-  difference_type __missing_                                 = 0;
-
   // using iterator_category = inherited;
   _LIBCPP_HIDE_FROM_ABI __iterator()
     requires default_initializable<iterator_t<_Base>>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
index d0f466d3adeaef0..a4a6ba56727663f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -15,16 +15,19 @@
 #include "../test.h"
 #include <cassert>
 #include <ranges>
+#include <type_traits>
 
-bool non_simple_view_iter_ctor_test() {
-  using StrideView             = std::ranges::stride_view<InstrumentedNotSimpleView>;
+constexpr bool non_simple_view_iter_ctor_test() {
+  using StrideView             = std::ranges::stride_view<NotSimpleView>;
   using StrideViewIterNonConst = std::ranges::iterator_t<StrideView>;
   using StrideViewIterConst    = std::ranges::iterator_t<const StrideView>;
 
-  StrideView sv{InstrumentedNotSimpleView{}, 1};
+  StrideView sv{NotSimpleView{}, 1};
   StrideViewIterNonConst iter = {sv, sv.base().begin(), 0};
-  StrideViewIterConst iterb   = {iter};
-  assert(iterb.__end_.moved_from_a == true);
+  // StrideViewIterNonConst constructs its new __current_ and __end_ by
+  // moving the same members from the given iterator. When that is not possible
+  // for either of those two values, it should fail.
+  static_assert(!std::is_constructible_v<StrideViewIterConst, decltype(iter)>);
   return true;
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
index d06ef2c3b923c8a..e6640cd892e9d6a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/test.h
@@ -84,40 +84,40 @@ struct InputIterBase {
   friend constexpr bool operator==(const InputIterBase&, const InputIterBase&) { return true; }
 };
 
-struct NotSimpleViewIterB : ForwardIterBase<NotSimpleViewIterB> {
-  bool moved = false;
-
-  constexpr NotSimpleViewIterB()                          = default;
-  constexpr NotSimpleViewIterB(const NotSimpleViewIterB&) = default;
-  constexpr NotSimpleViewIterB(NotSimpleViewIterB&&) : moved{true} {}
-  constexpr NotSimpleViewIterB& operator=(NotSimpleViewIterB&&)      = default;
-  constexpr NotSimpleViewIterB& operator=(const NotSimpleViewIterB&) = default;
+struct NotSimpleViewIter : ForwardIterBase<NotSimpleViewIter> {
+  constexpr NotSimpleViewIter()                                    = default;
+  constexpr NotSimpleViewIter(const NotSimpleViewIter&)            = default;
+  constexpr NotSimpleViewIter(NotSimpleViewIter&&)                 = default;
+  constexpr NotSimpleViewIter& operator=(NotSimpleViewIter&&)      = default;
+  constexpr NotSimpleViewIter& operator=(const NotSimpleViewIter&) = default;
 };
 
-struct NotSimpleViewIterA : ForwardIterBase<NotSimpleViewIterA> {
-  bool moved         = false;
-  bool moved_from_a  = false;
-  bool copied_from_a = false;
-
-  constexpr NotSimpleViewIterA()                          = default;
-  constexpr NotSimpleViewIterA(const NotSimpleViewIterA&) = default;
-  constexpr NotSimpleViewIterA(const NotSimpleViewIterB&) : copied_from_a{true} {}
-  constexpr NotSimpleViewIterA(NotSimpleViewIterA&&) : moved{true} {}
-  constexpr NotSimpleViewIterA(NotSimpleViewIterB&&) : moved_from_a{true} {}
-  constexpr NotSimpleViewIterA& operator=(NotSimpleViewIterA&&)      = default;
-  constexpr NotSimpleViewIterA& operator=(const NotSimpleViewIterA&) = default;
+struct NotSimpleViewIterEnd : ForwardIterBase<NotSimpleViewIter> {
+  constexpr NotSimpleViewIterEnd()                                       = default;
+  constexpr NotSimpleViewIterEnd(const NotSimpleViewIterEnd&)            = default;
+  constexpr NotSimpleViewIterEnd(NotSimpleViewIterEnd&&)                 = default;
+  constexpr NotSimpleViewIterEnd& operator=(NotSimpleViewIterEnd&&)      = default;
+  constexpr NotSimpleViewIterEnd& operator=(const NotSimpleViewIterEnd&) = default;
 };
 
-struct InstrumentedNotSimpleView : std::ranges::view_base {
-  constexpr NotSimpleViewIterA begin() const { return {}; }
-  constexpr NotSimpleViewIterB begin() { return {}; }
-  constexpr NotSimpleViewIterA end() const { return {}; }
-  constexpr NotSimpleViewIterB end() { return {}; }
+struct ConstNotSimpleViewIter : ForwardIterBase<ConstNotSimpleViewIter> {
+  constexpr ConstNotSimpleViewIter()                              = default;
+  constexpr ConstNotSimpleViewIter(const ConstNotSimpleViewIter&) = default;
+  constexpr ConstNotSimpleViewIter(const NotSimpleViewIter&) {}
+  constexpr ConstNotSimpleViewIter(ConstNotSimpleViewIter&&) = default;
 
-  int* begin_;
-  int* end_;
-  bool wasCopyInitialized = false;
-  bool wasMoveInitialized = false;
+  constexpr ConstNotSimpleViewIter(NotSimpleViewIter&&) {}
+  constexpr ConstNotSimpleViewIter(NotSimpleViewIterEnd&&) = delete;
+
+  constexpr ConstNotSimpleViewIter& operator=(ConstNotSimpleViewIter&&)      = default;
+  constexpr ConstNotSimpleViewIter& operator=(const ConstNotSimpleViewIter&) = default;
+};
+
+struct NotSimpleView : std::ranges::view_base {
+  constexpr ConstNotSimpleViewIter begin() const { return {}; }
+  constexpr NotSimpleViewIter begin() { return {}; }
+  constexpr ConstNotSimpleViewIter end() const { return {}; }
+  constexpr NotSimpleViewIterEnd end() { return {}; }
 };
 
 struct ForwardTracedMoveIter : ForwardIterBase<ForwardTracedMoveIter> {

>From 335e866f5e094ed2bdbb7d59e8070d2cecb8b2a4 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Mon, 18 Sep 2023 12:31:07 -0400
Subject: [PATCH 12/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Remove duplicate module declaration of stride_view.
---
 libcxx/modules/std/ranges.inc | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 85384202e4758ce..7066c26011f14a8 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -282,7 +282,7 @@ export namespace std {
 
     namespace views {
       using std::ranges::views::stride;
-    }
+    } // namespace views
 #endif // _LIBCPP_STD_VER >= 23
 
 #if 0
@@ -331,13 +331,6 @@ export namespace std {
     }
 #endif // _LIBCPP_STD_VER >= 23
 
-    // [range.stride], stride view
-    using std::ranges::stride_view;
-
-    namespace views {
-      using std::ranges::views::stride;
-    }
-
 #if 0
     using std::ranges::cartesian_product_view;
 

>From 62bde1be397c02df3a27e1e99d5305a916a89a61 Mon Sep 17 00:00:00 2001
From: Will Hawkins <hawkinsw at obs.cr>
Date: Wed, 4 Oct 2023 19:44:22 -0400
Subject: [PATCH 13/13] fixup! WIP: [libc++][ranges] Implement
 `ranges::stride_view`.

Address first of Konstantin's helpful comments.
---
 libcxx/include/__ranges/stride_view.h         | 92 ++++++++++---------
 .../range.stride.view/ctor.pass.cpp           | 32 +++++++
 .../iterator/ctor.default.pass.cpp            | 44 ++++-----
 3 files changed, 101 insertions(+), 67 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp

diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h
index e2ef0cdc30ee541..47ecf3c58506674 100644
--- a/libcxx/include/__ranges/stride_view.h
+++ b/libcxx/include/__ranges/stride_view.h
@@ -71,7 +71,9 @@ class stride_view : public view_interface<stride_view<_View>> {
 
 public:
   _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride)
-      : __base_(std::move(__base)), __stride_(__stride) {}
+      : __base_(std::move(__base)), __stride_(__stride) {
+    _LIBCPP_ASSERT_UNCATEGORIZED(__stride > 0, "The value of stride must be greater than 0");
+  }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
@@ -83,68 +85,72 @@ class stride_view : public view_interface<stride_view<_View>> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr range_difference_t<_View> stride() const noexcept { return __stride_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
-    requires sized_range<_View>
-  {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
-  }
-
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
-    requires sized_range<const _View>
-  {
-    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
-  }
-
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!__simple_view<_View>)
   {
-    return __iterator<false>{*this, ranges::begin(__base_)};
+    return __iterator<false>(this, ranges::begin(__base_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires range<const _View>
   {
-    return __iterator<true>{*this, ranges::begin(__base_)};
+    return __iterator<true>(this, ranges::begin(__base_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && common_range<_View> && sized_range<_View> && forward_range<_View>)
   {
     auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
-    return __iterator<false>{*this, ranges::end(__base_), __missing};
+    return __iterator<false>(this, ranges::end(__base_), __missing);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View> && common_range<_View> && !bidirectional_range<_View>)
   {
-    return __iterator<false>{*this, ranges::end(__base_)};
+    return __iterator<false>(this, ranges::end(__base_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!__simple_view<_View>)
   {
-    return std::default_sentinel;
+    return default_sentinel;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires(range<const _View> && common_range<const _View> && sized_range<const _View> && forward_range<const _View>)
   {
     auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_;
-    return __iterator<true>{*this, ranges::end(__base_), __missing};
+    return __iterator<true>(this, ranges::end(__base_), __missing);
   }
   _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires(range<const _View> && common_range<_View> && !bidirectional_range<_View>)
   {
-    return __iterator<true>{*this, ranges::end(__base_)};
+    return __iterator<true>(this, ranges::end(__base_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
     requires(range<const _View>)
   {
-    return std::default_sentinel;
+    return default_sentinel;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_));
   }
 }; // class stride_view
 
+template <class _Range>
+stride_view(_Range&&, range_difference_t<_Range>) -> stride_view<views::all_t<_Range>>;
+
+namespace views {
 template <class _View>
 struct __stride_view_iterator_concept {
   using type = input_iterator_tag;
@@ -191,31 +197,31 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
 
   friend stride_view;
 
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+      _Parent* __parent, ranges::iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+      : __current_(std::move(__current)),
+        __end_(ranges::end(__parent->__base_)),
+        __stride_(__parent->__stride_),
+        __missing_(__missing) {}
+
 public:
   using difference_type  = range_difference_t<_Base>;
   using value_type       = range_value_t<_Base>;
   using iterator_concept = typename __stride_view_iterator_concept<_View>::type;
-
   // using iterator_category = inherited;
+
   _LIBCPP_HIDE_FROM_ABI __iterator()
     requires default_initializable<iterator_t<_Base>>
   = default;
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
-    requires _Const && std::convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
-                 std::convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+    requires _Const && convertible_to<ranges::iterator_t<_View>, iterator_t<_Base>> &&
+                 convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __current_(std::move(__i.__current_)),
         __end_(std::move(__i.__end_)),
         __stride_(__i.__stride_),
         __missing_(__i.__missing_) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
-      _Parent& __parent, ranges::iterator_t<_Base> __current, difference_type __missing = 0)
-      : __current_(std::move(__current)),
-        __end_(ranges::end(__parent.__base_)),
-        __stride_(__parent.__stride_),
-        __missing_(__missing) {}
-
   _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> const& base() const& noexcept { return __current_; }
   _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> base() && { return std::move(__current_); }
 
@@ -250,29 +256,29 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
     return __tmp;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __s)
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n)
     requires random_access_range<_Base>
   {
-    if (__s > 0) {
-      ranges::advance(__current_, __stride_ * (__s - 1));
+    if (__n > 0) {
+      ranges::advance(__current_, __stride_ * (__n - 1));
       __missing_ = ranges::advance(__current_, __stride_, __end_);
-    } else if (__s < 0) {
-      ranges::advance(__current_, __stride_ * __s + __missing_);
+    } else if (__n < 0) {
+      ranges::advance(__current_, __stride_ * __n + __missing_);
       __missing_ = 0;
     }
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __s)
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n)
     requires random_access_range<_Base>
   {
-    return *this += -__s;
+    return *this += -__n;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __s) const
+  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
-    return *(*this + __s);
+    return *(*this + __n);
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) {
@@ -380,17 +386,13 @@ class stride_view<_View>::__iterator : public __stride_iterator_category<_View>
   }
 }; // class stride_view::__iterator
 
-template <class _Range>
-stride_view(_Range&&) -> stride_view<views::all_t<_Range>>;
-
-namespace views {
 namespace __stride {
 struct __fn {
   template <viewable_range _Range>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const
       noexcept(noexcept(stride_view{std::forward<_Range>(__range), __n}))
           -> decltype(stride_view{std::forward<_Range>(__range), __n}) {
-    return stride_view{std::forward<_Range>(__range), __n};
+    return stride_view(std::forward<_Range>(__range), __n);
   }
 
   template <class _Np>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
new file mode 100644
index 000000000000000..126a7af66093e73
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// ranges
+
+// std::views::stride_view
+
+#include "test.h"
+#include <exception>
+#include <ranges>
+#include <type_traits>
+
+bool test() {
+  // Make sure that a constructor with a negative stride asserts.
+
+  int arr[] = {1, 2, 3};
+  ForwardView sc{arr, arr + 3};
+  auto sv = std::ranges::stride_view(sc, 0);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
index a4a6ba56727663f..acd26c35a5db2b5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.pass.cpp
@@ -13,40 +13,40 @@
 // std::views::stride_view
 
 #include "../test.h"
+#include "__concepts/same_as.h"
+#include "__ranges/stride_view.h"
 #include <cassert>
 #include <ranges>
 #include <type_traits>
 
 constexpr bool non_simple_view_iter_ctor_test() {
-  using StrideView             = std::ranges::stride_view<NotSimpleView>;
-  using StrideViewIterNonConst = std::ranges::iterator_t<StrideView>;
-  using StrideViewIterConst    = std::ranges::iterator_t<const StrideView>;
-
-  StrideView sv{NotSimpleView{}, 1};
-  StrideViewIterNonConst iter = {sv, sv.base().begin(), 0};
-  // StrideViewIterNonConst constructs its new __current_ and __end_ by
-  // moving the same members from the given iterator. When that is not possible
-  // for either of those two values, it should fail.
-  static_assert(!std::is_constructible_v<StrideViewIterConst, decltype(iter)>);
-  return true;
-}
+  using NotSimpleStrideView     = std::ranges::stride_view<NotSimpleView>;
+  using NotSimpleStrideViewIter = std::ranges::iterator_t<NotSimpleStrideView>;
+
+  using SimpleStrideView     = std::ranges::stride_view<ForwardTracedMoveView>;
+  using SimpleStrideViewIter = std::ranges::iterator_t<SimpleStrideView>;
+
+  NotSimpleStrideView nsv{NotSimpleView{}, 1};
+  [[maybe_unused]] NotSimpleStrideViewIter nsv_iter = nsv.begin();
 
-constexpr bool simpleview_iter_ctor_test() {
-  using StrideView     = std::ranges::stride_view<ForwardTracedMoveView>;
-  using StrideViewIter = std::ranges::iterator_t<StrideView>;
+  SimpleStrideView sv{ForwardTracedMoveView{}, 1};
+  [[maybe_unused]] SimpleStrideViewIter ssv_iter = sv.begin();
 
-  StrideView sv{ForwardTracedMoveView{}, 1};
-  StrideViewIter iter = {sv, sv.base().begin(), 0};
-  // Guarantee that when the iterator is given to the constructor that
-  // it is moved there.
-  assert(iter.base().moved);
+  using NotSimpleStrideViewIterConst = std::ranges::iterator_t<const NotSimpleStrideView>;
+  using SimpleStrideViewIterConst    = std::ranges::iterator_t<const SimpleStrideView>;
 
+  // .begin on a stride view over a non-simple view will give us a
+  // stride_view iterator with its _Const == false. Compare that type
+  // with an iterator on a stride view over a simple view that will give
+  // us an iterator with its _Const == true. They should *not* be the same.
+  static_assert(!std::is_same_v<decltype(ssv_iter), decltype(nsv_iter)>);
+  static_assert(!std::is_same_v<NotSimpleStrideViewIterConst, decltype(nsv_iter)>);
+  static_assert(std::is_same_v<SimpleStrideViewIterConst, decltype(ssv_iter)>);
   return true;
 }
 
 int main(int, char**) {
-  simpleview_iter_ctor_test();
   non_simple_view_iter_ctor_test();
-  static_assert(simpleview_iter_ctor_test());
+  static_assert(non_simple_view_iter_ctor_test());
   return 0;
 }



More information about the libcxx-commits mailing list