[libcxx-commits] [flang] [libcxx] [libc++] Implement adjacent_view (PR #165089)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 13 00:08:58 PST 2025


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/165089

>From f25ca3d74cdd3b5ce680aa905c9a23dbde79bcb1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Dec 2025 08:06:07 +0000
Subject: [PATCH 01/10] rebase

---
 flang/test/Semantics/modfile75.f90 | 44 ++++++++++++------------------
 1 file changed, 17 insertions(+), 27 deletions(-)

diff --git a/flang/test/Semantics/modfile75.f90 b/flang/test/Semantics/modfile75.f90
index e1f4db4051bbe..8f7adafe7204d 100644
--- a/flang/test/Semantics/modfile75.f90
+++ b/flang/test/Semantics/modfile75.f90
@@ -1,28 +1,18 @@
-! RUN: %python %S/test_modfile.py %s %flang_fc1
-module m
-  type dt
-    procedure(sub), pointer, nopass :: p1 => sub
-    procedure(sub), pointer, nopass :: p2 => null()
-    procedure(sub), pointer, nopass :: p3
-  end type
-  procedure(sub), pointer :: p4 => sub
-  procedure(sub), pointer :: p5 => null()
- contains
-  subroutine sub
-  end
-end
+!RUN: rm -rf %t && mkdir -p %t
+!RUN: %flang -c -fhermetic-module-files -DWHICH=1 -J%t %s && %flang -c -fhermetic-module-files -DWHICH=2 -J%t %s && %flang_fc1 -fdebug-unparse -J%t %s | FileCheck %s
 
-!Expect: m.mod
-!module m
-!type::dt
-!procedure(sub),nopass,pointer::p1=>sub
-!procedure(sub),nopass,pointer::p2=>NULL()
-!procedure(sub),nopass,pointer::p3
-!end type
-!intrinsic::null
-!procedure(sub),pointer::p4
-!procedure(sub),pointer::p5
-!contains
-!subroutine sub()
-!end
-!end
+#if WHICH == 1
+module modfile75a
+  use iso_c_binding
+end
+#elif WHICH == 2
+module modfile75b
+  use modfile75a
+end
+#else
+program test
+  use modfile75b
+!CHECK: INTEGER(KIND=4_4) n
+  integer(c_int) n
+end
+#endif

>From 2739a8d01850c1a52735f29c2443ccab1759f30e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Oct 2025 11:04:50 +0100
Subject: [PATCH 02/10] [libc++] implement adjacent_view

ci

ci

ci

ci

Update libcxx/include/__ranges/zip_utils.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/ReleaseNotes/22.rst               |   2 +-
 libcxx/include/CMakeLists.txt                 |   2 +
 libcxx/include/__ranges/adjacent_view.h       | 408 ++++++++++++++++++
 libcxx/include/__ranges/zip_utils.h           |  48 +++
 libcxx/include/__ranges/zip_view.h            |  16 +-
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/ranges                         |  15 +
 libcxx/modules/std/ranges.inc                 |   6 +-
 .../test/libcxx/transitive_includes/cxx23.csv |   1 +
 .../test/libcxx/transitive_includes/cxx26.csv |   1 +
 .../cpo.compile.pass.cpp                      |   3 +-
 .../range.adjacent/adaptor.pass.cpp           | 242 +++++++++++
 .../range.adjacent/base.pass.cpp              | 104 +++++
 .../range.adjacent/begin.pass.cpp             | 136 ++++++
 .../range.adjacent/borrowing.compile.pass.cpp |  39 ++
 .../range.adjacent/ctor.default.pass.cpp      |  73 ++++
 .../range.adjacent/ctor.views.pass.cpp        | 102 +++++
 .../range.adjacent/end.pass.cpp               | 143 ++++++
 .../range.adjacent/general.pass.cpp           |  57 +++
 .../range.adaptors/range.adjacent/helpers.h   |  43 ++
 .../iterator/arithmetic.pass.cpp              | 165 +++++++
 .../range.adjacent/iterator/compare.pass.cpp  | 153 +++++++
 .../iterator/ctor.default.pass.cpp            |  94 ++++
 .../iterator/ctor.other.pass.cpp              |  82 ++++
 .../iterator/decrement.pass.cpp               | 102 +++++
 .../range.adjacent/iterator/deref.pass.cpp    | 105 +++++
 .../iterator/increment.pass.cpp               |  92 ++++
 .../iterator/iter_move.pass.cpp               | 154 +++++++
 .../iterator/iter_swap.pass.cpp               | 136 ++++++
 .../iterator/member_types.compile.pass.cpp    | 150 +++++++
 .../range.adjacent/iterator/singular.pass.cpp | 106 +++++
 .../iterator/subscript.pass.cpp               |  67 +++
 .../range.concept.compile.pass.cpp            | 135 ++++++
 .../sentinel/ctor.default.pass.cpp            |  60 +++
 .../sentinel/ctor.other.pass.cpp              | 116 +++++
 .../range.adjacent/sentinel/eq.pass.cpp       | 195 +++++++++
 .../range.adjacent/sentinel/minus.pass.cpp    | 185 ++++++++
 .../range.adjacent/size.pass.cpp              | 122 ++++++
 .../sentinel/minus.pass.cpp                   |  78 +---
 .../range.zip/sentinel/minus.pass.cpp         |  93 +---
 .../range.adaptors/range_adaptor_types.h      | 109 ++++-
 ..._robust_against_no_unique_address.pass.cpp |   1 +
 42 files changed, 3756 insertions(+), 189 deletions(-)
 create mode 100644 libcxx/include/__ranges/adjacent_view.h
 create mode 100644 libcxx/include/__ranges/zip_utils.h
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp
 create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index dc3569d1eeae9..57f63db573300 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -39,7 +39,7 @@ Implemented Papers
 ------------------
 
 - P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__) (The paper is partially implemented. ``zip_transform_view``
-  is implemented in this release)
+  and `adjacent_view` are implemented in this release)
 - P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
 - P3044R2: sub-``string_view`` from ``string`` (`Github <https://llvm.org/PR148140>`__)
 - P3223R2: Making ``std::istream::ignore`` less surprising (`Github <https://llvm.org/PR148178>`__)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 19732016ee411..08b8780815df2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -700,6 +700,7 @@ set(files
   __random/uniform_real_distribution.h
   __random/weibull_distribution.h
   __ranges/access.h
+  __ranges/adjacent_view.h
   __ranges/all.h
   __ranges/as_rvalue_view.h
   __ranges/chunk_by_view.h
@@ -743,6 +744,7 @@ set(files
   __ranges/view_interface.h
   __ranges/views.h
   __ranges/zip_transform_view.h
+  __ranges/zip_utils.h
   __ranges/zip_view.h
   __split_buffer
   __std_mbstate_t.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
new file mode 100644
index 0000000000000..c31ce2cd75d3c
--- /dev/null
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -0,0 +1,408 @@
+// -*- 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_ADJACENT_VIEW_H
+#define _LIBCPP___RANGES_ADJACENT_VIEW_H
+
+#include <__config>
+
+#include <__algorithm/min.h>
+#include <__compare/three_way_comparable.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/equality_comparable.h>
+#include <__cstddef/size_t.h>
+#include <__functional/invoke.h>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
+#include <__iterator/prev.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__ranges/zip_utils.h>
+#include <__type_traits/common_type.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/make_unsigned.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/declval.h>
+#include <__utility/forward.h>
+#include <__utility/integer_sequence.h>
+#include <__utility/move.h>
+#include <array>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <forward_range _View, size_t _Np>
+  requires view<_View> && (_Np > 0)
+class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+
+  template <bool>
+  class __iterator;
+
+  template <bool>
+  class __sentinel;
+
+  struct __as_sentinel {};
+
+public:
+  _LIBCPP_HIDE_FROM_ABI adjacent_view()
+    requires default_initializable<_View>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_view(_View __base) : __base_(std::move(__base)) {}
+
+  _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 begin()
+    requires(!__simple_view<_View>)
+  {
+    return __iterator<false>(ranges::begin(__base_), ranges::end(__base_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+    requires range<const _View> // todo: this seems under-constrained. lwg issue?
+  {
+    return __iterator<true>(ranges::begin(__base_), ranges::end(__base_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+    requires(!__simple_view<_View>)
+  {
+    if constexpr (common_range<_View>) {
+      return __iterator<false>(__as_sentinel{}, ranges::begin(__base_), ranges::end(__base_));
+    } else {
+      return __sentinel<false>(ranges::end(__base_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+    requires range<const _View>
+  {
+    if constexpr (common_range<const _View>) {
+      return __iterator<true>(__as_sentinel{}, ranges::begin(__base_), ranges::end(__base_));
+    } else {
+      return __sentinel<true>(ranges::end(__base_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+    requires sized_range<_View>
+  {
+    using _ST = decltype(ranges::size(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::size(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires sized_range<const _View>
+  {
+    using _ST = decltype(ranges::size(__base_));
+    using _CT = common_type_t<_ST, size_t>;
+    auto __sz = static_cast<_CT>(ranges::size(__base_));
+    __sz -= std::min<_CT>(__sz, _Np - 1);
+    return static_cast<_ST>(__sz);
+  }
+};
+
+template <forward_range _View, size_t _Np>
+  requires view<_View> && (_Np > 0)
+template <bool _Const>
+class adjacent_view<_View, _Np>::__iterator {
+  friend adjacent_view;
+  using _Base _LIBCPP_NODEBUG              = __maybe_const<_Const, _View>;
+  array<iterator_t<_Base>, _Np> __current_ = array<iterator_t<_Base>, _Np>();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(iterator_t<_Base> __first, sentinel_t<_Base> __last) {
+    __current_[0] = __first;
+    for (int __i = 1; __i < static_cast<int>(_Np); ++__i) {
+      __current_[__i] = ranges::next(__current_[__i - 1], 1, __last);
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__as_sentinel, iterator_t<_Base> __first, iterator_t<_Base> __last) {
+    if constexpr (!bidirectional_range<_Base>) {
+      __current_.fill(__last);
+    } else {
+      __current_[_Np - 1] = __last;
+      for (int __i = static_cast<int>(_Np) - 2; __i >= 0; --__i) {
+        __current_[__i] = ranges::prev(__current_[__i + 1], 1, __first);
+      }
+    }
+  }
+
+  template <class _Iter, size_t... _Is>
+  _LIBCPP_HIDE_FROM_ABI explicit constexpr __iterator(_Iter&& __i, index_sequence<_Is...>)
+      : __current_{std::move(__i.__current_[_Is])...} {}
+
+  static consteval auto __get_iterator_concept() {
+    if constexpr (random_access_range<_Base>)
+      return random_access_iterator_tag{};
+    else if constexpr (bidirectional_range<_Base>)
+      return bidirectional_iterator_tag{};
+    else
+      return forward_iterator_tag{};
+  }
+
+  template <class _Tp, size_t... _Is>
+  static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple<decltype((_Is, std::declval<_Tp (*)()>()()))...>;
+
+public:
+  using iterator_category = input_iterator_tag;
+  using iterator_concept  = decltype(__get_iterator_concept());
+  using value_type        = decltype(__repeat_tuple_helper<range_value_t<_Base>>(make_index_sequence<_Np>{}));
+  using difference_type   = range_difference_t<_Base>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+      : __iterator(std::move(__i), make_index_sequence<_Np>{}) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
+    return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    for (auto& __i : __current_) {
+      ++__i;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+    requires bidirectional_range<_Base>
+  {
+    for (auto& __i : __current_) {
+      --__i;
+    }
+    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 __x)
+    requires random_access_range<_Base>
+  {
+    for (auto& __i : __current_) {
+      __i += __x;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+    requires random_access_range<_Base>
+  {
+    for (auto& __i : __current_) {
+      __i -= __x;
+    }
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
+    requires random_access_range<_Base>
+  {
+    return ranges::__tuple_transform([&](auto& __i) -> decltype(auto) { return __i[__n]; }, __current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_.back() == __y.__current_.back();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __x.__current_.back() < __y.__current_.back();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return __y < __x;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__y < __x);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base>
+  {
+    return !(__x < __y);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_.back() <=> __y.__current_.back();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r += __n;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+    requires random_access_range<_Base>
+  {
+    return __i + __n;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+    requires random_access_range<_Base>
+  {
+    auto __r = __i;
+    __r -= __n;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+    requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+  {
+    return __x.__current_.back() - __y.__current_.back();
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
+      noexcept(ranges::iter_move(std::declval<const iterator_t<_Base>&>())) &&
+      is_nothrow_move_constructible_v<range_rvalue_reference_t<_Base>>) {
+    return ranges::__tuple_transform(ranges::iter_move, __i.__current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept(
+      noexcept(ranges::iter_swap(std::declval<iterator_t<_Base>>(), std::declval<iterator_t<_Base>>())))
+    requires indirectly_swappable<iterator_t<_Base>>
+  {
+    for (size_t __i = 0; __i < _Np; ++__i) {
+      ranges::iter_swap(__l.__current_[__i], __r.__current_[__i]);
+    }
+  }
+};
+
+template <forward_range _View, size_t _Np>
+  requires view<_View> && (_Np > 0)
+template <bool _Const>
+class adjacent_view<_View, _Np>::__sentinel {
+  friend adjacent_view;
+  using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>;
+  sentinel_t<_Base> __end_    = sentinel_t<_Base>();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) { __end_ = std::move(__end); }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __i)
+    requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+      : __end_(std::move(__i.__end_)) {}
+
+  template <bool _OtherConst>
+    requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    return __x.__current_.back() == __y.__end_;
+  }
+
+  template <bool _OtherConst>
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    return __x.__current_.back() - __y.__end_;
+  }
+
+  template <bool _OtherConst>
+    requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) {
+    return __y.__end_ - __x.__current_.back();
+  }
+};
+
+template <class _View, size_t _Np>
+constexpr bool enable_borrowed_range<adjacent_view<_View, _Np>> = enable_borrowed_range<_View>;
+
+namespace views {
+namespace __adjacent {
+
+template <size_t _Np>
+struct __fn : __range_adaptor_closure<__fn<_Np>> {
+  template <class _Range>
+    requires(_Np == 0 && forward_range<_Range &&>)
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&&) noexcept {
+    return empty_view<tuple<>>{};
+  }
+
+  template <class _Ranges>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ranges&& __range) noexcept(
+      noexcept(adjacent_view<views::all_t<_Ranges&&>, _Np>(std::forward<_Ranges>(__range))))
+      -> decltype(adjacent_view<views::all_t<_Ranges&&>, _Np>(std::forward<_Ranges>(__range))) {
+    return adjacent_view<views::all_t<_Ranges&&>, _Np>(std::forward<_Ranges>(__range));
+  }
+};
+
+} // namespace __adjacent
+inline namespace __cpo {
+template <size_t _Np>
+inline constexpr auto adjacent = __adjacent::__fn<_Np>{};
+inline constexpr auto pairwise = adjacent<2>;
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_ADJACENT_VIEW_H
diff --git a/libcxx/include/__ranges/zip_utils.h b/libcxx/include/__ranges/zip_utils.h
new file mode 100644
index 0000000000000..f538486b6777d
--- /dev/null
+++ b/libcxx/include/__ranges/zip_utils.h
@@ -0,0 +1,48 @@
+// -*- 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_ZIP_VIEW_H
+#define _LIBCPP___RANGES_ZIP_VIEW_H
+
+#include <__config>
+
+#include <__functional/invoke.h>
+#include <__utility/forward.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Fun, class _Tuple>
+_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) {
+  return std::apply(
+      [&]<class... _Types>(_Types&&... __elements) {
+        return tuple<invoke_result_t<_Fun&, _Types>...>(std::invoke(__f, std::forward<_Types>(__elements))...);
+      },
+      std::forward<_Tuple>(__tuple));
+}
+
+} // namespace ranges
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_ZIP_VIEW_H
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index ce00c98710c4e..bd54f37028e45 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___RANGES_ZIP_VIEW_H
-#define _LIBCPP___RANGES_ZIP_VIEW_H
+#ifndef _LIBCPP___RANGES_ZIP_UTILS_H
+#define _LIBCPP___RANGES_ZIP_UTILS_H
 
 #include <__config>
 
@@ -31,6 +31,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
+#include <__ranges/zip_utils.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/declval.h>
@@ -58,15 +59,6 @@ concept __zip_is_common =
     (!(bidirectional_range<_Ranges> && ...) && (common_range<_Ranges> && ...)) ||
     ((random_access_range<_Ranges> && ...) && (sized_range<_Ranges> && ...));
 
-template <class _Fun, class _Tuple>
-_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) {
-  return std::apply(
-      [&]<class... _Types>(_Types&&... __elements) {
-        return tuple<invoke_result_t<_Fun&, _Types>...>(std::invoke(__f, std::forward<_Types>(__elements))...);
-      },
-      std::forward<_Tuple>(__tuple));
-}
-
 template <class _Fun, class _Tuple>
 _LIBCPP_HIDE_FROM_ABI constexpr void __tuple_for_each(_Fun&& __f, _Tuple&& __tuple) {
   std::apply(
@@ -504,4 +496,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___RANGES_ZIP_VIEW_H
+#endif // _LIBCPP___RANGES_ZIP_UTILS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index bbc2617835bc0..136e92bf7daee 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1850,6 +1850,7 @@ module std [system] {
 
   module ranges {
     module access                         { header "__ranges/access.h" }
+    module adjacent_view                  { header "__ranges/adjacent_view.h" }
     module all                            {
       header "__ranges/all.h"
       export std.ranges.ref_view
@@ -1947,6 +1948,9 @@ module std [system] {
       header "__ranges/zip_view.h"
       export std.utility.pair
     }
+    module zip_utils {
+      header "__ranges/zip_utils.h"
+    }
     module zip_transform_view {
       header "__ranges/zip_transform_view.h"
     }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index cfaa66a0831b3..c9fa3fca321f5 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -353,6 +353,20 @@ namespace std::ranges {
 
   namespace views { inline constexpr unspecified zip_transform = unspecified; }     // C++23
 
+  // [range.adjacent], adjacent view
+  template<forward_range V, size_t N>
+    requires view<V> && (N > 0)
+  class adjacent_view;
+
+  template<class V, size_t N>
+    constexpr bool enable_borrowed_range<adjacent_view<V, N>> =
+      enable_borrowed_range<V>;
+
+  namespace views {
+    template<size_t N>
+      constexpr unspecified adjacent = unspecified;
+    inline constexpr auto pairwise = adjacent<2>;
+  }
 
   // [range.as.rvalue]
   template <view V>
@@ -448,6 +462,7 @@ namespace std {
 #  endif
 
 #  if _LIBCPP_STD_VER >= 23
+#    include <__ranges/adjacent_view.h>
 #    include <__ranges/as_rvalue_view.h>
 #    include <__ranges/chunk_by_view.h>
 #    include <__ranges/from_range.h>
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index cc7daa3cd1aec..38222812fdc0b 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -299,9 +299,6 @@ export namespace std {
     namespace views {
       using std::ranges::views::zip_transform;
     }
-#endif // _LIBCPP_STD_VER >= 23
-
-#if 0
     using std::ranges::adjacent_view;
 
     namespace views {
@@ -309,6 +306,9 @@ export namespace std {
       using std::ranges::views::pairwise;
     } // namespace views
 
+#endif // _LIBCPP_STD_VER >= 23
+
+#if 0
     using std::ranges::adjacent_transform_view;
 
     namespace views {
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..506a020e03345 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -805,6 +805,7 @@ random string
 random string_view
 random tuple
 random version
+ranges array
 ranges cctype
 ranges compare
 ranges concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 8c3e1f0a97dfe..0ee20d390b5d1 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -787,6 +787,7 @@ random string
 random string_view
 random tuple
 random version
+ranges array
 ranges cctype
 ranges compare
 ranges concepts
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 7e2510f5b5d13..9996ea42ab26d 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -121,7 +121,7 @@ static_assert(test(std::views::values, pairs));
 
 #if TEST_STD_VER >= 23
 // static_assert(test(std::views::adjacent_transform<2>, [](int x, int y) { return x + y; }, a));
-// static_assert(test(std::views::adjacent<2>, a));
+static_assert(test(std::views::adjacent<2>, a));
 // static_assert(test(std::views::as_const, a));
 static_assert(test(std::views::as_rvalue, a));
 // static_assert(test(std::views::cartesian_product, a, a, a));
@@ -129,6 +129,7 @@ static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; }))
 // static_assert(test(std::views::chunk, a, 1));
 // static_assert(test(std::views::enumerate, a));
 static_assert(test(std::views::join_with, 1));
+static_assert(test(std::views::pairwise, a));
 // static_assert(test(std::views::stride, a, 1));
 static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a));
 static_assert(test(std::views::zip, a, a));
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp
new file mode 100644
index 0000000000000..ffd0f52765734
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp
@@ -0,0 +1,242 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Test std::views::adjacent<N>
+
+#include <concepts>
+#include <cstddef>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+
+#include "../range_adaptor_types.h"
+
+template <std::size_t N>
+constexpr void test_constraints() {
+  // needs to be a range
+  static_assert(std::is_invocable_v<decltype((std::views::adjacent<N>)), std::ranges::empty_view<int>>);
+  static_assert(!std::is_invocable_v<decltype((std::views::adjacent<N>)), int>);
+
+  // underlying needs to be forward_range
+  static_assert(!std::is_invocable_v<decltype((std::views::adjacent<N>)), InputCommonView>);
+  static_assert(std::is_invocable_v<decltype((std::views::adjacent<N>)), ForwardSizedView>);
+}
+
+constexpr void test_pairwise_constraints() {
+  // needs to be a range
+  static_assert(std::is_invocable_v<decltype((std::views::pairwise)), std::ranges::empty_view<int>>);
+  static_assert(!std::is_invocable_v<decltype((std::views::pairwise)), int>);
+
+  // underlying needs to be forward_range
+  static_assert(!std::is_invocable_v<decltype((std::views::pairwise)), InputCommonView>);
+  static_assert(std::is_invocable_v<decltype((std::views::pairwise)), ForwardSizedView>);
+}
+
+constexpr void test_all_constraints() {
+  test_pairwise_constraints();
+  test_constraints<0>();
+  test_constraints<1>();
+  test_constraints<2>();
+  test_constraints<3>();
+  test_constraints<5>();
+}
+
+constexpr void test_zero_case() {
+  // N == 0 is a special case that always results in an empty range
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  std::same_as<std::ranges::empty_view<std::tuple<>>> decltype(auto) v =
+      std::views::adjacent<0>(ContiguousCommonView{buffer});
+  assert(std::ranges::size(v) == 0);
+}
+
+struct MoveOnlyView : ForwardSizedView {
+  using ForwardSizedView::ForwardSizedView;
+
+  constexpr MoveOnlyView(MoveOnlyView&&)      = default;
+  constexpr MoveOnlyView(const MoveOnlyView&) = delete;
+
+  constexpr MoveOnlyView& operator=(MoveOnlyView&&)      = default;
+  constexpr MoveOnlyView& operator=(const MoveOnlyView&) = delete;
+};
+
+template <std::size_t N>
+constexpr void test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  const auto validateBegin = [&](auto&& view) {
+    auto it    = view.begin();
+    auto tuple = *it;
+
+    assert(std::get<0>(tuple) == buffer[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(tuple) == buffer[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(tuple) == buffer[2]);
+    if constexpr (N >= 4)
+      assert(std::get<3>(tuple) == buffer[3]);
+    if constexpr (N >= 5)
+      assert(std::get<4>(tuple) == buffer[4]);
+  };
+
+  // Test `views::adjacent<N>(r)`
+  {
+    using View                          = std::ranges::adjacent_view<ContiguousCommonView, N>;
+    std::same_as<View> decltype(auto) v = std::views::adjacent<N>(ContiguousCommonView{buffer});
+    assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0));
+    validateBegin(v);
+  }
+
+  // Test `views::adjacent<N>(move only view)`
+  {
+    using View                          = std::ranges::adjacent_view<MoveOnlyView, N>;
+    std::same_as<View> decltype(auto) v = std::views::adjacent<N>(MoveOnlyView{buffer});
+    assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0));
+    validateBegin(v);
+  }
+
+  // Test `r | views::adjacent<N>`
+  {
+    using View                          = std::ranges::adjacent_view<ContiguousCommonView, N>;
+    std::same_as<View> decltype(auto) v = ContiguousCommonView{buffer} | std::views::adjacent<N>;
+    assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0));
+    validateBegin(v);
+  }
+
+  // Test `move only view | views::adjacent<N>`
+  {
+    using View                          = std::ranges::adjacent_view<MoveOnlyView, N>;
+    std::same_as<View> decltype(auto) v = MoveOnlyView{buffer} | std::views::adjacent<N>;
+    assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0));
+    validateBegin(v);
+  }
+
+  // Test adjacent<N> | adjacent<N>
+  {
+    using View = std::ranges::adjacent_view<std::ranges::adjacent_view<ContiguousCommonView, N>, N>;
+
+    auto twice                          = std::views::adjacent<N> | std::views::adjacent<N>;
+    std::same_as<View> decltype(auto) v = ContiguousCommonView{buffer} | twice;
+    assert(std::ranges::size(v) == (N <= 5 ? 10 - 2 * N : 0));
+
+    if (std::ranges::size(v) == 0)
+      return;
+
+    auto it          = v.begin();
+    auto nestedTuple = *it;
+
+    auto innerFirstTuple = std::get<0>(nestedTuple);
+
+    assert(std::get<0>(innerFirstTuple) == buffer[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(innerFirstTuple) == buffer[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(innerFirstTuple) == buffer[2]);
+    if constexpr (N >= 4)
+      assert(std::get<3>(innerFirstTuple) == buffer[3]);
+    if constexpr (N >= 5)
+      assert(std::get<4>(innerFirstTuple) == buffer[4]);
+
+    if constexpr (N >= 2) {
+      auto innerSecondTuple = std::get<1>(nestedTuple);
+      assert(std::get<0>(innerSecondTuple) == buffer[1]);
+      if constexpr (N >= 3)
+        assert(std::get<1>(innerSecondTuple) == buffer[2]);
+      if constexpr (N >= 4)
+        assert(std::get<2>(innerSecondTuple) == buffer[3]);
+      if constexpr (N >= 5)
+        assert(std::get<3>(innerSecondTuple) == buffer[4]);
+    }
+  }
+}
+
+constexpr void test_pairwise() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    // Test `views::pairwise(r)`
+    using View                          = std::ranges::adjacent_view<ContiguousCommonView, 2>;
+    std::same_as<View> decltype(auto) v = std::views::pairwise(ContiguousCommonView{buffer});
+    assert(std::ranges::size(v) == 7);
+    auto it    = v.begin();
+    auto tuple = *it;
+    assert(std::get<0>(tuple) == buffer[0]);
+    assert(std::get<1>(tuple) == buffer[1]);
+  }
+
+  {
+    // Test `views::pairwise(move only view)`
+    using View                          = std::ranges::adjacent_view<MoveOnlyView, 2>;
+    std::same_as<View> decltype(auto) v = std::views::pairwise(MoveOnlyView{buffer});
+    assert(std::ranges::size(v) == 7);
+    auto it    = v.begin();
+    auto tuple = *it;
+    assert(std::get<0>(tuple) == buffer[0]);
+    assert(std::get<1>(tuple) == buffer[1]);
+  }
+  {
+    // Test `r | views::pairwise`
+    using View                          = std::ranges::adjacent_view<ContiguousCommonView, 2>;
+    std::same_as<View> decltype(auto) v = ContiguousCommonView{buffer} | std::views::pairwise;
+    assert(std::ranges::size(v) == 7);
+    auto it    = v.begin();
+    auto tuple = *it;
+    assert(std::get<0>(tuple) == buffer[0]);
+    assert(std::get<1>(tuple) == buffer[1]);
+  }
+  {
+    // Test `move only view | views::pairwise`
+    using View                          = std::ranges::adjacent_view<MoveOnlyView, 2>;
+    std::same_as<View> decltype(auto) v = MoveOnlyView{buffer} | std::views::pairwise;
+    assert(std::ranges::size(v) == 7);
+    auto it    = v.begin();
+    auto tuple = *it;
+    assert(std::get<0>(tuple) == buffer[0]);
+    assert(std::get<1>(tuple) == buffer[1]);
+  }
+  {
+    // Test pairwise | pairwise
+    using View = std::ranges::adjacent_view<std::ranges::adjacent_view<ContiguousCommonView, 2>, 2>;
+
+    auto twice                          = std::views::pairwise | std::views::pairwise;
+    std::same_as<View> decltype(auto) v = ContiguousCommonView{buffer} | twice;
+    assert(std::ranges::size(v) == 6);
+
+    auto it          = v.begin();
+    auto nestedTuple = *it;
+
+    auto innerFirstTuple = std::get<0>(nestedTuple);
+
+    assert(std::get<0>(innerFirstTuple) == buffer[0]);
+    assert(std::get<1>(innerFirstTuple) == buffer[1]);
+
+    auto innerSecondTuple = std::get<1>(nestedTuple);
+    assert(std::get<0>(innerSecondTuple) == buffer[1]);
+    assert(std::get<1>(innerSecondTuple) == buffer[2]);
+  }
+}
+
+constexpr bool test() {
+  test_all_constraints();
+  test_zero_case();
+  test_pairwise();
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp
new file mode 100644
index 0000000000000..c09231d672288
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr View base() const& requires copy_constructible<View>;
+// constexpr View base() &&;
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+struct Range : std::ranges::view_base {
+  template <std::size_t N>
+  constexpr explicit Range(int (&buffer)[N]) : begin_(&buffer[0]), end_(&buffer[0] + N) {}
+  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 int* begin() const { return begin_; }
+  constexpr int* end() const { return end_; }
+
+  int* begin_;
+  int* end_;
+  bool wasCopyInitialized = false;
+  bool wasMoveInitialized = false;
+};
+
+static_assert(std::ranges::view<Range>);
+static_assert(std::ranges::forward_range<Range>);
+
+struct NonCopyableRange : std::ranges::view_base {
+  explicit NonCopyableRange(int*, int*);
+  NonCopyableRange(NonCopyableRange const&)            = delete;
+  NonCopyableRange(NonCopyableRange&&)                 = default;
+  NonCopyableRange& operator=(NonCopyableRange const&) = default;
+  NonCopyableRange& operator=(NonCopyableRange&&)      = default;
+  int* begin() const;
+  int* end() const;
+};
+
+static_assert(!std::copy_constructible<NonCopyableRange>);
+
+template <typename T>
+concept CanCallBaseOn = requires(T t) { std::forward<T>(t).base(); };
+
+template <std::size_t N>
+constexpr void test() {
+  int buff[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  // Check the const& overload
+  {
+    Range range(buff);
+    auto view = range | std::views::adjacent<N>;
+
+    std::same_as<Range> decltype(auto) result = view.base();
+    assert(result.wasCopyInitialized);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 9);
+  }
+
+  // Check the && overload
+  {
+    Range range(buff);
+    auto view                                 = range | std::views::adjacent<N>;
+    std::same_as<Range> decltype(auto) result = std::move(view).base();
+    assert(result.wasMoveInitialized);
+    assert(result.begin() == buff);
+    assert(result.end() == buff + 9);
+  }
+
+  // Ensure the const& overload is not considered when the base is not copy-constructible
+  {
+    static_assert(!CanCallBaseOn<std::ranges::adjacent_view<NonCopyableRange, N> const&>);
+    static_assert(!CanCallBaseOn<std::ranges::adjacent_view<NonCopyableRange, N>&>);
+    static_assert(!CanCallBaseOn<std::ranges::adjacent_view<NonCopyableRange, N> const&&>);
+    static_assert(CanCallBaseOn<std::ranges::adjacent_view<NonCopyableRange, N>&&>);
+    static_assert(CanCallBaseOn<std::ranges::adjacent_view<NonCopyableRange, N>>);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp
new file mode 100644
index 0000000000000..c17138bd6a5a3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto begin() requires (!(simple-view<Views> && ...));
+// constexpr auto begin() const requires (range<const Views> && ...);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "../range_adaptor_types.h"
+
+template <class T>
+concept HasConstBegin = requires(const T& ct) { ct.begin(); };
+
+template <class T>
+concept HasBegin = requires(T& t) { t.begin(); };
+
+template <class T>
+concept HasConstAndNonConstBegin = HasConstBegin<T> && requires(T& t, const T& ct) {
+  requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>;
+};
+
+template <class T>
+concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>;
+
+template <class T>
+concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>;
+
+struct NoConstBeginView : std::ranges::view_base {
+  int* begin();
+  int* end();
+};
+
+template <class Range, std::size_t N>
+constexpr void test_one() {
+  using View = std::ranges::adjacent_view<Range, N>;
+  {
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    View v(Range{buffer});
+
+    auto it    = v.begin();
+    auto tuple = *it;
+    assert(std::get<0>(tuple) == buffer[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(tuple) == buffer[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(tuple) == buffer[2]);
+
+    auto cit    = std::as_const(v).begin();
+    auto ctuple = *cit;
+    assert(std::get<0>(ctuple) == buffer[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(ctuple) == buffer[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(ctuple) == buffer[2]);
+    if constexpr (N >= 4)
+      assert(std::get<3>(ctuple) == buffer[3]);
+    if constexpr (N >= 5)
+      assert(std::get<4>(ctuple) == buffer[4]);
+  }
+
+  {
+    // empty range
+    std::array<int, 0> buffer = {};
+    View v(Range{buffer.data(), 0});
+    auto it  = v.begin();
+    auto cit = std::as_const(v).begin();
+    assert(it == v.end());
+    assert(cit == std::as_const(v).end());
+  }
+
+  if constexpr (N > 2) {
+    // N greater than range size
+    int buffer[2] = {1, 2};
+    View v(Range{buffer});
+    auto it  = v.begin();
+    auto cit = std::as_const(v).begin();
+    assert(it == v.end());
+    assert(cit == std::as_const(v).end());
+  }
+}
+
+template <std::size_t N>
+constexpr void test_simple() {
+  test_one<SimpleCommon, N>();
+
+  using View = std::ranges::adjacent_view<SimpleCommon, N>;
+  static_assert(std::is_same_v<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+}
+
+template <std::size_t N>
+constexpr void test_non_simple() {
+  test_one<NonSimpleCommon, N>();
+
+  using View = std::ranges::adjacent_view<NonSimpleCommon, N>;
+  static_assert(!std::is_same_v<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test_simple<N>();
+  test_non_simple<N>();
+
+  // Test with view that doesn't support const begin()
+  using ViewWithNoConstBegin = std::ranges::adjacent_view<NoConstBeginView, N>;
+  static_assert(!HasOnlyConstBegin<ViewWithNoConstBegin>);
+  static_assert(HasOnlyNonConstBegin<ViewWithNoConstBegin>);
+  static_assert(!HasConstAndNonConstBegin<ViewWithNoConstBegin>);
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp
new file mode 100644
index 0000000000000..75e70c2bb95dc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class View, size_t N>
+// inline constexpr bool enable_borrowed_range<adjacent_view<View, N>> =
+//      enable_borrowed_range<View>;
+
+#include <ranges>
+
+struct Borrowed : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<Borrowed> = true;
+
+static_assert(std::ranges::borrowed_range<Borrowed>);
+
+struct NonBorrowed : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::borrowed_range<NonBorrowed>);
+
+// test borrowed_range
+static_assert(std::ranges::borrowed_range<std::ranges::adjacent_view<Borrowed, 1>>);
+static_assert(std::ranges::borrowed_range<std::ranges::adjacent_view<Borrowed, 2>>);
+static_assert(std::ranges::borrowed_range<std::ranges::adjacent_view<Borrowed, 3>>);
+static_assert(!std::ranges::borrowed_range<std::ranges::adjacent_view<NonBorrowed, 1>>);
+static_assert(!std::ranges::borrowed_range<std::ranges::adjacent_view<NonBorrowed, 2>>);
+static_assert(!std::ranges::borrowed_range<std::ranges::adjacent_view<NonBorrowed, 3>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..c8a8a9c740281
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// adjacent_view() default_initializable<V> = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+constexpr int buff[] = {1, 2, 3, 4, 5};
+
+struct DefaultConstructibleView : std::ranges::view_base {
+  constexpr DefaultConstructibleView() : begin_(buff), end_(buff + std::ranges::size(buff)) {}
+  constexpr int const* begin() const { return begin_; }
+  constexpr int const* end() const { return end_; }
+
+private:
+  int const* begin_;
+  int const* end_;
+};
+
+struct NoDefaultCtrView : std::ranges::view_base {
+  NoDefaultCtrView() = delete;
+  int* begin() const;
+  int* end() const;
+};
+
+static_assert(std::is_default_constructible_v<std::ranges::adjacent_view<DefaultConstructibleView, 1>>);
+static_assert(std::is_default_constructible_v<std::ranges::adjacent_view<DefaultConstructibleView, 2>>);
+static_assert(std::is_default_constructible_v<std::ranges::adjacent_view<DefaultConstructibleView, 3>>);
+static_assert(!std::is_default_constructible_v<std::ranges::adjacent_view<NoDefaultCtrView, 1>>);
+static_assert(!std::is_default_constructible_v<std::ranges::adjacent_view<NoDefaultCtrView, 2>>);
+static_assert(!std::is_default_constructible_v<std::ranges::adjacent_view<NoDefaultCtrView, 3>>);
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    using View = std::ranges::adjacent_view<DefaultConstructibleView, N>;
+    View v     = View(); // the default constructor is not explicit
+    assert(v.size() == std::ranges::size(buff) - (N - 1));
+    auto tuple = *v.begin();
+    assert(std::get<0>(tuple) == buff[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(tuple) == buff[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(tuple) == buff[2]);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp
new file mode 100644
index 0000000000000..c060da36c4c01
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr explicit adjacent_view(View)
+
+#include <ranges>
+#include <tuple>
+
+#include "../range_adaptor_types.h"
+
+template <class T>
+void conversion_test(T);
+
+template <class T, class... Args>
+concept implicitly_constructible_from = requires(Args&&... args) { conversion_test<T>({std::move(args)...}); };
+
+// test constructor is explicit
+static_assert(std::constructible_from<std::ranges::adjacent_view<SimpleCommon, 1>, SimpleCommon>);
+static_assert(!implicitly_constructible_from<std::ranges::adjacent_view<SimpleCommon, 1>, SimpleCommon>);
+
+static_assert(std::constructible_from<std::ranges::adjacent_view<SimpleCommon, 5>, SimpleCommon>);
+static_assert(!implicitly_constructible_from<std::ranges::adjacent_view<SimpleCommon, 5>, SimpleCommon>);
+
+struct MoveAwareView : std::ranges::view_base {
+  int moves                 = 0;
+  constexpr MoveAwareView() = default;
+  constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; }
+  constexpr MoveAwareView& operator=(MoveAwareView&& other) {
+    moves       = other.moves + 1;
+    other.moves = 0;
+    return *this;
+  }
+  constexpr const int* begin() const { return &moves; }
+  constexpr const int* end() const { return &moves + 1; }
+};
+
+template <class View, std::size_t N>
+constexpr void constructorTest(auto&& buffer) {
+  std::ranges::adjacent_view<View, N> v{View{buffer}};
+  auto tuple = *v.begin();
+  assert(std::get<0>(tuple) == buffer[0]);
+  if constexpr (N >= 2)
+    assert(std::get<1>(tuple) == buffer[1]);
+  if constexpr (N >= 3)
+    assert(std::get<2>(tuple) == buffer[2]);
+};
+
+template <std::size_t N>
+constexpr void test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    // arguments are moved once
+    MoveAwareView mv;
+    std::ranges::adjacent_view<MoveAwareView, 1> v{std::move(mv)};
+    auto tuple = *v.begin();
+    assert(std::get<0>(tuple) == 2); // one move from the local variable to parameter, one move from parameter to member
+  }
+
+  // forward
+  {
+    constructorTest<ForwardSizedView, N>(buffer);
+  }
+
+  // bidi
+  {
+    constructorTest<BidiCommonView, N>(buffer);
+  }
+
+  // random_access
+  {
+    constructorTest<SizedRandomAccessView, N>(buffer);
+  }
+
+  // contiguous
+  {
+    constructorTest<ContiguousCommonView, N>(buffer);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
new file mode 100644
index 0000000000000..3ce45f49920fe
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto end() requires(!simple-view<_View>)
+// constexpr auto end() const requires range<const _View>
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <type_traits>
+#include <utility>
+
+#include "../range_adaptor_types.h"
+
+template <class Underlying, std::size_t N>
+constexpr void test_one() {
+  {
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    std::ranges::adjacent_view<Underlying, N> v{Underlying{buffer}};
+
+    auto it     = v.begin();
+    auto cit    = std::as_const(v).begin();
+    auto endIt  = v.end();
+    auto cendIt = std::as_const(v).end();
+    assert(it != endIt);
+    assert(cit != cendIt);
+    assert(it + (8 - (N - 1)) == endIt);
+    assert(cit + (8 - (N - 1)) == cendIt);
+  }
+  {
+    // empty range
+    std::array<int, 0> buffer = {};
+    std::ranges::adjacent_view<Underlying, N> v{Underlying{buffer.data(), 0}};
+    auto it     = v.begin();
+    auto cit    = std::as_const(v).begin();
+    auto endIt  = v.end();
+    auto cendIt = std::as_const(v).end();
+    assert(it == endIt);
+    assert(cit == cendIt);
+  }
+  if constexpr (N > 2) {
+    // N greater than range size
+    int buffer[2] = {1, 2};
+    std::ranges::adjacent_view<Underlying, N> v{Underlying{buffer}};
+    auto it     = v.begin();
+    auto cit    = std::as_const(v).begin();
+    auto endIt  = v.end();
+    auto cendIt = std::as_const(v).end();
+    assert(it == endIt);
+    assert(cit == cendIt);
+  }
+}
+
+template <std::size_t N>
+constexpr void test_simple_common_types() {
+  using NonConstView = std::ranges::adjacent_view<SimpleCommon, N>;
+  using ConstView    = const NonConstView;
+
+  static_assert(std::ranges::common_range<NonConstView>);
+  static_assert(std::ranges::common_range<ConstView>);
+  static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+
+  test_one<SimpleCommon, N>();
+}
+
+template <std::size_t N>
+constexpr void test_simple_non_common_types() {
+  using NonConstView = std::ranges::adjacent_view<SimpleNonCommon, N>;
+  using ConstView    = const NonConstView;
+
+  static_assert(!std::ranges::common_range<NonConstView>);
+  static_assert(!std::ranges::common_range<ConstView>);
+  static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+
+  test_one<SimpleNonCommon, N>();
+}
+
+template <std::size_t N>
+constexpr void test_non_simple_common_types() {
+  using NonConstView = std::ranges::adjacent_view<NonSimpleCommon, N>;
+  using ConstView    = const NonConstView;
+
+  static_assert(std::ranges::common_range<NonConstView>);
+  static_assert(std::ranges::common_range<ConstView>);
+  static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+
+  test_one<NonSimpleCommon, N>();
+}
+
+template <std::size_t N>
+constexpr void test_non_simple_non_common_types() {
+  using NonConstView = std::ranges::adjacent_view<NonSimpleNonCommon, N>;
+  using ConstView    = const NonConstView;
+
+  static_assert(!std::ranges::common_range<NonConstView>);
+  static_assert(!std::ranges::common_range<ConstView>);
+  static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+
+  test_one<NonSimpleNonCommon, N>();
+}
+
+template <std::size_t N>
+constexpr void test_forward_only() {
+  using NonConstView = std::ranges::adjacent_view<NonSimpleForwardSizedNonCommon, N>;
+  using ConstView    = const NonConstView;
+
+  static_assert(!std::ranges::common_range<NonConstView>);
+  static_assert(!std::ranges::common_range<ConstView>);
+  static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+
+  test_one<NonSimpleForwardSizedNonCommon, N>();
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test_simple_common_types<N>();
+  test_simple_non_common_types<N>();
+  test_non_simple_common_types<N>();
+  test_non_simple_non_common_types<N>();
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp
new file mode 100644
index 0000000000000..3c3716190a495
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Some basic examples of how adjacent_view might be used in the wild. This is a general
+// collection of sample algorithms and functions that try to mock general usage of
+// this view.
+
+#include <cstddef>
+#include <ranges>
+
+#include <cassert>
+#include <string_view>
+#include <vector>
+
+constexpr void test_adjacent_pairs() {
+  std::vector v = {1, 2, 3, 4};
+
+  std::pair<size_t, size_t> expected_index{0, 1};
+  for (auto [x, y] : v | std::views::adjacent<2>) {
+    assert(x == v[expected_index.first]);
+    assert(y == v[expected_index.second]);
+    assert(&x == &v[expected_index.first]);
+    assert(&y == &v[expected_index.second]);
+    ++expected_index.first;
+    ++expected_index.second;
+  }
+}
+
+constexpr void test_string_view() {
+  std::string_view sv = "123456789";
+  auto v              = sv | std::views::adjacent<3>;
+  auto [a, b, c]      = *v.begin();
+  assert(a == '1');
+  assert(b == '2');
+  assert(c == '3');
+}
+
+constexpr bool test() {
+  test_adjacent_pairs();
+  test_string_view();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h
new file mode 100644
index 0000000000000..cef29689ea301
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H
+
+#include <tuple>
+
+// intentionally not using meta programming for the expected tuple types
+
+template <std::size_t N, class T>
+struct ExpectedTupleType;
+
+template <class T>
+struct ExpectedTupleType<1, T> {
+  using type = std::tuple<T>;
+};
+template <class T>
+struct ExpectedTupleType<2, T> {
+  using type = std::tuple<T, T>;
+};
+template <class T>
+struct ExpectedTupleType<3, T> {
+  using type = std::tuple<T, T, T>;
+};
+template <class T>
+struct ExpectedTupleType<4, T> {
+  using type = std::tuple<T, T, T, T>;
+};
+template <class T>
+struct ExpectedTupleType<5, T> {
+  using type = std::tuple<T, T, T, T, T>;
+};
+
+template <std::size_t N, class T>
+using expectedTupleType = typename ExpectedTupleType<N, T>::type;
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H
\ No newline at end of file
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp
new file mode 100644
index 0000000000000..671a5c3fec4d0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp
@@ -0,0 +1,165 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator& operator+=(difference_type x)
+//   requires random_access_range<Base>;
+// constexpr iterator& operator-=(difference_type x)
+//   requires random_access_range<Base>;
+// friend constexpr iterator operator+(const iterator& i, difference_type n)
+//   requires random_access_range<Base>;
+// friend constexpr iterator operator+(difference_type n, const iterator& i)
+//   requires random_access_range<Base>;
+// friend constexpr iterator operator-(const iterator& i, difference_type n)
+//   requires random_access_range<Base>;
+// friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+//   requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>;
+
+#include <iterator>
+#include <ranges>
+
+#include <array>
+#include <concepts>
+#include <functional>
+
+#include "../../range_adaptor_types.h"
+
+template <class T, class U>
+concept canPlusEqual = requires(T& t, U& u) { t += u; };
+
+template <class T, class U>
+concept canMinusEqual = requires(T& t, U& u) { t -= u; };
+
+template <class R, std::size_t N>
+constexpr void test() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  const auto validateRefFromIndex = [&](auto&& tuple, std::size_t idx) {
+    assert(&std::get<0>(tuple) == &buffer[idx]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(tuple) == &buffer[idx + 1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(tuple) == &buffer[idx + 2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(tuple) == &buffer[idx + 3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(tuple) == &buffer[idx + 4]);
+  };
+
+  R rng{buffer};
+  {
+    // operator+(x, n) and operator+=
+    std::ranges::adjacent_view<R, N> v(rng);
+    auto it1 = v.begin();
+
+    validateRefFromIndex(*it1, 0);
+
+    auto it2 = it1 + 3;
+    validateRefFromIndex(*it2, 3);
+
+    auto it3 = 3 + it1;
+    validateRefFromIndex(*it3, 3);
+
+    it1 += 3;
+    assert(it1 == it2);
+    validateRefFromIndex(*it1, 3);
+  }
+
+  {
+    // operator-(x, n) and operator-=
+    std::ranges::adjacent_view<R, N> v(rng);
+    auto it1 = v.end();
+
+    auto it2 = it1 - 3;
+    validateRefFromIndex(*it2, 7 - N);
+
+    it1 -= 3;
+    assert(it1 == it2);
+    validateRefFromIndex(*it1, 7 - N);
+  }
+
+  {
+    // operator-(x, y)
+    std::ranges::adjacent_view<R, N> v(rng);
+    assert((v.end() - v.begin()) == (10 - N));
+
+    auto it1 = v.begin() + 2;
+    auto it2 = v.end() - 1;
+    assert((it1 - it2) == static_cast<long>(N) - 7);
+  }
+
+  {
+    // empty range
+    std::ranges::adjacent_view<R, N> v(R{buffer, 0});
+    assert((v.end() - v.begin()) == 0);
+  }
+
+  {
+    // N > size of range
+    std::ranges::adjacent_view<R, 3> v(R{buffer, 2});
+    assert((v.end() - v.begin()) == 0);
+  }
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<ContiguousCommonView, N>();
+  test<SimpleCommonRandomAccessSized, N>();
+
+  {
+    // Non random access but sized sentinel
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    using View   = std::ranges::adjacent_view<ForwardSizedView, N>;
+    using Iter   = std::ranges::iterator_t<View>;
+    using Diff   = std::iter_difference_t<Iter>;
+
+    static_assert(!std::invocable<std::plus<>, Iter, Diff>);
+    static_assert(!std::invocable<std::plus<>, Diff, Iter>);
+    static_assert(!canPlusEqual<Iter, Diff>);
+    static_assert(!std::invocable<std::minus<>, Iter, Diff>);
+    static_assert(!canMinusEqual<Iter, Diff>);
+    static_assert(std::invocable<std::minus<>, Iter, Iter>);
+
+    View v(ForwardSizedView{buffer});
+    auto it1 = v.begin();
+    auto it2 = v.end();
+    assert((it2 - it1) == (10 - N));
+  }
+
+  {
+    // Non random access and non-sized sentinel
+    using View = std::ranges::adjacent_view<SimpleNonCommonNonRandom, N>;
+    using Iter = std::ranges::iterator_t<View>;
+    using Diff = std::iter_difference_t<Iter>;
+
+    static_assert(!std::invocable<std::plus<>, Iter, Diff>);
+    static_assert(!std::invocable<std::plus<>, Diff, Iter>);
+    static_assert(!canPlusEqual<Iter, Diff>);
+    static_assert(!std::invocable<std::minus<>, Iter, Diff>);
+    static_assert(!canMinusEqual<Iter, Diff>);
+    static_assert(!std::invocable<std::minus<>, Iter, Iter>);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp
new file mode 100644
index 0000000000000..8461d7fa4a53c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp
@@ -0,0 +1,153 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// friend constexpr bool operator==(const iterator& x, const iterator& y);
+// friend constexpr bool operator<(const iterator& x, const iterator& y)
+//   requires random_access_range<Base>;
+// friend constexpr bool operator>(const iterator& x, const iterator& y)
+//   requires random_access_range<Base>;
+// friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//   requires random_access_range<Base>;
+// friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//   requires random_access_range<Base>;
+// friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//   requires random_access_range<Base> &&
+//            three_way_comparable<iterator_t<Base>>;
+
+#include <ranges>
+#include <compare>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+#include "../../range_adaptor_types.h"
+
+constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) {
+  assert(!(iter1 < iter1));
+  assert(iter1 < iter2);
+  assert(!(iter2 < iter1));
+  assert(iter1 <= iter1);
+  assert(iter1 <= iter2);
+  assert(!(iter2 <= iter1));
+  assert(!(iter1 > iter1));
+  assert(!(iter1 > iter2));
+  assert(iter2 > iter1);
+  assert(iter1 >= iter1);
+  assert(!(iter1 >= iter2));
+  assert(iter2 >= iter1);
+  assert(iter1 == iter1);
+  assert(!(iter1 == iter2));
+  assert(iter2 == iter2);
+  assert(!(iter1 != iter1));
+  assert(iter1 != iter2);
+  assert(!(iter2 != iter2));
+}
+
+constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) {
+  using Iter1 = decltype(iter1);
+  using Iter2 = decltype(iter2);
+  static_assert(!std::is_invocable_v<std::less<>, Iter1, Iter2>);
+  static_assert(!std::is_invocable_v<std::less_equal<>, Iter1, Iter2>);
+  static_assert(!std::is_invocable_v<std::greater<>, Iter1, Iter2>);
+  static_assert(!std::is_invocable_v<std::greater_equal<>, Iter1, Iter2>);
+}
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // Test a new-school iterator with operator<=>; the iterator should also have operator<=>.
+    using It       = three_way_contiguous_iterator<int*>;
+    using SubRange = std::ranges::subrange<It>;
+    static_assert(std::three_way_comparable<It>);
+    using R = std::ranges::adjacent_view<SubRange, N>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+    auto r       = R{SubRange(It(buffer), It(buffer + 8))};
+    auto iter1   = r.begin();
+    auto iter2   = iter1 + 1;
+
+    compareOperatorTest(iter1, iter2);
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
+  }
+
+  {
+    // Test an old-school iterator with no operator<=>; the adjacent iterator shouldn't have
+    // operator<=> either.
+    using It       = random_access_iterator<int*>;
+    using SubRange = std::ranges::subrange<It>;
+    static_assert(!std::three_way_comparable<It>);
+    using R = std::ranges::adjacent_view<SubRange, N>;
+    static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+    auto r       = R{SubRange(It(buffer), It(buffer + 8))};
+    auto iter1   = r.begin();
+    auto iter2   = iter1 + 1;
+
+    compareOperatorTest(iter1, iter2);
+  }
+
+  {
+    // non random_access_range
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+    std::ranges::adjacent_view<BidiCommonView, N> v(BidiCommonView{buffer});
+    using View = decltype(v);
+    static_assert(!std::ranges::random_access_range<View>);
+
+    auto it1 = v.begin();
+    auto it2 = v.end();
+    assert(it1 != it2);
+
+    // advance it1 to the end
+    std::ranges::advance(it1, 9 - N);
+    assert(it1 == it2);
+
+    inequalityOperatorsDoNotExistTest(it1, it2);
+  }
+
+  {
+    // empty range
+    auto v   = std::views::empty<int> | std::views::adjacent<N>;
+    auto it1 = v.begin();
+    auto it2 = v.end();
+    assert(it1 == it2);
+  }
+
+  {
+    // N > size of range
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+    auto v       = std::ranges::adjacent_view<ContiguousCommonView, 10>(ContiguousCommonView{buffer});
+    auto it1     = v.begin();
+    auto it2     = v.end();
+    assert(it1 == it2);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..5c1fb15b443e0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// iterator() = default;
+
+#include <ranges>
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+
+struct PODIter {
+  int i; // deliberately uninitialised
+
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+
+  constexpr int operator*() const { return i; }
+
+  constexpr PODIter& operator++() { return *this; }
+  constexpr PODIter operator++(int) { return *this; }
+
+  friend constexpr bool operator==(const PODIter&, const PODIter&) = default;
+};
+
+struct IterDefaultCtrView : std::ranges::view_base {
+  PODIter begin() const;
+  PODIter end() const;
+};
+
+struct IterNoDefaultCtrView : std::ranges::view_base {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class... Views>
+using adjacent_iter = std::ranges::iterator_t<std::ranges::adjacent_view<Views...>>;
+
+template <std::size_t N>
+constexpr void test() {
+  using View = std::ranges::adjacent_view<IterDefaultCtrView, N>;
+  using Iter = std::ranges::iterator_t<View>;
+  {
+    Iter iter;
+    auto tuple = *iter;
+    assert((std::get<0>(tuple) == 0));
+    if constexpr (N >= 2)
+      assert((std::get<1>(tuple) == 0));
+    if constexpr (N >= 3)
+      assert((std::get<2>(tuple) == 0));
+    if constexpr (N >= 4)
+      assert((std::get<3>(tuple) == 0));
+    if constexpr (N >= 5)
+      assert((std::get<4>(tuple) == 0));
+  }
+
+  {
+    Iter iter  = {};
+    auto tuple = *iter;
+    assert((std::get<0>(tuple) == 0));
+    if constexpr (N >= 2)
+      assert((std::get<1>(tuple) == 0));
+    if constexpr (N >= 3)
+      assert((std::get<2>(tuple) == 0));
+    if constexpr (N >= 4)
+      assert((std::get<3>(tuple) == 0));
+    if constexpr (N >= 5)
+      assert((std::get<4>(tuple) == 0));
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp
new file mode 100644
index 0000000000000..7d47ca7761ae1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator(iterator<!Const> i)
+//      requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+
+using ConstIterIncompatibleView =
+    BasicView<forward_iterator<int*>,
+              forward_iterator<int*>,
+              random_access_iterator<const int*>,
+              random_access_iterator<const int*>>;
+static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
+                                   std::ranges::iterator_t<const ConstIterIncompatibleView>>);
+
+template <std::size_t N>
+constexpr void test() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    std::ranges::adjacent_view<NonSimpleCommon, N> v(NonSimpleCommon{buffer});
+    auto iter1                                       = v.begin();
+    std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
+
+    static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+    // We cannot create a non-const iterator from a const iterator.
+    static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+
+    assert(iter1 == iter2);
+
+    auto tuple = *iter2;
+    assert(std::get<0>(tuple) == buffer[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(tuple) == buffer[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(tuple) == buffer[2]);
+    if constexpr (N >= 4)
+      assert(std::get<3>(tuple) == buffer[3]);
+    if constexpr (N >= 5)
+      assert(std::get<4>(tuple) == buffer[4]);
+  }
+
+  {
+    // underlying non-const to const not convertible
+    std::ranges::adjacent_view<ConstIterIncompatibleView, N> v(ConstIterIncompatibleView{buffer});
+    auto iter1 = v.begin();
+    auto iter2 = std::as_const(v).begin();
+
+    static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+
+    static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+    static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp
new file mode 100644
index 0000000000000..155b3d40a7378
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr iterator& operator--() requires bidirectional_range<Base>;
+// constexpr iterator operator--(int) requires bidirectional_range<Base>;
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+
+template <class Iter>
+concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; };
+
+template <class R, std::size_t N>
+constexpr void test_one() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  const auto validateRefFromIndex = [&](auto&& tuple, std::size_t idx) {
+    assert(&std::get<0>(tuple) == &buffer[idx]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(tuple) == &buffer[idx + 1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(tuple) == &buffer[idx + 2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(tuple) == &buffer[idx + 3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(tuple) == &buffer[idx + 4]);
+  };
+
+  {
+    auto v = R(buffer) | std::views::adjacent<N>;
+
+    auto it    = v.begin();
+    using Iter = decltype(it);
+
+    std::ranges::advance(it, v.end());
+
+    --it;
+    validateRefFromIndex(*it, 9 - N);
+
+    static_assert(std::is_same_v<decltype(--it), Iter&>);
+    std::same_as<Iter&> decltype(auto) it_ref = --it;
+    assert(&it_ref == &it);
+
+    validateRefFromIndex(*it, 8 - N);
+
+    std::same_as<Iter> decltype(auto) tmp = it--;
+
+    validateRefFromIndex(*tmp, 8 - N);
+    validateRefFromIndex(*it, 7 - N);
+
+    // Decrement to the beginning
+    for (int i = 6 - N; i >= 0; --i) {
+      --it;
+      validateRefFromIndex(*it, i);
+    }
+    assert(it == v.begin());
+  }
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test_one<ContiguousNonCommonSized, N>();
+  test_one<SimpleCommonRandomAccessSized, N>();
+  test_one<BidiNonCommonView, N>();
+
+  // Non-bidirectional base range
+  {
+    using View = std::ranges::adjacent_view<ForwardSizedView, N>;
+    using Iter = std::ranges::iterator_t<View>;
+
+    static_assert(!canDecrement<Iter>);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp
new file mode 100644
index 0000000000000..f1e516c11701b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto operator*() const;
+
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+#include <tuple>
+
+#include "../helpers.h"
+#include "../../range_adaptor_types.h"
+
+template <std::size_t N>
+constexpr void test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    // simple case
+    auto v                                                      = buffer | std::views::adjacent<N>;
+    std::same_as<expectedTupleType<N, int&>> decltype(auto) res = *v.begin();
+
+    assert(&std::get<0>(res) == &buffer[0]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(res) == &buffer[1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(res) == &buffer[2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(res) == &buffer[3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(res) == &buffer[4]);
+  }
+
+  {
+    // operator* is const
+    auto v                                                      = buffer | std::views::adjacent<N>;
+    const auto it                                               = v.begin();
+    std::same_as<expectedTupleType<N, int&>> decltype(auto) res = *it;
+    assert(&std::get<0>(res) == &buffer[0]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(res) == &buffer[1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(res) == &buffer[2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(res) == &buffer[3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(res) == &buffer[4]);
+  }
+
+  {
+    // underlying range with prvalue range_reference_t
+    auto v                                                     = std::views::iota(0, 8) | std::views::adjacent<N>;
+    std::same_as<expectedTupleType<N, int>> decltype(auto) res = *v.begin();
+    assert(std::get<0>(res) == 0);
+    if constexpr (N >= 2)
+      assert(std::get<1>(res) == 1);
+    if constexpr (N >= 3)
+      assert(std::get<2>(res) == 2);
+    if constexpr (N >= 4)
+      assert(std::get<3>(res) == 3);
+    if constexpr (N >= 5)
+      assert(std::get<4>(res) == 4);
+  }
+
+  {
+    // const-correctness
+    const std::array bufferConst                                      = {1, 2, 3, 4, 5, 6, 7, 8};
+    auto v                                                            = bufferConst | std::views::adjacent<N>;
+    std::same_as<expectedTupleType<N, const int&>> decltype(auto) res = *v.begin();
+    assert(&std::get<0>(res) == &bufferConst[0]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(res) == &bufferConst[1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(res) == &bufferConst[2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(res) == &bufferConst[3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(res) == &bufferConst[4]);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..d1dab943d2785
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr iterator& operator++();
+// constexpr iterator operator++(int);
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+
+template <class R, std::size_t N>
+constexpr void test() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  const auto validateRefFromIndex = [&](auto&& tuple, std::size_t idx) {
+    assert(&std::get<0>(tuple) == &buffer[idx]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(tuple) == &buffer[idx + 1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(tuple) == &buffer[idx + 2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(tuple) == &buffer[idx + 3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(tuple) == &buffer[idx + 4]);
+  };
+
+  {
+    auto v     = R(buffer) | std::views::adjacent<N>;
+    auto it    = v.begin();
+    using Iter = decltype(it);
+
+    validateRefFromIndex(*it, 0);
+
+    std::same_as<Iter&> decltype(auto) it_ref = ++it;
+    assert(&it_ref == &it);
+
+    validateRefFromIndex(*it, 1);
+
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    auto original                          = it;
+    std::same_as<Iter> decltype(auto) copy = it++;
+    assert(original == copy);
+
+    validateRefFromIndex(*copy, 1);
+    validateRefFromIndex(*it, 2);
+
+    // Increment to the end
+    for (std::size_t i = 3; i != 9 - N; ++i) {
+      ++it;
+      validateRefFromIndex(*it, i);
+    }
+
+    ++it;
+    assert(it == v.end());
+  }
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test<ContiguousCommonView, N>();
+  test<SimpleCommonRandomAccessSized, N>();
+  test<BidiNonCommonView, N>();
+  test<ForwardSizedView, N>();
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..0d228c79e48ea
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// friend constexpr auto iter_move(const iterator& i) noexcept(see below);
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <tuple>
+
+#include "../helpers.h"
+#include "../../range_adaptor_types.h"
+
+struct ThrowingMove {
+  ThrowingMove() = default;
+  ThrowingMove(ThrowingMove&&) {}
+};
+
+class IterMoveMayThrowIter {
+  int* it_;
+
+public:
+  using value_type      = int;
+  using difference_type = typename std::iterator_traits<int*>::difference_type;
+
+  constexpr IterMoveMayThrowIter() = default;
+  explicit constexpr IterMoveMayThrowIter(int* it) : it_(it) {}
+
+  friend constexpr decltype(auto) iter_move(const IterMoveMayThrowIter& it) noexcept(false) {
+    return std::ranges::iter_move(it.it_);
+  }
+
+  friend constexpr bool operator==(const IterMoveMayThrowIter& x, const IterMoveMayThrowIter& y) {
+    return x.it_ == y.it_;
+  }
+
+  constexpr decltype(auto) operator*() const { return *it_; }
+  constexpr IterMoveMayThrowIter& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr IterMoveMayThrowIter operator++(int) {
+    auto tmp(*this);
+    ++(*this);
+    return tmp;
+  }
+};
+
+class IterMoveMayThrowRange {
+  int* buffer_;
+  std::size_t size_;
+
+public:
+  constexpr IterMoveMayThrowRange(int* buffer, std::size_t size) : buffer_(buffer), size_(size) {}
+  constexpr IterMoveMayThrowIter begin() const { return IterMoveMayThrowIter{buffer_}; }
+  constexpr IterMoveMayThrowIter end() const { return IterMoveMayThrowIter{buffer_ + size_}; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // underlying iter_move noexcept
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+    auto v = buffer | std::views::adjacent<N>;
+
+    auto it = v.begin();
+    static_assert(noexcept(std::ranges::iter_move(it)));
+
+    std::same_as<expectedTupleType<N, int&&>> decltype(auto) res = std::ranges::iter_move(it);
+
+    assert(&std::get<0>(res) == &buffer[0]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(res) == &buffer[1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(res) == &buffer[2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(res) == &buffer[3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(res) == &buffer[4]);
+  }
+
+  {
+    // underlying iter_move may throw
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+    auto v = IterMoveMayThrowRange{buffer, 9} | std::views::adjacent<N>;
+
+    auto it = v.begin();
+    static_assert(!noexcept(std::ranges::iter_move(it)));
+
+    std::same_as<expectedTupleType<N, int&&>> decltype(auto) res = std::ranges::iter_move(it);
+    assert(&std::get<0>(res) == &buffer[0]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(res) == &buffer[1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(res) == &buffer[2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(res) == &buffer[3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(res) == &buffer[4]);
+  }
+
+  {
+    // !is_nothrow_move_constructible_v<range_rvalue_reference_t<Base>>
+    // underlying iter_move may throw
+    auto throwingMoveRange =
+        std::views::iota(0, 9) | std::views::transform([](auto) noexcept { return ThrowingMove{}; });
+    auto v  = throwingMoveRange | std::views::adjacent<N>;
+    auto it = v.begin();
+    static_assert(!noexcept(std::ranges::iter_move(it)));
+  }
+
+  {
+    // underlying iterators' iter_move are called through ranges::iter_move
+    auto rng = adltest::IterMoveSwapRange{};
+    auto v   = rng | std::views::adjacent<N>;
+    assert(rng.iter_move_called_times == 0);
+    auto it = v.begin();
+    {
+      [[maybe_unused]] auto&& i = std::ranges::iter_move(it);
+      assert(rng.iter_move_called_times == N);
+    }
+    {
+      [[maybe_unused]] auto&& i = std::ranges::iter_move(it);
+      assert(rng.iter_move_called_times == 2 * N);
+    }
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..36be63dc6594b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(see below)
+//   requires indirectly_swappable<iterator_t<Base>>;
+
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <ranges>
+
+#include "../helpers.h"
+#include "../../range_adaptor_types.h"
+
+struct ThrowingMove {
+  ThrowingMove() = default;
+  ThrowingMove(ThrowingMove&&) {}
+  ThrowingMove& operator=(ThrowingMove&&) { return *this; }
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    // underlying iter_swap noexcept
+    int buffer[] = {
+        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+    };
+
+    auto v = buffer | std::views::adjacent<N>;
+
+    auto iter1 = v.begin();
+    auto iter2 = v.begin() + 10;
+    static_assert(noexcept(std::ranges::iter_swap(iter1, iter2)));
+
+    std::ranges::iter_swap(iter1, iter2);
+
+    assert(buffer[0] == 11);
+    assert(buffer[10] == 1);
+    if constexpr (N >= 2) {
+      assert(buffer[1] == 12);
+      assert(buffer[11] == 2);
+    }
+    if constexpr (N >= 3) {
+      assert(buffer[2] == 13);
+      assert(buffer[12] == 3);
+    }
+    if constexpr (N >= 4) {
+      assert(buffer[3] == 14);
+      assert(buffer[13] == 4);
+    }
+    if constexpr (N >= 5) {
+      assert(buffer[4] == 15);
+      assert(buffer[14] == 5);
+    }
+
+    auto tuple1 = *iter1;
+    auto tuple2 = *iter2;
+    assert(&std::get<0>(tuple1) == &buffer[0]);
+    assert(&std::get<0>(tuple2) == &buffer[10]);
+
+    if constexpr (N >= 2) {
+      assert(&std::get<1>(tuple1) == &buffer[1]);
+      assert(&std::get<1>(tuple2) == &buffer[11]);
+    }
+    if constexpr (N >= 3) {
+      assert(&std::get<2>(tuple1) == &buffer[2]);
+      assert(&std::get<2>(tuple2) == &buffer[12]);
+    }
+    if constexpr (N >= 4) {
+      assert(&std::get<3>(tuple1) == &buffer[3]);
+      assert(&std::get<3>(tuple2) == &buffer[13]);
+    }
+    if constexpr (N >= 5) {
+      assert(&std::get<4>(tuple1) == &buffer[4]);
+      assert(&std::get<4>(tuple2) == &buffer[14]);
+    }
+  }
+
+  {
+    // underlying iter_swap may throw
+    std::array<ThrowingMove, 10> iterSwapMayThrow{};
+    auto v     = iterSwapMayThrow | std::views::adjacent<N>;
+    auto iter1 = v.begin();
+    auto iter2 = ++v.begin();
+    static_assert(!noexcept(std::ranges::iter_swap(iter1, iter2)));
+  }
+
+  {
+    // underlying iterators' iter_swap are called through ranges::iter_swap
+    auto rng = adltest::IterMoveSwapRange{};
+    auto v   = rng | std::views::adjacent<N>;
+    assert(rng.iter_move_called_times == 0);
+    auto it1 = v.begin();
+    auto it2 = std::ranges::next(it1, 3);
+
+    std::ranges::iter_swap(it1, it2);
+    assert(rng.iter_swap_called_times == 2 * N);
+
+    std::ranges::iter_swap(it1, it2);
+    assert(rng.iter_swap_called_times == 4 * N);
+  }
+
+  {
+    // !indirectly_swappable<iterator_t<Base>>;
+
+    const int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    auto v             = buffer | std::views::adjacent<N>;
+    auto it1           = v.begin();
+    auto it2           = v.begin() + 1;
+    static_assert(!std::invocable<decltype(std::ranges::iter_swap), decltype(it1), decltype(it2)>);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
new file mode 100644
index 0000000000000..fc70fc4eed162
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Iterator traits and member typedefs in adjacent_view::iterator.
+
+#include <array>
+#include <iterator>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+#include "../../range_adaptor_types.h"
+
+template <class T>
+struct DiffTypeIter : random_access_iterator<int*> {
+  using value_type      = int;
+  using difference_type = T;
+
+  int operator*() const;
+  DiffTypeIter& operator++();
+  DiffTypeIter operator++(int);
+  friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default;
+};
+
+template <class T>
+struct DiffTypeView : std::ranges::view_base {
+  DiffTypeIter<T> begin() const;
+  DiffTypeIter<T> end() const;
+};
+
+struct Foo {};
+
+struct ConstVeryDifferentRange {
+  int* begin();
+  int* end();
+
+  forward_iterator<double*> begin() const;
+  forward_iterator<double*> end() const;
+};
+
+template <std::size_t N>
+void test() {
+  int buffer[] = {1, 2, 3, 4};
+
+  const auto expectedTupleType = [] {
+    if constexpr (N == 1)
+      return std::tuple<int>{};
+    else if constexpr (N == 2)
+      return std::tuple<int, int>{};
+    else if constexpr (N == 3)
+      return std::tuple<int, int, int>{};
+    else if constexpr (N == 4)
+      return std::tuple<int, int, int, int>{};
+    else if constexpr (N == 5)
+      return std::tuple<int, int, int, int, int>{};
+  };
+
+  using expected_value_type = decltype(expectedTupleType());
+
+  {
+    // Base contiguous range
+    std::ranges::adjacent_view<std::views::all_t<decltype((buffer))>, N> v(buffer);
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<typename Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename Iter::value_type, expected_value_type>);
+  }
+
+  {
+    // Base random access
+    std::ranges::adjacent_view<SizedRandomAccessView, N> v(SizedRandomAccessView{buffer});
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<typename Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename Iter::value_type, expected_value_type>);
+  }
+
+  {
+    // Base bidirectional
+    std::ranges::adjacent_view<BidiCommonView, N> v(BidiCommonView{buffer});
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<typename Iter::iterator_concept, std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename Iter::value_type, expected_value_type>);
+  }
+
+  {
+    // Base forward
+    std::ranges::adjacent_view<ForwardSizedView, N> v(ForwardSizedView{buffer});
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<typename Iter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename Iter::value_type, expected_value_type>);
+  }
+
+  {
+    // difference_type
+    std::ranges::adjacent_view<DiffTypeView<std::intptr_t>, N> v{DiffTypeView<std::intptr_t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<typename Iter::difference_type, std::intptr_t>);
+  }
+
+  {
+    // value_type
+    const std::array foos{Foo{}};
+    auto v     = std::views::adjacent<2>(foos);
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<typename Iter::value_type, std::tuple<Foo, Foo>>);
+  }
+
+  {
+    // const-iterator different from iterator
+    auto v          = ConstVeryDifferentRange{} | std::views::adjacent<2>;
+    using Iter      = decltype(v.begin());
+    using ConstIter = decltype(std::as_const(v).begin());
+
+    static_assert(std::is_same_v<typename Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename Iter::value_type, std::tuple<int, int>>);
+
+    static_assert(std::is_same_v<typename ConstIter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<typename ConstIter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename ConstIter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename ConstIter::value_type, std::tuple<double, double>>);
+  }
+}
+
+void test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+}
\ No newline at end of file
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp
new file mode 100644
index 0000000000000..de912ca50bac7
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-exceptions
+
+// If the invocation of any non-const member function of `iterator` exits via an
+// exception, the iterator acquires a singular value.
+
+#include <ranges>
+
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+
+struct ThrowOnDecrementIterator {
+  int* it_;
+
+  using value_type      = int;
+  using difference_type = std::intptr_t;
+
+  ThrowOnDecrementIterator() = default;
+  explicit ThrowOnDecrementIterator(int* it) : it_(it) {}
+
+  ThrowOnDecrementIterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  ThrowOnDecrementIterator operator++(int) {
+    auto tmp = *this;
+    ++it_;
+    return tmp;
+  }
+
+  ThrowOnDecrementIterator& operator--() { throw 5; }
+  ThrowOnDecrementIterator operator--(int) { throw 5; }
+
+  int& operator*() const { return *it_; }
+
+  friend bool operator==(ThrowOnDecrementIterator const&, ThrowOnDecrementIterator const&) = default;
+};
+
+struct ThrowOnIncrementView : IntBufferView {
+  ThrowOnDecrementIterator begin() const { return ThrowOnDecrementIterator{buffer_}; }
+  ThrowOnDecrementIterator end() const { return ThrowOnDecrementIterator{buffer_ + size_}; }
+};
+
+template <std::size_t N>
+void test() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8};
+  {
+    // adjacent_view iterator should be able to be destroyed after member function throws
+    auto v  = ThrowOnIncrementView{buffer} | std::views::adjacent<N>;
+    auto it = v.begin();
+    ++it;
+    try {
+      --it;
+      assert(false); // should not be reached as the above expression should throw.
+    } catch (int e) {
+      assert(e == 5);
+    }
+  }
+
+  {
+    // adjacent_view iterator should be able to be assigned after member function throws
+    auto v  = ThrowOnIncrementView{buffer} | std::views::adjacent<N>;
+    auto it = v.begin();
+    ++it;
+    try {
+      --it;
+      assert(false); // should not be reached as the above expression should throw.
+    } catch (int e) {
+      assert(e == 5);
+    }
+    it         = v.begin();
+    auto tuple = *it;
+    assert(std::get<0>(tuple) == buffer[0]);
+    if constexpr (N >= 2)
+      assert(std::get<1>(tuple) == buffer[1]);
+    if constexpr (N >= 3)
+      assert(std::get<2>(tuple) == buffer[2]);
+    if constexpr (N >= 4)
+      assert(std::get<3>(tuple) == buffer[3]);
+    if constexpr (N >= 5)
+      assert(std::get<4>(tuple) == buffer[4]);
+  }
+}
+
+void test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
new file mode 100644
index 0000000000000..389fbe9dc98d4
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto operator[](difference_type n) const requires
+//        all_random_access<Const, Views...>
+
+#include <ranges>
+#include <cassert>
+
+#include "../../range_adaptor_types.h"
+
+template <std::size_t N>
+constexpr void test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+  {
+    // random_access_range
+    std::ranges::adjacent_view<SizedRandomAccessView, N> v(SizedRandomAccessView{buffer});
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+
+    static_assert(std::is_same_v<decltype(it[0]), decltype(*it)>);
+  }
+
+  {
+    // contiguous_range
+    std::ranges::adjacent_view<ContiguousCommonView, N> v(ContiguousCommonView{buffer});
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+
+    static_assert(std::is_same_v<decltype(it[0]), decltype(*it)>);
+  }
+
+  {
+    // non random_access_range
+    std::ranges::adjacent_view<BidiCommonView, N> v(BidiCommonView{buffer});
+    auto iter               = v.begin();
+    const auto canSubscript = [](auto&& it) { return requires { it[0]; }; };
+    static_assert(!canSubscript(iter));
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp
new file mode 100644
index 0000000000000..674182592a281
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// test if adjacent_view models input_range, forward_range, bidirectional_range,
+//                            random_access_range, contiguous_range, common_range
+//                            sized_range
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <tuple>
+#include <utility>
+
+#include "../range_adaptor_types.h"
+
+template <std::size_t N>
+constexpr bool testConcept() {
+  int buffer[3] = {1, 2, 3};
+  {
+    std::ranges::adjacent_view<ContiguousCommonView, N> v(ContiguousCommonView{buffer});
+    using View = decltype(v);
+    static_assert(std::ranges::random_access_range<View>);
+    static_assert(!std::ranges::contiguous_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<ContiguousNonCommonView, N> v{ContiguousNonCommonView{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::random_access_range<View>);
+    static_assert(!std::ranges::contiguous_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<ContiguousNonCommonSized, N> v{ContiguousNonCommonSized{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::random_access_range<View>);
+    static_assert(!std::ranges::contiguous_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<SizedRandomAccessView, N> v{SizedRandomAccessView{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::random_access_range<View>);
+    static_assert(!std::ranges::contiguous_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<NonSizedRandomAccessView, N> v{NonSizedRandomAccessView{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::random_access_range<View>);
+    static_assert(!std::ranges::contiguous_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<BidiCommonView, N> v{BidiCommonView{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::bidirectional_range<View>);
+    static_assert(!std::ranges::random_access_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<BidiNonCommonView, N> v{BidiNonCommonView{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::forward_range<View>);
+    static_assert(std::ranges::bidirectional_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<ForwardSizedView, N> v{ForwardSizedView{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::forward_range<View>);
+    static_assert(!std::ranges::bidirectional_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(std::ranges::sized_range<View>);
+  }
+
+  {
+    std::ranges::adjacent_view<ForwardSizedNonCommon, N> v{ForwardSizedNonCommon{buffer}};
+    using View = decltype(v);
+    static_assert(std::ranges::forward_range<View>);
+    static_assert(!std::ranges::bidirectional_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(std::ranges::sized_range<View>);
+  }
+
+  return true;
+}
+
+static_assert(testConcept<1>());
+static_assert(testConcept<2>());
+static_assert(testConcept<3>());
+static_assert(testConcept<5>());
+
+using OutputIter = cpp17_output_iterator<int*>;
+static_assert(std::output_iterator<OutputIter, int>);
+
+struct OutputView : std::ranges::view_base {
+  OutputIter begin() const;
+  sentinel_wrapper<OutputIter> end() const;
+};
+static_assert(std::ranges::output_range<OutputView, int>);
+static_assert(!std::ranges::input_range<OutputView>);
+
+template <class T, std::size_t N>
+concept adjacent_viewable = requires { typename std::ranges::adjacent_view<T, N>; };
+
+static_assert(adjacent_viewable<SimpleCommon, 2>);
+
+// output_range is not supported
+static_assert(!adjacent_viewable<OutputView, 2>);
+
+// input only range is not supported
+static_assert(!adjacent_viewable<InputCommonView, 1>);
+static_assert(!adjacent_viewable<InputNonCommonView, 2>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..00a6cee577741
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// sentinel() = default;
+
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+struct PODSentinel {
+  bool b; // deliberately uninitialised
+
+  friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; }
+};
+
+struct Range : std::ranges::view_base {
+  int* begin() const;
+  PODSentinel end();
+};
+
+template <std::size_t N>
+constexpr void test() {
+  {
+    using R        = std::ranges::adjacent_view<Range, N>;
+    using Sentinel = std::ranges::sentinel_t<R>;
+    static_assert(!std::is_same_v<Sentinel, std::ranges::iterator_t<R>>);
+
+    std::ranges::iterator_t<R> it;
+
+    Sentinel s1;
+    assert(it != s1); // PODSentinel.b is initialised to false
+
+    Sentinel s2 = {};
+    assert(it != s2); // PODSentinel.b is initialised to false
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp
new file mode 100644
index 0000000000000..7a3ebbd94ca9d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr sentinel(sentinel<!Const> s);
+//   requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;;
+
+#include <cassert>
+#include <ranges>
+
+#include "../../range_adaptor_types.h"
+
+template <class T>
+struct convertible_sentinel_wrapper {
+  explicit convertible_sentinel_wrapper() = default;
+  constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {}
+
+  template <class U>
+    requires std::convertible_to<const U&, T>
+  constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {}
+
+  constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) {
+    return self.it_ == other;
+  }
+  T it_;
+};
+
+struct SentinelConvertibleView : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  constexpr int* begin() { return buffer_; }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_); }
+  constexpr convertible_sentinel_wrapper<const int*> end() const {
+    return convertible_sentinel_wrapper<const int*>(buffer_ + size_);
+  }
+};
+
+static_assert(!std::ranges::common_range<SentinelConvertibleView>);
+static_assert(std::convertible_to<std::ranges::sentinel_t<SentinelConvertibleView>,
+                                  std::ranges::sentinel_t<SentinelConvertibleView const>>);
+static_assert(!simple_view<SentinelConvertibleView>);
+
+struct SentinelNonConvertibleView : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  constexpr int* begin() { return buffer_; }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr sentinel_wrapper<int*> end() { return sentinel_wrapper<int*>(buffer_ + size_); }
+  constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); }
+};
+
+static_assert(!std::ranges::common_range<SentinelNonConvertibleView>);
+static_assert(!std::convertible_to<std::ranges::sentinel_t<SentinelNonConvertibleView>,
+                                   std::ranges::sentinel_t<SentinelNonConvertibleView const>>);
+static_assert(!simple_view<SentinelNonConvertibleView>);
+
+template <std::size_t N>
+constexpr void test() {
+  using View = std::ranges::adjacent_view<SentinelConvertibleView, N>;
+  static_assert(!std::ranges::common_range<View>);
+
+  using Sent      = std::ranges::sentinel_t<View>;
+  using ConstSent = std::ranges::sentinel_t<const View>;
+  static_assert(!std::is_same_v<Sent, ConstSent>);
+
+  {
+    // implicitly convertible
+    static_assert(std::convertible_to<Sent, ConstSent>);
+  }
+  {
+    // !Const
+    static_assert(!std::convertible_to<ConstSent, Sent>);
+  }
+  {
+    // !convertible_to<iterator_t<V>, iterator_t<Base>>
+    using V2 = std::ranges::adjacent_view<SentinelNonConvertibleView, N>;
+    static_assert(!std::convertible_to<std::ranges::sentinel_t<V2>, std::ranges::sentinel_t<const V2>>);
+  }
+
+  {
+    int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+    View v{SentinelConvertibleView{buffer}};
+    Sent sent1      = v.end();
+    ConstSent sent2 = sent1;
+
+    assert(v.begin() != sent2);
+    assert(std::as_const(v).begin() != sent2);
+    assert(v.begin() + (10 - N) == sent2);
+    assert(std::as_const(v).begin() + (10 - N) == sent2);
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp
new file mode 100644
index 0000000000000..a79107c69fb4b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp
@@ -0,0 +1,195 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<bool OtherConst>
+//   requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
+
+#include <cassert>
+#include <compare>
+#include <ranges>
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+using Iterator      = random_access_iterator<int*>;
+using ConstIterator = contiguous_iterator<const int*>;
+
+template <bool Const>
+struct ComparableSentinel {
+  using Iter = std::conditional_t<Const, ConstIterator, Iterator>;
+  Iter iter_;
+
+  explicit ComparableSentinel() = default;
+  constexpr explicit ComparableSentinel(const Iter& it) : iter_(it) {}
+
+  constexpr friend bool operator==(const Iterator& i, const ComparableSentinel& s) { return base(i) == base(s.iter_); }
+
+  constexpr friend bool operator==(const ConstIterator& i, const ComparableSentinel& s) {
+    return base(i) == base(s.iter_);
+  }
+};
+
+struct ComparableView : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  constexpr auto begin() { return Iterator(buffer_); }
+  constexpr auto begin() const { return ConstIterator(buffer_); }
+  constexpr auto end() { return ComparableSentinel<false>(Iterator(buffer_ + size_)); }
+  constexpr auto end() const { return ComparableSentinel<true>(ConstIterator(buffer_ + size_)); }
+};
+
+struct ConstIncompatibleView : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  constexpr random_access_iterator<int*> begin() { return random_access_iterator<int*>(buffer_); }
+  constexpr contiguous_iterator<const int*> begin() const { return contiguous_iterator<const int*>(buffer_); }
+  constexpr sentinel_wrapper<random_access_iterator<int*>> end() {
+    return sentinel_wrapper<random_access_iterator<int*>>(random_access_iterator<int*>(buffer_ + size_));
+  }
+  constexpr sentinel_wrapper<contiguous_iterator<const int*>> end() const {
+    return sentinel_wrapper<contiguous_iterator<const int*>>(contiguous_iterator<const int*>(buffer_ + size_));
+  }
+};
+
+template <std::size_t N>
+constexpr bool test() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+  {
+    // simple-view: const and non-const have the same iterator/sentinel type
+    using View = std::ranges::adjacent_view<SimpleNonCommon, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(simple_view<View>);
+
+    View v{SimpleNonCommon(buffer)};
+
+    assert(v.begin() != v.end());
+    assert(v.begin() + 1 != v.end());
+    assert(v.begin() + 2 != v.end());
+    assert(v.begin() + 3 != v.end());
+    assert(v.begin() + (10 - N) == v.end());
+  }
+
+  {
+    // !simple-view: const and non-const have different iterator/sentinel types
+    using View = std::ranges::adjacent_view<NonSimpleNonCommon, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!simple_view<View>);
+
+    using Iter      = std::ranges::iterator_t<View>;
+    using ConstIter = std::ranges::iterator_t<const View>;
+    static_assert(!std::is_same_v<Iter, ConstIter>);
+    using Sentinel      = std::ranges::sentinel_t<View>;
+    using ConstSentinel = std::ranges::sentinel_t<const View>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(weakly_equality_comparable_with<Iter, Sentinel>);
+    static_assert(!weakly_equality_comparable_with<ConstIter, Sentinel>);
+    static_assert(weakly_equality_comparable_with<Iter, ConstSentinel>);
+    static_assert(weakly_equality_comparable_with<ConstIter, ConstSentinel>);
+
+    View v{NonSimpleNonCommon(buffer)};
+
+    assert(v.begin() != v.end());
+    assert(v.begin() + (10 - N) == v.end());
+
+    assert(v.begin() != std::as_const(v).end());
+    assert(v.begin() + (10 - N) == std::as_const(v).end());
+    // the above works because
+    static_assert(std::convertible_to<Iter, ConstIter>);
+
+    assert(std::as_const(v).begin() != std::as_const(v).end());
+    assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end());
+  }
+
+  {
+    // underlying const/non-const sentinel can be compared with both const/non-const iterator
+    using View = std::ranges::adjacent_view<ComparableView, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!simple_view<View>);
+
+    using Iter      = std::ranges::iterator_t<View>;
+    using ConstIter = std::ranges::iterator_t<const View>;
+    static_assert(!std::is_same_v<Iter, ConstIter>);
+    using Sentinel      = std::ranges::sentinel_t<View>;
+    using ConstSentinel = std::ranges::sentinel_t<const View>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(weakly_equality_comparable_with<Iter, Sentinel>);
+    static_assert(weakly_equality_comparable_with<ConstIter, Sentinel>);
+    static_assert(weakly_equality_comparable_with<Iter, ConstSentinel>);
+    static_assert(weakly_equality_comparable_with<ConstIter, ConstSentinel>);
+
+    View v{ComparableView(buffer)};
+
+    assert(v.begin() != v.end());
+    assert(v.begin() + (10 - N) == v.end());
+
+    static_assert(!std::convertible_to<Iter, ConstIter>);
+
+    assert(v.begin() != std::as_const(v).end());
+    assert(v.begin() + (10 - N) == std::as_const(v).end());
+
+    assert(std::as_const(v).begin() != v.end());
+    assert(std::as_const(v).begin() + (10 - N) == v.end());
+
+    assert(std::as_const(v).begin() != std::as_const(v).end());
+    assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end());
+  }
+
+  {
+    // underlying const/non-const sentinel cannot be compared with non-const/const iterator
+
+    using View = std::ranges::adjacent_view<ConstIncompatibleView, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!simple_view<View>);
+
+    using Iter      = std::ranges::iterator_t<View>;
+    using ConstIter = std::ranges::iterator_t<const View>;
+    static_assert(!std::is_same_v<Iter, ConstIter>);
+    using Sentinel      = std::ranges::sentinel_t<View>;
+    using ConstSentinel = std::ranges::sentinel_t<const View>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(weakly_equality_comparable_with<Iter, Sentinel>);
+    static_assert(!weakly_equality_comparable_with<ConstIter, Sentinel>);
+    static_assert(!weakly_equality_comparable_with<Iter, ConstSentinel>);
+    static_assert(weakly_equality_comparable_with<ConstIter, ConstSentinel>);
+
+    View v{ConstIncompatibleView{buffer}};
+
+    assert(v.begin() != v.end());
+    assert(v.begin() + (10 - N) == v.end());
+
+    assert(std::as_const(v).begin() != std::as_const(v).end());
+    assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end());
+  }
+
+  return true;
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp
new file mode 100644
index 0000000000000..9f3fada6013cb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<bool OtherConst>
+//   requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr range_difference_t<maybe-const<OtherConst, V>>
+//   operator-(const iterator<OtherConst>& x, const sentinel& y);
+
+// template<bool OtherConst>
+//   requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
+// friend constexpr range_difference_t<maybe-const<OtherConst, V>>
+//   operator-(const sentinel& y, const iterator<OtherConst>& x);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <ranges>
+#include <tuple>
+
+#include "../../range_adaptor_types.h"
+
+// clang-format off
+template <class T, class U>
+concept HasMinus = std::invocable<std::minus<>,const T&, const U&>;
+
+template <class T>
+concept SentinelHasMinus = HasMinus<std::ranges::sentinel_t<T>, std::ranges::iterator_t<T>>;
+// clang-format on
+
+template <std::size_t N>
+constexpr void test() {
+  int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  {
+    // simple-view
+    using View = std::ranges::adjacent_view<ForwardSizedNonCommon, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(simple_view<View>);
+
+    View v{ForwardSizedNonCommon(buffer)};
+
+    auto it = v.begin();
+    auto st = v.end();
+    assert(st - it == (10 - N));
+    assert(st - std::ranges::next(it, 1) == (9 - N));
+
+    assert(it - st == (static_cast<int>(N) - 10));
+    assert(std::ranges::next(it, 1) - st == (static_cast<int>(N) - 9));
+    static_assert(SentinelHasMinus<View>);
+  }
+
+  {
+    // empty range
+    using View = std::ranges::adjacent_view<ForwardSizedNonCommon, N>;
+    View v{ForwardSizedNonCommon(buffer, 0)};
+
+    auto it = v.begin();
+    auto st = v.end();
+    assert(st - it == 0);
+    assert(it - st == 0);
+  }
+
+  {
+    // N > size of underlying range
+    using View = std::ranges::adjacent_view<ForwardSizedNonCommon, 5>;
+    View v{ForwardSizedNonCommon(buffer, 3)};
+
+    auto it = v.begin();
+    auto st = v.end();
+    assert(st - it == 0);
+    assert(it - st == 0);
+  }
+
+  {
+    // underlying sentinel does not model sized_sentinel_for
+    using View = std::ranges::adjacent_view<decltype(std::views::iota(0)), N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!SentinelHasMinus<View>);
+  }
+
+  {
+    // const incompatible:
+    // underlying const sentinels cannot subtract underlying iterators
+    // underlying sentinels cannot subtract underlying const iterators
+    using View = std::ranges::adjacent_view<NonSimpleForwardSizedNonCommon, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!simple_view<View>);
+
+    using Iter      = std::ranges::iterator_t<View>;
+    using ConstIter = std::ranges::iterator_t<const View>;
+    static_assert(!std::is_same_v<Iter, ConstIter>);
+    using Sentinel      = std::ranges::sentinel_t<View>;
+    using ConstSentinel = std::ranges::sentinel_t<const View>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(HasMinus<Iter, Sentinel>);
+    static_assert(HasMinus<Sentinel, Iter>);
+    static_assert(HasMinus<ConstIter, ConstSentinel>);
+    static_assert(HasMinus<ConstSentinel, ConstIter>);
+
+    View v{NonSimpleForwardSizedNonCommon{buffer}};
+
+    auto it       = v.begin();
+    auto const_it = std::as_const(v).begin();
+    auto st       = v.end();
+    auto const_st = std::as_const(v).end();
+
+    int n = N;
+
+    assert(it - st == (n - 10));
+    assert(st - it == (10 - n));
+    assert(const_it - const_st == (n - 10));
+    assert(const_st - const_it == (10 - n));
+
+    static_assert(!HasMinus<Iter, ConstSentinel>);
+    static_assert(!HasMinus<ConstSentinel, Iter>);
+    static_assert(!HasMinus<ConstIter, Sentinel>);
+    static_assert(!HasMinus<Sentinel, ConstIter>);
+  }
+
+  {
+    // const compatible allow non-const to const conversion
+    using View = std::ranges::adjacent_view<ConstCompatibleForwardSized, N>;
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!simple_view<View>);
+
+    using Iter      = std::ranges::iterator_t<View>;
+    using ConstIter = std::ranges::iterator_t<const View>;
+    static_assert(!std::is_same_v<Iter, ConstIter>);
+    using Sentinel      = std::ranges::sentinel_t<View>;
+    using ConstSentinel = std::ranges::sentinel_t<const View>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(HasMinus<Iter, Sentinel>);
+    static_assert(HasMinus<Sentinel, Iter>);
+    static_assert(HasMinus<ConstIter, ConstSentinel>);
+    static_assert(HasMinus<ConstSentinel, ConstIter>);
+    static_assert(HasMinus<Iter, ConstSentinel>);
+    static_assert(HasMinus<ConstSentinel, Iter>);
+    static_assert(HasMinus<ConstIter, Sentinel>);
+    static_assert(HasMinus<Sentinel, ConstIter>);
+
+    View v{ConstCompatibleForwardSized{buffer}};
+
+    auto it       = v.begin();
+    auto const_it = std::as_const(v).begin();
+    auto st       = v.end();
+    auto const_st = std::as_const(v).end();
+
+    int n = N;
+
+    assert(it - st == (n - 10));
+    assert(st - it == (10 - n));
+    assert(const_it - const_st == (n - 10));
+    assert(const_st - const_it == (10 - n));
+    assert(it - const_st == (n - 10));
+    assert(const_st - it == (10 - n));
+    assert(const_it - st == (n - 10));
+    assert(st - const_it == (10 - n));
+  }
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<4>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp
new file mode 100644
index 0000000000000..be88d27b54654
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr auto size() requires sized_range<View>
+// constexpr auto size() const requires sized_range<const View>
+
+#include <ranges>
+
+#include "test_macros.h"
+#include "../range_adaptor_types.h"
+
+int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+struct View : std::ranges::view_base {
+  std::size_t size_ = 0;
+  constexpr View(std::size_t s) : size_(s) {}
+  constexpr auto begin() const { return buffer; }
+  constexpr auto end() const { return buffer + size_; }
+};
+
+struct SizedNonConst : std::ranges::view_base {
+  using iterator    = forward_iterator<int*>;
+  std::size_t size_ = 0;
+  constexpr SizedNonConst(std::size_t s) : size_(s) {}
+  constexpr auto begin() const { return iterator{buffer}; }
+  constexpr auto end() const { return iterator{buffer + size_}; }
+  constexpr std::size_t size() { return size_; }
+};
+
+struct StrangeSizeView : std::ranges::view_base {
+  constexpr auto begin() const { return buffer; }
+  constexpr auto end() const { return buffer + 8; }
+
+  constexpr auto size() { return 5; }
+  constexpr auto size() const { return 6; }
+};
+
+// Test with different values of N for a sized view
+template <std::size_t N>
+constexpr void test_sized_view() {
+  std::ranges::adjacent_view<View, N> v(View(8));
+  static_assert(std::ranges::sized_range<decltype(v)>);
+  static_assert(std::ranges::sized_range<const decltype(v)>);
+
+  auto expected_size = 8 - (N - 1);
+  assert(v.size() == expected_size);
+  assert(std::as_const(v).size() == expected_size);
+}
+
+// Test with different values of N for a non-const sized view
+template <std::size_t N>
+constexpr void test_nonconst_sized() {
+  std::ranges::adjacent_view<SizedNonConst, N> v(SizedNonConst(5));
+  static_assert(std::ranges::sized_range<decltype(v)>);
+  static_assert(!std::ranges::sized_range<const decltype(v)>);
+
+  auto expected_size = 5 - (N - 1);
+  assert(v.size() == expected_size);
+}
+
+// Test with different values of N for a view with different const/non-const sizes
+template <std::size_t N>
+constexpr void test_strange_size() {
+  std::ranges::adjacent_view<StrangeSizeView, N> v(StrangeSizeView{});
+  static_assert(std::ranges::sized_range<decltype(v)>);
+  static_assert(std::ranges::sized_range<const decltype(v)>);
+
+  assert(v.size() == 5 - (N - 1));
+  assert(std::as_const(v).size() == 6 - (N - 1));
+}
+
+template <std::size_t N>
+constexpr void test_empty_range() {
+  std::ranges::adjacent_view<View, N> v(View(0));
+  static_assert(std::ranges::sized_range<decltype(v)>);
+  static_assert(std::ranges::sized_range<const decltype(v)>);
+
+  assert(v.size() == 0);
+  assert(std::as_const(v).size() == 0);
+}
+
+template <std::size_t N>
+constexpr void test_N_greater_than_size() {
+  if constexpr (N > 2) {
+    std::ranges::adjacent_view<View, N> v(View(2));
+    static_assert(std::ranges::sized_range<decltype(v)>);
+    static_assert(std::ranges::sized_range<const decltype(v)>);
+    assert(v.size() == 0);
+    assert(std::as_const(v).size() == 0);
+  }
+}
+
+template <std::size_t N>
+constexpr void test() {
+  test_sized_view<N>();
+  test_nonconst_sized<N>();
+  test_strange_size<N>();
+  test_empty_range<N>();
+  test_N_greater_than_size<N>();
+}
+
+constexpr bool test() {
+  test<1>();
+  test<2>();
+  test<3>();
+  test<5>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp
index fc29a00014f67..48a0a6d206db5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp
@@ -20,83 +20,7 @@
 #include <tuple>
 
 #include "../types.h"
-
-template <class Base = int*>
-struct convertible_forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
-
-  convertible_forward_sized_iterator() = default;
-  constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}
-
-  template <std::convertible_to<Base> U>
-  constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}
-
-  constexpr decltype(*Base{}) operator*() const { return *it_; }
-
-  constexpr convertible_forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool
-  operator==(const convertible_forward_sized_iterator&, const convertible_forward_sized_iterator&) = default;
-
-  friend constexpr difference_type
-  operator-(const convertible_forward_sized_iterator& x, const convertible_forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
-static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
-
-template <class Base>
-struct convertible_sized_sentinel {
-  Base base_;
-  explicit convertible_sized_sentinel() = default;
-  constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
-
-  template <std::convertible_to<Base> U>
-  constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
-
-  template <class U>
-    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
-  friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
-    return s.base_ == base;
-  }
-  template <class U>
-    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
-  friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
-    return s.base_ - i;
-  }
-
-  template <class U>
-    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
-  friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
-    return i - s.base_;
-  }
-};
-static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
-                                      convertible_forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
-                                      convertible_forward_sized_iterator<int*>>);
-static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
-                                      convertible_forward_sized_iterator<const int*>>);
-
-struct ConstCompatibleForwardSized : IntBufferView {
-  using IntBufferView::IntBufferView;
-
-  using iterator       = convertible_forward_sized_iterator<int*>;
-  using const_iterator = convertible_forward_sized_iterator<const int*>;
-
-  constexpr iterator begin() { return {buffer_}; }
-  constexpr const_iterator begin() const { return {buffer_}; }
-  constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
-  constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
-};
+#include "../../range_adaptor_types.h"
 
 template <class T, class U>
 concept HasMinus = std::invocable<std::minus<>, const T&, const U&>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp
index bcfa3407e6cb1..61f8935af3c4a 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp
@@ -28,83 +28,6 @@
 
 #include "../../range_adaptor_types.h"
 
-template <class Base = int*>
-struct convertible_forward_sized_iterator {
-  Base it_ = nullptr;
-
-  using iterator_category = std::forward_iterator_tag;
-  using value_type = int;
-  using difference_type = std::intptr_t;
-
-  convertible_forward_sized_iterator() = default;
-  constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}
-
-  template <std::convertible_to<Base> U>
-  constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}
-
-  constexpr decltype(*Base{}) operator*() const { return *it_; }
-
-  constexpr convertible_forward_sized_iterator& operator++() {
-    ++it_;
-    return *this;
-  }
-  constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
-
-  friend constexpr bool operator==(const convertible_forward_sized_iterator&,
-                                   const convertible_forward_sized_iterator&) = default;
-
-  friend constexpr difference_type operator-(const convertible_forward_sized_iterator& x,
-                                             const convertible_forward_sized_iterator& y) {
-    return x.it_ - y.it_;
-  }
-};
-static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
-
-template <class Base>
-struct convertible_sized_sentinel {
-  Base base_;
-  explicit convertible_sized_sentinel() = default;
-  constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
-
-  template <std::convertible_to<Base> U>
-  constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
-
-  template <class U>
-    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
-  friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
-    return s.base_ == base;
-  }
-  template <class U>
-    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
-  friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
-    return s.base_ - i;
-  }
-
-  template <class U>
-    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
-  friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
-    return i - s.base_;
-  }
-};
-static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
-                                      convertible_forward_sized_iterator<>>);
-static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
-                                      convertible_forward_sized_iterator<int*>>);
-static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
-                                      convertible_forward_sized_iterator<const int*>>);
-
-struct ConstCompatibleForwardSized : IntBufferView {
-  using IntBufferView::IntBufferView;
-
-  using iterator = convertible_forward_sized_iterator<int*>;
-  using const_iterator = convertible_forward_sized_iterator<const int*>;
-
-  constexpr iterator begin() { return {buffer_}; }
-  constexpr const_iterator begin() const { return {buffer_}; }
-  constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
-  constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
-};
-
 // clang-format off
 template <class T, class U>
 concept HasMinus = std::invocable<std::minus<>,const T&, const U&>;
@@ -161,10 +84,10 @@ constexpr bool test() {
     static_assert(!std::ranges::common_range<decltype(v)>);
     static_assert(!simple_view<decltype(v)>);
 
-    using Iter = std::ranges::iterator_t<decltype(v)>;
+    using Iter      = std::ranges::iterator_t<decltype(v)>;
     using ConstIter = std::ranges::iterator_t<const decltype(v)>;
     static_assert(!std::is_same_v<Iter, ConstIter>);
-    using Sentinel = std::ranges::sentinel_t<decltype(v)>;
+    using Sentinel      = std::ranges::sentinel_t<decltype(v)>;
     using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
     static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
 
@@ -172,9 +95,9 @@ constexpr bool test() {
     static_assert(HasMinus<Sentinel, Iter>);
     static_assert(HasMinus<ConstIter, ConstSentinel>);
     static_assert(HasMinus<ConstSentinel, ConstIter>);
-    auto it = v.begin();
+    auto it       = v.begin();
     auto const_it = std::as_const(v).begin();
-    auto st = v.end();
+    auto st       = v.end();
     auto const_st = std::as_const(v).end();
     assert(it - st == -5);
     assert(st - it == 5);
@@ -193,10 +116,10 @@ constexpr bool test() {
     static_assert(!std::ranges::common_range<decltype(v)>);
     static_assert(!simple_view<decltype(v)>);
 
-    using Iter = std::ranges::iterator_t<decltype(v)>;
+    using Iter      = std::ranges::iterator_t<decltype(v)>;
     using ConstIter = std::ranges::iterator_t<const decltype(v)>;
     static_assert(!std::is_same_v<Iter, ConstIter>);
-    using Sentinel = std::ranges::sentinel_t<decltype(v)>;
+    using Sentinel      = std::ranges::sentinel_t<decltype(v)>;
     using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
     static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
 
@@ -209,9 +132,9 @@ constexpr bool test() {
     static_assert(HasMinus<ConstIter, Sentinel>);
     static_assert(HasMinus<Sentinel, ConstIter>);
 
-    auto it = v.begin();
+    auto it       = v.begin();
     auto const_it = std::as_const(v).begin();
-    auto st = v.end();
+    auto st       = v.end();
     auto const_st = std::as_const(v).end();
 
     assert(it - st == -5);
diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
index 288a78ac722e6..da5b94d75f466 100644
--- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h
@@ -429,13 +429,18 @@ static_assert(!simple_view<NonSimpleSizedBidiNonCommonView>);
 
 namespace adltest {
 struct iter_move_swap_iterator {
-  std::reference_wrapper<int> iter_move_called_times;
-  std::reference_wrapper<int> iter_swap_called_times;
-  int i = 0;
+  int* iter_move_called_times = nullptr;
+  int* iter_swap_called_times = nullptr;
+  int i                       = 0;
 
-  using iterator_category = std::input_iterator_tag;
-  using value_type        = int;
-  using difference_type   = std::intptr_t;
+  constexpr iter_move_swap_iterator() = default;
+  constexpr iter_move_swap_iterator(int& iter_move_called_times_ref, int& iter_swap_called_times_ref)
+      : iter_move_called_times(&iter_move_called_times_ref),
+        iter_swap_called_times(&iter_swap_called_times_ref),
+        i(0) {}
+
+  using value_type      = int;
+  using difference_type = std::intptr_t;
 
   constexpr int operator*() const { return i; }
 
@@ -443,17 +448,24 @@ struct iter_move_swap_iterator {
     ++i;
     return *this;
   }
-  constexpr void operator++(int) { ++i; }
+  constexpr iter_move_swap_iterator operator++(int) {
+    auto tmp = *this;
+    ++*this;
+    return tmp;
+  }
 
   friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; }
+  friend constexpr bool operator==(const iter_move_swap_iterator& x, const iter_move_swap_iterator& y) {
+    return x.i == y.i;
+  }
 
   friend constexpr int iter_move(iter_move_swap_iterator const& it) {
-    ++it.iter_move_called_times;
+    ++*it.iter_move_called_times;
     return it.i;
   }
   friend constexpr void iter_swap(iter_move_swap_iterator const& x, iter_move_swap_iterator const& y) {
-    ++x.iter_swap_called_times;
-    ++y.iter_swap_called_times;
+    ++*x.iter_swap_called_times;
+    ++*y.iter_swap_called_times;
   }
 };
 
@@ -465,4 +477,81 @@ struct IterMoveSwapRange {
 };
 } // namespace adltest
 
+template <class Base = int*>
+struct convertible_forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type        = int;
+  using difference_type   = std::intptr_t;
+
+  convertible_forward_sized_iterator() = default;
+  constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}
+
+  template <std::convertible_to<Base> U>
+  constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}
+
+  constexpr decltype(*Base{}) operator*() const { return *it_; }
+
+  constexpr convertible_forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool
+  operator==(const convertible_forward_sized_iterator&, const convertible_forward_sized_iterator&) = default;
+
+  friend constexpr difference_type
+  operator-(const convertible_forward_sized_iterator& x, const convertible_forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
+
+template <class Base>
+struct convertible_sized_sentinel {
+  Base base_;
+  explicit convertible_sized_sentinel() = default;
+  constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
+
+  template <std::convertible_to<Base> U>
+  constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
+
+  template <class U>
+    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+  friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
+    return s.base_ == base;
+  }
+  template <class U>
+    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+  friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
+    return s.base_ - i;
+  }
+
+  template <class U>
+    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+  friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
+    return i - s.base_;
+  }
+};
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
+                                      convertible_forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
+                                      convertible_forward_sized_iterator<int*>>);
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
+                                      convertible_forward_sized_iterator<const int*>>);
+
+struct ConstCompatibleForwardSized : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  using iterator       = convertible_forward_sized_iterator<int*>;
+  using const_iterator = convertible_forward_sized_iterator<const int*>;
+
+  constexpr iterator begin() { return {buffer_}; }
+  constexpr const_iterator begin() const { return {buffer_}; }
+  constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
+  constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
+};
+
 #endif //  TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H
diff --git a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp
index 505ed2e77ae3b..43235e23a6a98 100644
--- a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp
+++ b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp
@@ -56,6 +56,7 @@ constexpr bool test() {
   testOne<std::ranges::single_view<Pred>>();
 
 #if TEST_STD_VER >= 23
+  testOne<std::ranges::adjacent_view<View, 5>>();
   testOne<std::ranges::chunk_by_view<View, Pred>>();
   testOne<std::ranges::repeat_view<Pred>>();
   testOne<std::ranges::zip_transform_view<Pred, View>>();

>From ea35818e89d8334a9077ff222240f8fb5b91b019 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 22 Nov 2025 16:18:12 +0000
Subject: [PATCH 03/10] review

---
 libcxx/include/CMakeLists.txt                 |  2 +-
 libcxx/include/__ranges/adjacent_view.h       | 10 ++---
 libcxx/include/__ranges/zip_view.h            | 30 ++++++-------
 .../zip_utils.h => __tuple/tuple_transform.h} |  9 ++--
 libcxx/include/module.modulemap.in            |  4 +-
 .../range.adjacent/end.pass.cpp               | 42 ++++++++++++++++++-
 .../iterator/subscript.pass.cpp               | 22 +++++++++-
 7 files changed, 85 insertions(+), 34 deletions(-)
 rename libcxx/include/{__ranges/zip_utils.h => __tuple/tuple_transform.h} (88%)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 08b8780815df2..44241079d82c4 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -744,7 +744,6 @@ set(files
   __ranges/view_interface.h
   __ranges/views.h
   __ranges/zip_transform_view.h
-  __ranges/zip_utils.h
   __ranges/zip_view.h
   __split_buffer
   __std_mbstate_t.h
@@ -790,6 +789,7 @@ set(files
   __tuple/tuple_like.h
   __tuple/tuple_like_no_subrange.h
   __tuple/tuple_size.h
+  __tuple/tuple_transform.h
   __type_traits/add_cv_quals.h
   __type_traits/add_pointer.h
   __type_traits/add_reference.h
diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index c31ce2cd75d3c..db02584f59744 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -35,7 +35,7 @@
 #include <__ranges/range_adaptor.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
-#include <__ranges/zip_utils.h>
+#include <__tuple/tuple_transform.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
@@ -151,7 +151,7 @@ class adjacent_view<_View, _Np>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(iterator_t<_Base> __first, sentinel_t<_Base> __last) {
     __current_[0] = __first;
-    for (int __i = 1; __i < static_cast<int>(_Np); ++__i) {
+    for (size_t __i = 1; __i < _Np; ++__i) {
       __current_[__i] = ranges::next(__current_[__i - 1], 1, __last);
     }
   }
@@ -195,7 +195,7 @@ class adjacent_view<_View, _Np>::__iterator {
       : __iterator(std::move(__i), make_index_sequence<_Np>{}) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
-    return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
+    return std::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
@@ -249,7 +249,7 @@ class adjacent_view<_View, _Np>::__iterator {
   _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
     requires random_access_range<_Base>
   {
-    return ranges::__tuple_transform([&](auto& __i) -> decltype(auto) { return __i[__n]; }, __current_);
+    return std::__tuple_transform([&](auto& __i) -> decltype(auto) { return __i[__n]; }, __current_);
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
@@ -317,7 +317,7 @@ class adjacent_view<_View, _Np>::__iterator {
   _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
       noexcept(ranges::iter_move(std::declval<const iterator_t<_Base>&>())) &&
       is_nothrow_move_constructible_v<range_rvalue_reference_t<_Base>>) {
-    return ranges::__tuple_transform(ranges::iter_move, __i.__current_);
+    return std::__tuple_transform(ranges::iter_move, __i.__current_);
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept(
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index bd54f37028e45..bea64c4997a9e 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___RANGES_ZIP_UTILS_H
-#define _LIBCPP___RANGES_ZIP_UTILS_H
+#ifndef _LIBCPP___RANGES_ZIP_VIEW_H
+#define _LIBCPP___RANGES_ZIP_VIEW_H
 
 #include <__config>
 
@@ -31,7 +31,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/size.h>
 #include <__ranges/view_interface.h>
-#include <__ranges/zip_utils.h>
+#include <__tuple/tuple_transform.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/make_unsigned.h>
 #include <__utility/declval.h>
@@ -137,24 +137,24 @@ class zip_view : public view_interface<zip_view<_Views...>> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
     requires(!(__simple_view<_Views> && ...))
   {
-    return __iterator<false>(ranges::__tuple_transform(ranges::begin, __views_));
+    return __iterator<false>(std::__tuple_transform(ranges::begin, __views_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
     requires(range<const _Views> && ...)
   {
-    return __iterator<true>(ranges::__tuple_transform(ranges::begin, __views_));
+    return __iterator<true>(std::__tuple_transform(ranges::begin, __views_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end()
     requires(!(__simple_view<_Views> && ...))
   {
     if constexpr (!__zip_is_common<_Views...>) {
-      return __sentinel<false>(ranges::__tuple_transform(ranges::end, __views_));
+      return __sentinel<false>(std::__tuple_transform(ranges::end, __views_));
     } else if constexpr ((random_access_range<_Views> && ...)) {
       return begin() + iter_difference_t<__iterator<false>>(size());
     } else {
-      return __iterator<false>(ranges::__tuple_transform(ranges::end, __views_));
+      return __iterator<false>(std::__tuple_transform(ranges::end, __views_));
     }
   }
 
@@ -162,11 +162,11 @@ class zip_view : public view_interface<zip_view<_Views...>> {
     requires(range<const _Views> && ...)
   {
     if constexpr (!__zip_is_common<const _Views...>) {
-      return __sentinel<true>(ranges::__tuple_transform(ranges::end, __views_));
+      return __sentinel<true>(std::__tuple_transform(ranges::end, __views_));
     } else if constexpr ((random_access_range<const _Views> && ...)) {
       return begin() + iter_difference_t<__iterator<true>>(size());
     } else {
-      return __iterator<true>(ranges::__tuple_transform(ranges::end, __views_));
+      return __iterator<true>(std::__tuple_transform(ranges::end, __views_));
     }
   }
 
@@ -178,7 +178,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return ranges::min({_CT(__sizes)...});
         },
-        ranges::__tuple_transform(ranges::size, __views_));
+        std::__tuple_transform(ranges::size, __views_));
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
@@ -189,7 +189,7 @@ class zip_view : public view_interface<zip_view<_Views...>> {
           using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
           return ranges::min({_CT(__sizes)...});
         },
-        ranges::__tuple_transform(ranges::size, __views_));
+        std::__tuple_transform(ranges::size, __views_));
   }
 };
 
@@ -268,7 +268,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
       : __current_(std::move(__i.__current_)) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
-    return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
+    return std::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
@@ -318,7 +318,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
   _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
     requires __zip_all_random_access<_Const, _Views...>
   {
-    return ranges::__tuple_transform(
+    return std::__tuple_transform(
         [&]<class _Iter>(_Iter& __i) -> decltype(auto) { return __i[iter_difference_t<_Iter>(__n)]; }, __current_);
   }
 
@@ -377,7 +377,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
   _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept(
       (noexcept(ranges::iter_move(std::declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && ...) &&
       (is_nothrow_move_constructible_v<range_rvalue_reference_t<__maybe_const<_Const, _Views>>> && ...)) {
-    return ranges::__tuple_transform(ranges::iter_move, __i.__current_);
+    return std::__tuple_transform(ranges::iter_move, __i.__current_);
   }
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept(
@@ -496,4 +496,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___RANGES_ZIP_UTILS_H
+#endif // _LIBCPP___RANGES_ZIP_VIEW_H
diff --git a/libcxx/include/__ranges/zip_utils.h b/libcxx/include/__tuple/tuple_transform.h
similarity index 88%
rename from libcxx/include/__ranges/zip_utils.h
rename to libcxx/include/__tuple/tuple_transform.h
index f538486b6777d..92a18e0f540a4 100644
--- a/libcxx/include/__ranges/zip_utils.h
+++ b/libcxx/include/__tuple/tuple_transform.h
@@ -7,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___RANGES_ZIP_VIEW_H
-#define _LIBCPP___RANGES_ZIP_VIEW_H
+#ifndef _LIBCPP___TUPLE_TUPLE_TRANSFORM_H
+#define _LIBCPP___TUPLE_TUPLE_TRANSFORM_H
 
 #include <__config>
 
@@ -27,8 +27,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 23
 
-namespace ranges {
-
 template <class _Fun, class _Tuple>
 _LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) {
   return std::apply(
@@ -38,11 +36,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tu
       std::forward<_Tuple>(__tuple));
 }
 
-} // namespace ranges
 #endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP___RANGES_ZIP_VIEW_H
+#endif // _LIBCPP___TUPLE_TUPLE_TRANSFORM_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 136e92bf7daee..f714afe680dc4 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1948,9 +1948,6 @@ module std [system] {
       header "__ranges/zip_view.h"
       export std.utility.pair
     }
-    module zip_utils {
-      header "__ranges/zip_utils.h"
-    }
     module zip_transform_view {
       header "__ranges/zip_transform_view.h"
     }
@@ -2122,6 +2119,7 @@ module std [system] {
     module tuple_like_no_subrange   { header "__tuple/tuple_like_no_subrange.h" }
     module tuple_like               { header "__tuple/tuple_like.h" }
     module tuple_size               { header "__tuple/tuple_size.h" }
+    module tuple_trasnform          { header "__tuple/tuple_transform.h" }
 
     header "tuple"
     export *
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
index 3ce45f49920fe..4629336d97449 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
@@ -13,6 +13,7 @@
 
 #include <array>
 #include <cassert>
+#include <iterator>
 #include <ranges>
 #include <type_traits>
 #include <utility>
@@ -31,8 +32,10 @@ constexpr void test_one() {
     auto cendIt = std::as_const(v).end();
     assert(it != endIt);
     assert(cit != cendIt);
-    assert(it + (8 - (N - 1)) == endIt);
-    assert(cit + (8 - (N - 1)) == cendIt);
+    std::ranges::advance(it, 8 - (N - 1));
+    std::ranges::advance(cit, 8 - (N - 1));
+    assert(it == endIt);
+    assert(cit == cendIt);
   }
   {
     // empty range
@@ -58,6 +61,37 @@ constexpr void test_one() {
   }
 }
 
+template <class Underlying, std::size_t N>
+constexpr void test_decrement_back() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  std::ranges::adjacent_view<Underlying, N> v{Underlying{buffer}};
+  auto endIt  = v.end();
+  auto cendIt = std::as_const(v).end();
+
+  --endIt;
+  --cendIt;
+  auto tuple  = *endIt;
+  auto ctuple = *cendIt;
+  assert(std::get<0>(tuple) == buffer[8 - N]);
+  assert(std::get<0>(ctuple) == buffer[8 - N]);
+  if constexpr (N >= 2) {
+    assert(std::get<1>(tuple) == buffer[9 - N]);
+    assert(std::get<1>(ctuple) == buffer[9 - N]);
+  }
+  if constexpr (N >= 3) {
+    assert(std::get<2>(tuple) == buffer[10 - N]);
+    assert(std::get<2>(ctuple) == buffer[10 - N]);
+  }
+  if constexpr (N >= 4) {
+    assert(std::get<3>(tuple) == buffer[11 - N]);
+    assert(std::get<3>(ctuple) == buffer[11 - N]);
+  }
+  if constexpr (N >= 5) {
+    assert(std::get<4>(tuple) == buffer[12 - N]);
+    assert(std::get<4>(ctuple) == buffer[12 - N]);
+  }
+}
+
 template <std::size_t N>
 constexpr void test_simple_common_types() {
   using NonConstView = std::ranges::adjacent_view<SimpleCommon, N>;
@@ -68,6 +102,7 @@ constexpr void test_simple_common_types() {
   static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
   test_one<SimpleCommon, N>();
+  test_decrement_back<SimpleCommon, N>();
 }
 
 template <std::size_t N>
@@ -92,6 +127,7 @@ constexpr void test_non_simple_common_types() {
   static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
   test_one<NonSimpleCommon, N>();
+  test_decrement_back<NonSimpleCommon, N>();
 }
 
 template <std::size_t N>
@@ -116,6 +152,7 @@ constexpr void test_forward_only() {
   static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
   test_one<NonSimpleForwardSizedNonCommon, N>();
+  test_one<ForwardSizedView, N>();
 }
 
 template <std::size_t N>
@@ -124,6 +161,7 @@ constexpr void test() {
   test_simple_non_common_types<N>();
   test_non_simple_common_types<N>();
   test_non_simple_non_common_types<N>();
+  test_forward_only<N>();
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
index 389fbe9dc98d4..2a8e8614712a8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
@@ -14,6 +14,7 @@
 #include <ranges>
 #include <cassert>
 
+#include "../helpers.h"
 #include "../../range_adaptor_types.h"
 
 template <std::size_t N>
@@ -26,8 +27,9 @@ constexpr void test() {
     auto it = v.begin();
     assert(it[0] == *it);
     assert(it[2] == *(it + 2));
+    assert(&std::get<0>(it[2]) == &buffer[2]);
 
-    static_assert(std::is_same_v<decltype(it[0]), decltype(*it)>);
+    static_assert(std::is_same_v<decltype(it[0]), expectedTupleType<N, int&>>);
   }
 
   {
@@ -36,8 +38,24 @@ constexpr void test() {
     auto it = v.begin();
     assert(it[0] == *it);
     assert(it[2] == *(it + 2));
+    assert(&std::get<0>(it[0]) == &buffer[0]);
 
-    static_assert(std::is_same_v<decltype(it[0]), decltype(*it)>);
+    static_assert(std::is_same_v<decltype(it[0]), expectedTupleType<N, int&>>);
+  }
+
+  {
+    // underlying range with prvalue range_reference_t
+    auto v                                                     = std::views::iota(0, 8) | std::views::adjacent<N>;
+    std::same_as<expectedTupleType<N, int>> decltype(auto) res = v.begin()[2];
+    assert(std::get<0>(res) == 2);
+    if constexpr (N >= 2)
+      assert(std::get<1>(res) == 3);
+    if constexpr (N >= 3)
+      assert(std::get<2>(res) == 4);
+    if constexpr (N >= 4)
+      assert(std::get<3>(res) == 5);
+    if constexpr (N >= 5)
+      assert(std::get<4>(res) == 6);
   }
 
   {

>From c18ec0a10d90801b6160582a36ad1a3e35937b27 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Dec 2025 16:33:30 +0000
Subject: [PATCH 04/10] review

---
 libcxx/include/__ranges/adjacent_view.h       |   2 +-
 .../range.adjacent/adaptor.pass.cpp           |   2 +-
 .../range.adjacent/begin.pass.cpp             |  31 +++--
 .../range.adjacent/ctor.views.pass.cpp        |  10 +-
 .../range.adjacent/end.pass.cpp               | 106 ++++++++----------
 .../range.adaptors/range.adjacent/helpers.h   |   6 +-
 .../iterator/ctor.default.pass.cpp            |   2 +-
 .../iterator/ctor.other.pass.cpp              |   2 +-
 .../iterator/member_types.compile.pass.cpp    |   2 +-
 .../range.adjacent/iterator/singular.pass.cpp |   5 +-
 .../iterator/subscript.pass.cpp               |  16 +++
 .../range.adjacent/size.pass.cpp              |  93 +++++++--------
 12 files changed, 135 insertions(+), 142 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index db02584f59744..c9412f47a969a 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -191,7 +191,7 @@ class adjacent_view<_View, _Np>::__iterator {
 
   _LIBCPP_HIDE_FROM_ABI __iterator() = default;
   _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
-    requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
+    requires _Const && convertible_to<iterator_t<_View>, iterator_t<const _View>>
       : __iterator(std::move(__i), make_index_sequence<_Np>{}) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp
index ffd0f52765734..5fd4e803955ad 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp
@@ -48,7 +48,7 @@ constexpr void test_all_constraints() {
   test_constraints<5>();
 }
 
-constexpr void test_zero_case() {
+static constexpr void test_zero_case() {
   // N == 0 is a special case that always results in an empty range
   int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp
index c17138bd6a5a3..e32b71a8bcaf5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp
@@ -91,26 +91,21 @@ constexpr void test_one() {
   }
 }
 
-template <std::size_t N>
-constexpr void test_simple() {
-  test_one<SimpleCommon, N>();
-
-  using View = std::ranges::adjacent_view<SimpleCommon, N>;
-  static_assert(std::is_same_v<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-}
-
-template <std::size_t N>
-constexpr void test_non_simple() {
-  test_one<NonSimpleCommon, N>();
-
-  using View = std::ranges::adjacent_view<NonSimpleCommon, N>;
-  static_assert(!std::is_same_v<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
-}
-
 template <std::size_t N>
 constexpr void test() {
-  test_simple<N>();
-  test_non_simple<N>();
+  {
+    // Test with simple view
+    test_one<SimpleCommon, N>();
+    using View = std::ranges::adjacent_view<SimpleCommon, N>;
+    static_assert(std::is_same_v<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+  }
+
+  {
+    // Test with non-simple view
+    test_one<NonSimpleCommon, N>();
+    using View = std::ranges::adjacent_view<NonSimpleCommon, N>;
+    static_assert(!std::is_same_v<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
+  }
 
   // Test with view that doesn't support const begin()
   using ViewWithNoConstBegin = std::ranges::adjacent_view<NoConstBeginView, N>;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp
index c060da36c4c01..6add56fe7a2d5 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp
@@ -42,7 +42,7 @@ struct MoveAwareView : std::ranges::view_base {
 };
 
 template <class View, std::size_t N>
-constexpr void constructorTest(auto&& buffer) {
+constexpr void constructor_test(auto&& buffer) {
   std::ranges::adjacent_view<View, N> v{View{buffer}};
   auto tuple = *v.begin();
   assert(std::get<0>(tuple) == buffer[0]);
@@ -66,22 +66,22 @@ constexpr void test() {
 
   // forward
   {
-    constructorTest<ForwardSizedView, N>(buffer);
+    constructor_test<ForwardSizedView, N>(buffer);
   }
 
   // bidi
   {
-    constructorTest<BidiCommonView, N>(buffer);
+    constructor_test<BidiCommonView, N>(buffer);
   }
 
   // random_access
   {
-    constructorTest<SizedRandomAccessView, N>(buffer);
+    constructor_test<SizedRandomAccessView, N>(buffer);
   }
 
   // contiguous
   {
-    constructorTest<ContiguousCommonView, N>(buffer);
+    constructor_test<ContiguousCommonView, N>(buffer);
   }
 }
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
index 4629336d97449..0ba714699b37d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp
@@ -93,75 +93,67 @@ constexpr void test_decrement_back() {
 }
 
 template <std::size_t N>
-constexpr void test_simple_common_types() {
-  using NonConstView = std::ranges::adjacent_view<SimpleCommon, N>;
-  using ConstView    = const NonConstView;
-
-  static_assert(std::ranges::common_range<NonConstView>);
-  static_assert(std::ranges::common_range<ConstView>);
-  static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
-
-  test_one<SimpleCommon, N>();
-  test_decrement_back<SimpleCommon, N>();
-}
-
-template <std::size_t N>
-constexpr void test_simple_non_common_types() {
-  using NonConstView = std::ranges::adjacent_view<SimpleNonCommon, N>;
-  using ConstView    = const NonConstView;
+constexpr void test() {
+  {
+    // simple common
+    using NonConstView = std::ranges::adjacent_view<SimpleCommon, N>;
+    using ConstView    = const NonConstView;
 
-  static_assert(!std::ranges::common_range<NonConstView>);
-  static_assert(!std::ranges::common_range<ConstView>);
-  static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+    static_assert(std::ranges::common_range<NonConstView>);
+    static_assert(std::ranges::common_range<ConstView>);
+    static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
-  test_one<SimpleNonCommon, N>();
-}
+    test_one<SimpleCommon, N>();
+    test_decrement_back<SimpleCommon, N>();
+  }
+  {
+    // simple non common
 
-template <std::size_t N>
-constexpr void test_non_simple_common_types() {
-  using NonConstView = std::ranges::adjacent_view<NonSimpleCommon, N>;
-  using ConstView    = const NonConstView;
+    using NonConstView = std::ranges::adjacent_view<SimpleNonCommon, N>;
+    using ConstView    = const NonConstView;
 
-  static_assert(std::ranges::common_range<NonConstView>);
-  static_assert(std::ranges::common_range<ConstView>);
-  static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+    static_assert(!std::ranges::common_range<NonConstView>);
+    static_assert(!std::ranges::common_range<ConstView>);
+    static_assert(std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
-  test_one<NonSimpleCommon, N>();
-  test_decrement_back<NonSimpleCommon, N>();
-}
+    test_one<SimpleNonCommon, N>();
+  }
 
-template <std::size_t N>
-constexpr void test_non_simple_non_common_types() {
-  using NonConstView = std::ranges::adjacent_view<NonSimpleNonCommon, N>;
-  using ConstView    = const NonConstView;
+  {
+    // non simple common
+    using NonConstView = std::ranges::adjacent_view<NonSimpleCommon, N>;
+    using ConstView    = const NonConstView;
 
-  static_assert(!std::ranges::common_range<NonConstView>);
-  static_assert(!std::ranges::common_range<ConstView>);
-  static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+    static_assert(std::ranges::common_range<NonConstView>);
+    static_assert(std::ranges::common_range<ConstView>);
+    static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
-  test_one<NonSimpleNonCommon, N>();
-}
+    test_one<NonSimpleCommon, N>();
+    test_decrement_back<NonSimpleCommon, N>();
+  }
+  {
+    // non simple non common
+    using NonConstView = std::ranges::adjacent_view<NonSimpleNonCommon, N>;
+    using ConstView    = const NonConstView;
 
-template <std::size_t N>
-constexpr void test_forward_only() {
-  using NonConstView = std::ranges::adjacent_view<NonSimpleForwardSizedNonCommon, N>;
-  using ConstView    = const NonConstView;
+    static_assert(!std::ranges::common_range<NonConstView>);
+    static_assert(!std::ranges::common_range<ConstView>);
+    static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
-  static_assert(!std::ranges::common_range<NonConstView>);
-  static_assert(!std::ranges::common_range<ConstView>);
-  static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
+    test_one<NonSimpleNonCommon, N>();
+  }
+  {
+    // forward only
+    using NonConstView = std::ranges::adjacent_view<NonSimpleForwardSizedNonCommon, N>;
+    using ConstView    = const NonConstView;
 
-  test_one<NonSimpleForwardSizedNonCommon, N>();
-  test_one<ForwardSizedView, N>();
-}
+    static_assert(!std::ranges::common_range<NonConstView>);
+    static_assert(!std::ranges::common_range<ConstView>);
+    static_assert(!std::is_same_v<std::ranges::sentinel_t<NonConstView>, std::ranges::sentinel_t<ConstView>>);
 
-template <std::size_t N>
-constexpr void test() {
-  test_simple_common_types<N>();
-  test_simple_non_common_types<N>();
-  test_non_simple_common_types<N>();
-  test_non_simple_non_common_types<N>();
-  test_forward_only<N>();
+    test_one<NonSimpleForwardSizedNonCommon, N>();
+    test_one<ForwardSizedView, N>();
+  }
 }
 
 constexpr bool test() {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h
index cef29689ea301..88af9f0c2fade 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h
@@ -11,7 +11,9 @@
 
 #include <tuple>
 
-// intentionally not using meta programming for the expected tuple types
+// We intentionally don't use metaprogramming to define the expected tuple types
+// because otherwise the test code would be basically the same as the source code
+// we're trying to test.
 
 template <std::size_t N, class T>
 struct ExpectedTupleType;
@@ -40,4 +42,4 @@ struct ExpectedTupleType<5, T> {
 template <std::size_t N, class T>
 using expectedTupleType = typename ExpectedTupleType<N, T>::type;
 
-#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H
\ No newline at end of file
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp
index 5c1fb15b443e0..8cb005a9d3f8f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp
@@ -16,7 +16,7 @@
 #include "../../range_adaptor_types.h"
 
 struct PODIter {
-  int i; // deliberately uninitialised
+  int i; // deliberately uninitialised because we're testing that default ctor of the iterator zero initialises the underlying iterators
 
   using iterator_category = std::random_access_iterator_tag;
   using value_type        = int;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp
index 7d47ca7761ae1..db70e1affe3ee 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // constexpr iterator(iterator<!Const> i)
-//      requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>;
+//      requires Const && convertible_to<iterator_t<V>, iterator_t<const V>>;
 
 #include <ranges>
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
index fc70fc4eed162..fc19adbbc32f2 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
@@ -147,4 +147,4 @@ void test() {
   test<2>();
   test<3>();
   test<5>();
-}
\ No newline at end of file
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp
index de912ca50bac7..c1cc9498d39e1 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp
@@ -9,7 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 // UNSUPPORTED: no-exceptions
 
-// If the invocation of any non-const member function of `iterator` exits via an
+// [range.adjacent.iterator#2] If the invocation of any non-const member function of `iterator` exits via an
 // exception, the iterator acquires a singular value.
 
 #include <ranges>
@@ -64,10 +64,11 @@ void test() {
     } catch (int e) {
       assert(e == 5);
     }
+    // destroy the iterator
   }
 
   {
-    // adjacent_view iterator should be able to be assigned after member function throws
+    // adjacent_view iterator should be assignable after member function throws
     auto v  = ThrowOnIncrementView{buffer} | std::views::adjacent<N>;
     auto it = v.begin();
     ++it;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
index 2a8e8614712a8..0f262b7b65a5c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
@@ -28,6 +28,14 @@ constexpr void test() {
     assert(it[0] == *it);
     assert(it[2] == *(it + 2));
     assert(&std::get<0>(it[2]) == &buffer[2]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(it[2]) == &buffer[3]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(it[2]) == &buffer[4]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(it[2]) == &buffer[5]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(it[2]) == &buffer[6]);
 
     static_assert(std::is_same_v<decltype(it[0]), expectedTupleType<N, int&>>);
   }
@@ -39,6 +47,14 @@ constexpr void test() {
     assert(it[0] == *it);
     assert(it[2] == *(it + 2));
     assert(&std::get<0>(it[0]) == &buffer[0]);
+    if constexpr (N >= 2)
+      assert(&std::get<1>(it[0]) == &buffer[1]);
+    if constexpr (N >= 3)
+      assert(&std::get<2>(it[0]) == &buffer[2]);
+    if constexpr (N >= 4)
+      assert(&std::get<3>(it[0]) == &buffer[3]);
+    if constexpr (N >= 5)
+      assert(&std::get<4>(it[0]) == &buffer[4]);  
 
     static_assert(std::is_same_v<decltype(it[0]), expectedTupleType<N, int&>>);
   }
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp
index be88d27b54654..79b20e56f53f3 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp
@@ -41,68 +41,55 @@ struct StrangeSizeView : std::ranges::view_base {
   constexpr auto size() const { return 6; }
 };
 
-// Test with different values of N for a sized view
 template <std::size_t N>
-constexpr void test_sized_view() {
-  std::ranges::adjacent_view<View, N> v(View(8));
-  static_assert(std::ranges::sized_range<decltype(v)>);
-  static_assert(std::ranges::sized_range<const decltype(v)>);
-
-  auto expected_size = 8 - (N - 1);
-  assert(v.size() == expected_size);
-  assert(std::as_const(v).size() == expected_size);
-}
-
-// Test with different values of N for a non-const sized view
-template <std::size_t N>
-constexpr void test_nonconst_sized() {
-  std::ranges::adjacent_view<SizedNonConst, N> v(SizedNonConst(5));
-  static_assert(std::ranges::sized_range<decltype(v)>);
-  static_assert(!std::ranges::sized_range<const decltype(v)>);
-
-  auto expected_size = 5 - (N - 1);
-  assert(v.size() == expected_size);
-}
-
-// Test with different values of N for a view with different const/non-const sizes
-template <std::size_t N>
-constexpr void test_strange_size() {
-  std::ranges::adjacent_view<StrangeSizeView, N> v(StrangeSizeView{});
-  static_assert(std::ranges::sized_range<decltype(v)>);
-  static_assert(std::ranges::sized_range<const decltype(v)>);
-
-  assert(v.size() == 5 - (N - 1));
-  assert(std::as_const(v).size() == 6 - (N - 1));
-}
+constexpr void test() {
+  {
+    // Test with different values of N for a sized view
+    std::ranges::adjacent_view<View, N> v(View(8));
+    static_assert(std::ranges::sized_range<decltype(v)>);
+    static_assert(std::ranges::sized_range<const decltype(v)>);
 
-template <std::size_t N>
-constexpr void test_empty_range() {
-  std::ranges::adjacent_view<View, N> v(View(0));
-  static_assert(std::ranges::sized_range<decltype(v)>);
-  static_assert(std::ranges::sized_range<const decltype(v)>);
+    auto expected_size = 8 - (N - 1);
+    assert(v.size() == expected_size);
+    assert(std::as_const(v).size() == expected_size);
+  }
+  {
+    // Test with different values of N for a non-const sized view
+    std::ranges::adjacent_view<SizedNonConst, N> v(SizedNonConst(5));
+    static_assert(std::ranges::sized_range<decltype(v)>);
+    static_assert(!std::ranges::sized_range<const decltype(v)>);
 
-  assert(v.size() == 0);
-  assert(std::as_const(v).size() == 0);
-}
+    auto expected_size = 5 - (N - 1);
+    assert(v.size() == expected_size);
+  }
+  {
+    // Test with different values of N for a view with different const/non-const sizes
+    std::ranges::adjacent_view<StrangeSizeView, N> v(StrangeSizeView{});
+    static_assert(std::ranges::sized_range<decltype(v)>);
+    static_assert(std::ranges::sized_range<const decltype(v)>);
 
-template <std::size_t N>
-constexpr void test_N_greater_than_size() {
-  if constexpr (N > 2) {
-    std::ranges::adjacent_view<View, N> v(View(2));
+    assert(v.size() == 5 - (N - 1));
+    assert(std::as_const(v).size() == 6 - (N - 1));
+  }
+  {
+    // empty range
+    std::ranges::adjacent_view<View, N> v(View(0));
     static_assert(std::ranges::sized_range<decltype(v)>);
     static_assert(std::ranges::sized_range<const decltype(v)>);
+
     assert(v.size() == 0);
     assert(std::as_const(v).size() == 0);
   }
-}
-
-template <std::size_t N>
-constexpr void test() {
-  test_sized_view<N>();
-  test_nonconst_sized<N>();
-  test_strange_size<N>();
-  test_empty_range<N>();
-  test_N_greater_than_size<N>();
+  {
+    // N greater than range size
+    if constexpr (N > 2) {
+      std::ranges::adjacent_view<View, N> v(View(2));
+      static_assert(std::ranges::sized_range<decltype(v)>);
+      static_assert(std::ranges::sized_range<const decltype(v)>);
+      assert(v.size() == 0);
+      assert(std::as_const(v).size() == 0);
+    }
+  }
 }
 
 constexpr bool test() {

>From 9dae73265cbccad4969534f74db82605b61aaa7a Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Dec 2025 18:44:22 +0000
Subject: [PATCH 05/10] benchmark

---
 .../benchmarks/adjacent_view_begin.bench.cpp  | 66 +++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 libcxx/test/benchmarks/adjacent_view_begin.bench.cpp

diff --git a/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp b/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
new file mode 100644
index 0000000000000..9d45b938dfe95
--- /dev/null
+++ b/libcxx/test/benchmarks/adjacent_view_begin.bench.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// ADDITIONAL_COMPILE_FLAGS: -O3
+#include <algorithm>
+#include <deque>
+#include <ranges>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+
+namespace {
+
+template <size_t N>
+void BM_adjacent_full(benchmark::State& state) {
+  const std::vector<int> inputs(1000000, 42);
+  auto view = inputs | std::views::adjacent<N>;
+  for (auto _ : state) {
+    auto it = view.begin();
+    benchmark::DoNotOptimize(it);
+  }
+}
+
+BENCHMARK(BM_adjacent_full<2>);
+BENCHMARK(BM_adjacent_full<3>);
+BENCHMARK(BM_adjacent_full<4>);
+BENCHMARK(BM_adjacent_full<5>);
+BENCHMARK(BM_adjacent_full<6>);
+BENCHMARK(BM_adjacent_full<7>);
+BENCHMARK(BM_adjacent_full<8>);
+BENCHMARK(BM_adjacent_full<9>);
+BENCHMARK(BM_adjacent_full<10>);
+BENCHMARK(BM_adjacent_full<100>);
+BENCHMARK(BM_adjacent_full<1000>);
+
+template <size_t N>
+void BM_adjacent_empty(benchmark::State& state) {
+  const std::vector<int> inputs;
+  auto view = inputs | std::views::adjacent<N>;
+  for (auto _ : state) {
+    auto it = view.begin();
+    benchmark::DoNotOptimize(it);
+  }
+}
+
+BENCHMARK(BM_adjacent_empty<2>);
+BENCHMARK(BM_adjacent_empty<3>);
+BENCHMARK(BM_adjacent_empty<4>);
+BENCHMARK(BM_adjacent_empty<5>);
+BENCHMARK(BM_adjacent_empty<6>);
+BENCHMARK(BM_adjacent_empty<7>);
+BENCHMARK(BM_adjacent_empty<8>);
+BENCHMARK(BM_adjacent_empty<9>);
+BENCHMARK(BM_adjacent_empty<10>);
+BENCHMARK(BM_adjacent_empty<100>);
+BENCHMARK(BM_adjacent_empty<1000>);
+
+} // namespace
+
+BENCHMARK_MAIN();
\ No newline at end of file

>From fe6783d3fe51334ba8830defbc440a2a7cdd743d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Dec 2025 19:20:05 +0000
Subject: [PATCH 06/10] format

---
 .../range.adaptors/range.adjacent/iterator/subscript.pass.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
index 0f262b7b65a5c..eda3ebd1f9897 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp
@@ -54,7 +54,7 @@ constexpr void test() {
     if constexpr (N >= 4)
       assert(&std::get<3>(it[0]) == &buffer[3]);
     if constexpr (N >= 5)
-      assert(&std::get<4>(it[0]) == &buffer[4]);  
+      assert(&std::get<4>(it[0]) == &buffer[4]);
 
     static_assert(std::is_same_v<decltype(it[0]), expectedTupleType<N, int&>>);
   }

>From d2997d855ed8e150d185d5773ba2dd11142883aa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Dec 2025 07:39:17 +0000
Subject: [PATCH 07/10] fix comma

fix comma
---
 libcxx/include/__ranges/adjacent_view.h           |  2 +-
 .../iterator/member_types.compile.pass.cpp        | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index c9412f47a969a..fe0955c2e73ff 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -181,7 +181,7 @@ class adjacent_view<_View, _Np>::__iterator {
   }
 
   template <class _Tp, size_t... _Is>
-  static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple<decltype((_Is, std::declval<_Tp (*)()>()()))...>;
+  static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple<decltype(std::declval<_Tp (*)(size_t)>()(_Is))...>;
 
 public:
   using iterator_category = input_iterator_tag;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
index fc19adbbc32f2..b24c730e1197f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp
@@ -46,6 +46,10 @@ struct ConstVeryDifferentRange {
   forward_iterator<double*> end() const;
 };
 
+struct OverloadComma {
+  friend int operator,(auto&&, OverloadComma);
+};
+
 template <std::size_t N>
 void test() {
   int buffer[] = {1, 2, 3, 4};
@@ -147,4 +151,15 @@ void test() {
   test<2>();
   test<3>();
   test<5>();
+
+  {
+    // overload comma operator
+    OverloadComma buffer[] = {OverloadComma{}, OverloadComma{}, OverloadComma{}};
+    auto v                 = std::views::adjacent<2>(buffer);
+    using Iter             = decltype(v.begin());
+    static_assert(std::is_same_v<typename Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<typename Iter::value_type, std::tuple<OverloadComma, OverloadComma>>);
+  }
 }

>From 8b92f2ff6322691c88cab60ae9380da8c5e560d7 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Dec 2025 08:04:27 +0000
Subject: [PATCH 08/10] lwg4482

---
 libcxx/include/__ranges/adjacent_view.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index fe0955c2e73ff..0d3f221202226 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -95,7 +95,7 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
-    requires range<const _View> // todo: this seems under-constrained. lwg issue?
+    requires range<const _View> // LWG4482 This is under-constrained.
   {
     return __iterator<true>(ranges::begin(__base_), ranges::end(__base_));
   }
@@ -111,7 +111,7 @@ class adjacent_view : public view_interface<adjacent_view<_View, _Np>> {
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
-    requires range<const _View>
+    requires range<const _View> // LWG4482 This is under-constrained.
   {
     if constexpr (common_range<const _View>) {
       return __iterator<true>(__as_sentinel{}, ranges::begin(__base_), ranges::end(__base_));

>From 8fd6839410b74c5b475695fa2392d47aaa1c0027 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Dec 2025 08:07:53 +0000
Subject: [PATCH 09/10] rebase error

---
 flang/test/Semantics/modfile75.f90 | 44 ++++++++++++++++++------------
 1 file changed, 27 insertions(+), 17 deletions(-)

diff --git a/flang/test/Semantics/modfile75.f90 b/flang/test/Semantics/modfile75.f90
index 8f7adafe7204d..e1f4db4051bbe 100644
--- a/flang/test/Semantics/modfile75.f90
+++ b/flang/test/Semantics/modfile75.f90
@@ -1,18 +1,28 @@
-!RUN: rm -rf %t && mkdir -p %t
-!RUN: %flang -c -fhermetic-module-files -DWHICH=1 -J%t %s && %flang -c -fhermetic-module-files -DWHICH=2 -J%t %s && %flang_fc1 -fdebug-unparse -J%t %s | FileCheck %s
-
-#if WHICH == 1
-module modfile75a
-  use iso_c_binding
-end
-#elif WHICH == 2
-module modfile75b
-  use modfile75a
+! RUN: %python %S/test_modfile.py %s %flang_fc1
+module m
+  type dt
+    procedure(sub), pointer, nopass :: p1 => sub
+    procedure(sub), pointer, nopass :: p2 => null()
+    procedure(sub), pointer, nopass :: p3
+  end type
+  procedure(sub), pointer :: p4 => sub
+  procedure(sub), pointer :: p5 => null()
+ contains
+  subroutine sub
+  end
 end
-#else
-program test
-  use modfile75b
-!CHECK: INTEGER(KIND=4_4) n
-  integer(c_int) n
-end
-#endif
+
+!Expect: m.mod
+!module m
+!type::dt
+!procedure(sub),nopass,pointer::p1=>sub
+!procedure(sub),nopass,pointer::p2=>NULL()
+!procedure(sub),nopass,pointer::p3
+!end type
+!intrinsic::null
+!procedure(sub),pointer::p4
+!procedure(sub),pointer::p5
+!contains
+!subroutine sub()
+!end
+!end

>From dabddedf32c01cc6da1e6df7815411fe0600b728 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 13 Dec 2025 08:08:45 +0000
Subject: [PATCH 10/10] Update libcxx/include/__ranges/adjacent_view.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/include/__ranges/adjacent_view.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h
index 0d3f221202226..7790552c2990b 100644
--- a/libcxx/include/__ranges/adjacent_view.h
+++ b/libcxx/include/__ranges/adjacent_view.h
@@ -180,8 +180,11 @@ class adjacent_view<_View, _Np>::__iterator {
       return forward_iterator_tag{};
   }
 
+  template <class _Tp, size_t _Index>
+  using __always = _Tp;
+
   template <class _Tp, size_t... _Is>
-  static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple<decltype(std::declval<_Tp (*)(size_t)>()(_Is))...>;
+  static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple<__always<_Tp, _Is>...>;
 
 public:
   using iterator_category = input_iterator_tag;



More information about the libcxx-commits mailing list