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

via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 13 00:18:27 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 1/3] 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 2/3] [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 f4dcff35730045e3ddbbf29344bf2e68ca008bd8 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 3/3] review

review

benchmark

format

fix comma

fix comma

lwg4482

rebase error

[LLVM][CMake] Build examples for llvm-test-depends

Build examples and example plug-ins by default when running tests. If
examples are unwanted, they can still be disabled completely using
LLVM_INCLUDE_EXAMPLES=OFF. Plugin tests depend on examples and it is
beneficial to test them by default. By default, Examples will still not
be included in the default target or be installed, this remains
controlled by LLVM_BUILD_EXAMPLES (which defaults to OFF).

The additional cost for building examples for tests is 17 compilation
units (12 C++, 5 C), which should be tolerable.

I don't know how broken the examples currently are in the various build
configurations, but if we find breakage, it would be good to fix it.

Pull Request: https://github.com/llvm/llvm-project/pull/171998

Update libcxx/include/__ranges/adjacent_view.h

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

?
---
 libcxx/include/CMakeLists.txt                 |   2 +-
 libcxx/include/__ranges/adjacent_view.h       |  21 +--
 libcxx/include/__ranges/zip_view.h            |  30 ++--
 .../zip_utils.h => __tuple/tuple_transform.h} |   9 +-
 libcxx/include/module.modulemap.in            |   4 +-
 .../benchmarks/adjacent_view_begin.bench.cpp  |  66 +++++++++
 .../range.adjacent/adaptor.pass.cpp           |   2 +-
 .../range.adjacent/begin.pass.cpp             |  31 ++--
 .../range.adjacent/ctor.views.pass.cpp        |  10 +-
 .../range.adjacent/end.pass.cpp               | 136 +++++++++++-------
 .../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    |  17 ++-
 .../range.adjacent/iterator/singular.pass.cpp |   5 +-
 .../iterator/subscript.pass.cpp               |  38 ++++-
 .../range.adjacent/size.pass.cpp              |  93 ++++++------
 llvm/docs/CMake.rst                           |   7 +-
 llvm/test/CMakeLists.txt                      |   4 +-
 llvm/test/Examples/lit.local.cfg              |   2 +-
 llvm/test/lit.cfg.py                          |   2 +-
 llvm/test/lit.site.cfg.py.in                  |   2 +-
 22 files changed, 310 insertions(+), 181 deletions(-)
 rename libcxx/include/{__ranges/zip_utils.h => __tuple/tuple_transform.h} (88%)
 create mode 100644 libcxx/test/benchmarks/adjacent_view_begin.bench.cpp

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..7790552c2990b 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>
@@ -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_));
@@ -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);
     }
   }
@@ -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((_Is, std::declval<_Tp (*)()>()()))...>;
+  static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple<__always<_Tp, _Is>...>;
 
 public:
   using iterator_category = input_iterator_tag;
@@ -191,11 +194,11 @@ 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 {
-    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 +252,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 +320,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/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
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 3ce45f49920fe..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
@@ -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,72 +61,99 @@ constexpr void test_one() {
   }
 }
 
-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 <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_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_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>();
-}
+    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_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..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>();
-}
\ No newline at end of file
+
+  {
+    // 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>>);
+  }
+}
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 389fbe9dc98d4..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
@@ -14,6 +14,7 @@
 #include <ranges>
 #include <cassert>
 
+#include "../helpers.h"
 #include "../../range_adaptor_types.h"
 
 template <std::size_t N>
@@ -26,8 +27,17 @@ constexpr void test() {
     auto it = v.begin();
     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]), decltype(*it)>);
+    static_assert(std::is_same_v<decltype(it[0]), expectedTupleType<N, int&>>);
   }
 
   {
@@ -36,8 +46,32 @@ constexpr void test() {
     auto it = v.begin();
     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]), 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);
   }
 
   {
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() {
diff --git a/llvm/docs/CMake.rst b/llvm/docs/CMake.rst
index de8be0ad2dded..ec181999aec57 100644
--- a/llvm/docs/CMake.rst
+++ b/llvm/docs/CMake.rst
@@ -366,9 +366,10 @@ its enabled sub-projects. Nearly all of these variable names begin with
   ``LLVM_ENABLE_SPHINX`` and ``LLVM_ENABLE_DOXYGEN``.
 
 **LLVM_BUILD_EXAMPLES**:BOOL
-  Build LLVM examples. Defaults to OFF. Targets for building each example are
-  generated in any case. See documentation for *LLVM_BUILD_TOOLS* above for more
-  details.
+  Include LLVM examples in the 'all' build target and install them as part of
+  the ``install`` target. Defaults to OFF. Targets for building examples are
+  still generated, this is controlled by *LLVM_INCLUDE_EXAMPLES*. Note that some
+  examples might still be built as dependencies for tests.
 
 **LLVM_BUILD_INSTRUMENTED_COVERAGE**:BOOL
   If enabled, `source-based code coverage
diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt
index e547c3429058b..1a17c97d119bf 100644
--- a/llvm/test/CMakeLists.txt
+++ b/llvm/test/CMakeLists.txt
@@ -13,7 +13,7 @@ llvm_canonicalize_cmake_booleans(
   LLVM_LINK_LLVM_DYLIB
   LLVM_TOOL_LTO_BUILD
   LLVM_USE_INTEL_JITEVENTS
-  LLVM_BUILD_EXAMPLES
+  LLVM_INCLUDE_EXAMPLES
   LLVM_ENABLE_PLUGINS
   LLVM_BYE_LINK_INTO_TOOLS
   LLVM_EXAMPLEIRTRANSFORMS_LINK_INTO_TOOLS
@@ -186,7 +186,7 @@ if (TARGET llvm-mt)
   list(APPEND LLVM_TEST_DEPENDS llvm-mt)
 endif ()
 
-if(LLVM_BUILD_EXAMPLES)
+if(LLVM_INCLUDE_EXAMPLES)
   list(APPEND LLVM_TEST_DEPENDS
     Kaleidoscope-Ch3
     Kaleidoscope-Ch4
diff --git a/llvm/test/Examples/lit.local.cfg b/llvm/test/Examples/lit.local.cfg
index f9127daf813cb..3e19c6cbb0cec 100644
--- a/llvm/test/Examples/lit.local.cfg
+++ b/llvm/test/Examples/lit.local.cfg
@@ -1,4 +1,4 @@
-if not config.build_examples or sys.platform in ["win32"]:
+if not config.include_examples or sys.platform in ["win32"]:
     config.unsupported = True
 
 # Test discovery should ignore subdirectories that contain test inputs.
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 1dfa280e6b82a..26fa8078a1e9b 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -511,7 +511,7 @@ def enable_ptxas(ptxas_executable):
 if config.has_plugins:
     config.available_features.add("plugins")
 
-if config.build_examples:
+if config.include_examples:
     config.available_features.add("examples")
 
 if config.linked_bye_extension:
diff --git a/llvm/test/lit.site.cfg.py.in b/llvm/test/lit.site.cfg.py.in
index c5cb7160a3d40..193f351ef52ef 100644
--- a/llvm/test/lit.site.cfg.py.in
+++ b/llvm/test/lit.site.cfg.py.in
@@ -40,7 +40,7 @@ config.have_curl = @LLVM_ENABLE_CURL@
 config.have_httplib = @LLVM_ENABLE_HTTPLIB@
 config.have_dia_sdk = @LLVM_ENABLE_DIA_SDK@
 config.enable_ffi = @LLVM_ENABLE_FFI@
-config.build_examples = @LLVM_BUILD_EXAMPLES@
+config.include_examples = @LLVM_INCLUDE_EXAMPLES@
 config.enable_backtrace = @ENABLE_BACKTRACES@
 config.enable_threads = @LLVM_ENABLE_THREADS@
 config.build_shared_libs = @BUILD_SHARED_LIBS@



More information about the llvm-commits mailing list