Skip to content

Conversation

@huixie90
Copy link
Member

No description provided.

@huixie90 huixie90 requested a review from a team as a code owner October 25, 2025 10:05
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 25, 2025

@llvm/pr-subscribers-flang-semantics

@llvm/pr-subscribers-libcxx

Author: Hui (huixie90)

Changes

Patch is 147.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/165089.diff

41 Files Affected:

  • (modified) libcxx/docs/ReleaseNotes/22.rst (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+2)
  • (added) libcxx/include/__ranges/adjacent_view.h (+408)
  • (added) libcxx/include/__ranges/zip_utils.h (+49)
  • (modified) libcxx/include/__ranges/zip_view.h (+4-12)
  • (modified) libcxx/include/module.modulemap.in (+4)
  • (modified) libcxx/include/ranges (+15)
  • (modified) libcxx/modules/std/ranges.inc (+3-3)
  • (modified) libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp (+2-1)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp (+242)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp (+104)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp (+136)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp (+39)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp (+73)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp (+102)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp (+143)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp (+57)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h (+43)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp (+165)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp (+153)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp (+94)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp (+82)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp (+102)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp (+105)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp (+92)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp (+154)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp (+136)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp (+150)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp (+106)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp (+67)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp (+135)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp (+60)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp (+116)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp (+195)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp (+185)
  • (added) libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp (+122)
  • (modified) libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp (+13-13)
  • (modified) libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp (+1-77)
  • (modified) libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp (+8-85)
  • (modified) libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h (+99-10)
  • (modified) libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp (+1)
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index ec23ba9d1e3a1..b3186f7e486d7 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)
 - P3044R2: sub-``string_view`` from ``string`` (`Github <https://llvm.org/PR148140>`__)
 - P3223R2: Making ``std::istream::ignore`` less surprising (`Github <https://llvm.org/PR148178>`__)
 - P3060R3: Add ``std::views::indices(n)`` (`Github <https://llvm.org/PR148175>`__)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ddace8bf8c728..9d725f49a18f1 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -696,6 +696,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
@@ -739,6 +740,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..cdb62385a5795
--- /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                              = __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              = __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
\ No newline at end of file
diff --git a/libcxx/include/__ranges/zip_utils.h b/libcxx/include/__ranges/zip_utils.h
new file mode 100644
index 0000000000000..d993e2c05b0d6
--- /dev/null
+++ b/libcxx/include/__ranges/zip_utils.h
@@ -0,0 +1,49 @@
+
+// -*- 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
\ No newline at end of file
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 894093b409e11..e2b60cf8607db 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..fe272579ca898 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>
@@ -411,6 +425,7 @@ namespace std {
 
 #  if _LIBCPP_STD_VER >= 20
 #    include <__ranges/access.h>
+#    include <__ranges/adjacent_view.h>
 #    include <__ranges/all.h>
 #    include <__ranges/common_view.h>
 #    include <__ranges/concepts.h>
diff --git a/libcxx/modules/std/ranges.inc b/libcxx...
[truncated]

@github-actions
Copy link

github-actions bot commented Oct 25, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@frederick-vs-ja frederick-vs-ja mentioned this pull request Oct 27, 2025
13 tasks
@ldionne ldionne changed the title [libc++] implement adjacent_view [libc++] Implement adjacent_view Dec 5, 2025
@huixie90 huixie90 force-pushed the hxie/adjacent_view branch 4 times, most recently from 3975a05 to f4feedb Compare December 7, 2025 07:43
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks! Just missing the adjustment with the LWG issue number.

@llvmbot llvmbot added flang Flang issues not falling into any other category flang:semantics labels Dec 13, 2025
@huixie90 huixie90 force-pushed the hxie/adjacent_view branch 2 times, most recently from f4dcff3 to eeee10c Compare December 13, 2025 08:21
[libc++] implement adjacent_view

ci

ci

ci

ci

Update libcxx/include/__ranges/zip_utils.h

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

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: llvm#171998

Update libcxx/include/__ranges/adjacent_view.h

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

?

undo rebase

ci
@huixie90 huixie90 removed flang Flang issues not falling into any other category flang:semantics labels Dec 13, 2025
@huixie90 huixie90 merged commit 1e15dbe into llvm:main Dec 13, 2025
79 of 80 checks passed
anonymouspc pushed a commit to anonymouspc/llvm that referenced this pull request Dec 15, 2025
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Dec 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants