diff --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv
--- a/libcxx/docs/Status/ZipProjects.csv
+++ b/libcxx/docs/Status/ZipProjects.csv
@@ -7,10 +7,10 @@
 | `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started|
 | `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Nikolas Klauser, |Not Started|
 | `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Unassigned, |Not Started|
-| `[range.zip] <https://wg21.link/ranges.syn>`_, "zip_view", "| `zip_view::iterator`
-| `zip_view::sentinel`", Unassigned, |Not Started|
-| `[range.zip.iterator] <https://wg21.link/range.zip.transform>`_, "zip_view::iterator", None, Unassigned, |Not Started|
-| `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "zip_view::sentinel", None, Unassigned, |Not Started|
+| `[range.zip] <https://wg21.link/ranges.syn>`_, "`zip_view <https://reviews.llvm.org/D122806>`_", "| `zip_view::iterator`
+| `zip_view::sentinel`", Hui Xie, |Complete|
+| `[range.zip.iterator] <https://wg21.link/range.zip.iterator>`_, "`zip_view::iterator <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
+| `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "`zip_view::sentinel <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
 | `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_, "zip_transform_view", "| `zip_transform_view::iterator`
 | `zip_transform_view::sentinel`", Unassigned, |Not Started|
 | `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Unassigned, |Not Started|
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -371,6 +371,7 @@
   __ranges/transform_view.h
   __ranges/view_interface.h
   __ranges/views.h
+  __ranges/zip_view.h
   __split_buffer
   __std_stream
   __string
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
new file mode 100644
--- /dev/null
+++ b/libcxx/include/__ranges/zip_view.h
@@ -0,0 +1,530 @@
+// -*- 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 <__algorithm/ranges_min.h>
+#include <__compare/three_way_comparable.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/equality_comparable.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 <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/size.h>
+#include <__ranges/view_interface.h>
+#include <__utility/forward.h>
+#include <__utility/integer_sequence.h>
+#include <__utility/move.h>
+#include <tuple>
+#include <type_traits>
+
+#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 > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+namespace ranges {
+
+template <class... _Ranges>
+concept __zip_is_common = (sizeof...(_Ranges) == 1 && (common_range<_Ranges> && ...)) ||
+                          (!(bidirectional_range<_Ranges> && ...) && (common_range<_Ranges> && ...)) ||
+                          ((random_access_range<_Ranges> && ...) && (sized_range<_Ranges> && ...));
+
+template <typename _Tp, typename _Up>
+auto __tuple_or_pair_test() -> pair<_Tp, _Up>;
+
+template <typename... _Types>
+  requires(sizeof...(_Types) != 2)
+auto __tuple_or_pair_test() -> tuple<_Types...>;
+
+template <class... _Types>
+using __tuple_or_pair = decltype(__tuple_or_pair_test<_Types...>());
+
+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_or_pair<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(
+      [&]<class... _Types>(_Types&&... __elements) { (std::invoke(__f, std::forward<_Types>(__elements)), ...); },
+      std::forward<_Tuple>(__tuple));
+}
+
+template <class _Fun, class _Tuple1, class _Tuple2, size_t... _Indices>
+_LIBCPP_HIDE_FROM_ABI constexpr __tuple_or_pair<
+    invoke_result_t<_Fun&, typename tuple_element<_Indices, remove_cvref_t<_Tuple1>>::type,
+                    typename tuple_element<_Indices, remove_cvref_t<_Tuple2>>::type>...>
+__tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2, index_sequence<_Indices...>) {
+  return {std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)),
+                      std::get<_Indices>(std::forward<_Tuple2>(__tuple2)))...};
+}
+
+template <class _Fun, class _Tuple1, class _Tuple2>
+_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) {
+  return ranges::__tuple_zip_transform(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2),
+                                       std::make_index_sequence<tuple_size<remove_cvref_t<_Tuple1>>::value>());
+}
+
+template <class _Fun, class _Tuple1, class _Tuple2, size_t... _Indices>
+_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2,
+                                                          index_sequence<_Indices...>) {
+  (std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)),
+               std::get<_Indices>(std::forward<_Tuple2>(__tuple2))),
+   ...);
+}
+
+template <class _Fun, class _Tuple1, class _Tuple2>
+_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) {
+  return ranges::__tuple_zip_for_each(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2),
+                                      std::make_index_sequence<tuple_size<remove_cvref_t<_Tuple1>>::value>());
+}
+
+// abs in cstdlib is not constexpr
+// TODO : remove __abs once P0533R9 is implemented.
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp __abs(_Tp __t) {
+  return __t < 0 ? -__t : __t;
+}
+
+template <input_range... _Views>
+  requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
+class zip_view : public view_interface<zip_view<_Views...>> {
+
+  _LIBCPP_NO_UNIQUE_ADDRESS tuple<_Views...> __views_;
+
+  template <bool>
+  class __iterator;
+
+  template <bool>
+  class __sentinel;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI
+  zip_view() = default;
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {}
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr auto begin()
+    requires(!(__simple_view<_Views> && ...))
+  {
+    return __iterator<false>(ranges::__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_));
+  }
+
+  _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_));
+    } 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_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr auto end() const
+    requires(range<const _Views> && ...)
+  {
+    if constexpr (!__zip_is_common<const _Views...>) {
+      return __sentinel<true>(ranges::__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_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr auto size()
+    requires(sized_range<_Views> && ...)
+  {
+    return std::apply(
+        [](auto... __sizes) {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return ranges::min({_CT(__sizes)...});
+        },
+        ranges::__tuple_transform(ranges::size, __views_));
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr auto size() const
+    requires(sized_range<const _Views> && ...)
+  {
+    return std::apply(
+        [](auto... __sizes) {
+          using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
+          return ranges::min({_CT(__sizes)...});
+        },
+        ranges::__tuple_transform(ranges::size, __views_));
+  }
+};
+
+template <class... _Ranges>
+zip_view(_Ranges&&...) -> zip_view<views::all_t<_Ranges>...>;
+
+template <bool _Const, class... _Views>
+concept __zip_all_random_access = (random_access_range<__maybe_const<_Const, _Views>> && ...);
+
+template <bool _Const, class... _Views>
+concept __zip_all_bidirectional = (bidirectional_range<__maybe_const<_Const, _Views>> && ...);
+
+template <bool _Const, class... _Views>
+concept __zip_all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...);
+
+template <bool _Const, class... _Views>
+consteval auto __get_zip_view_iterator_tag() {
+  if constexpr (__zip_all_random_access<_Const, _Views...>) {
+    return random_access_iterator_tag();
+  } else if constexpr (__zip_all_bidirectional<_Const, _Views...>) {
+    return bidirectional_iterator_tag();
+  } else if constexpr (__zip_all_forward<_Const, _Views...>) {
+    return forward_iterator_tag();
+  } else {
+    return input_iterator_tag();
+  }
+}
+
+template <bool _Const, class... _Views>
+struct __zip_view_iterator_category_base {};
+
+template <bool _Const, class... _Views>
+  requires __zip_all_forward<_Const, _Views...>
+struct __zip_view_iterator_category_base<_Const, _Views...> {
+  using iterator_category = input_iterator_tag;
+};
+
+template <input_range... _Views>
+  requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
+template <bool _Const>
+class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base<_Const, _Views...> {
+
+  __tuple_or_pair<iterator_t<__maybe_const<_Const, _Views>>...> __current_;
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr explicit __iterator(__tuple_or_pair<iterator_t<__maybe_const<_Const, _Views>>...> __current)
+      : __current_(std::move(__current)) {}
+
+  template <bool>
+  friend class zip_view<_Views...>::__iterator;
+
+  template <bool>
+  friend class zip_view<_Views...>::__sentinel;
+
+  friend class zip_view<_Views...>;
+
+public:
+  using iterator_concept = decltype(__get_zip_view_iterator_tag<_Const, _Views...>());
+  using value_type = __tuple_or_pair<range_value_t<__maybe_const<_Const, _Views>>...>;
+  using difference_type = common_type_t<range_difference_t<__maybe_const<_Const, _Views>>...>;
+
+  _LIBCPP_HIDE_FROM_ABI
+  __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator(__iterator<!_Const> __i)
+    requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<__maybe_const<_Const, _Views>>> && ...)
+  : __current_(std::move(__i.__current_)) {}
+
+  _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++() {
+    ranges::__tuple_for_each([](auto& __i) { ++__i; }, __current_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr void operator++(int) { ++*this; }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator operator++(int)
+    requires __zip_all_forward<_Const, _Views...>
+  {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator& operator--()
+    requires __zip_all_bidirectional<_Const, _Views...>
+  {
+    ranges::__tuple_for_each([](auto& __i) { --__i; }, __current_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator operator--(int)
+    requires __zip_all_bidirectional<_Const, _Views...>
+  {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator& operator+=(difference_type __x)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i += iter_difference_t<_Iter>(__x); }, __current_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator& operator-=(difference_type __x)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i -= iter_difference_t<_Iter>(__x); }, __current_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator[](difference_type __n) const
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    return ranges::__tuple_transform(
+        [&]<class _Iter>(_Iter& __i) -> decltype(auto) { return __i[iter_difference_t<_Iter>(__n)]; }, __current_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+    requires(equality_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...)
+  {
+    if constexpr (__zip_all_bidirectional<_Const, _Views...>) {
+      return __x.__current_ == __y.__current_;
+    } else {
+      const auto __it_equals = ranges::__tuple_zip_transform(std::equal_to<>(), __x.__current_, __y.__current_);
+      return std::apply([](auto... __bs) { return (__bs || ...); }, __it_equals);
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    return __x.__current_ < __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    return __y < __x;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    return !(__y < __x);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    return !(__x < __y);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires __zip_all_random_access<_Const, _Views...> &&
+             (three_way_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...)
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    auto __r = __i;
+    __r += __n;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    return __i + __n;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+    requires __zip_all_random_access<_Const, _Views...>
+  {
+    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<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
+             ...)
+  {
+    const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __x.__current_, __y.__current_);
+    return std::apply(
+        [](auto... __ds) {
+          return ranges::min({difference_type(__ds)...},
+                             [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); });
+        },
+        __diffs);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr auto iter_move(const __iterator& __i) noexcept(
+      (noexcept(ranges::iter_move(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_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept(
+      (noexcept(ranges::iter_swap(declval<const iterator_t<__maybe_const<_Const, _Views>>&>(),
+                                  declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) &&
+       ...))
+    requires(indirectly_swappable<iterator_t<__maybe_const<_Const, _Views>>> && ...)
+  {
+    ranges::__tuple_zip_for_each(ranges::iter_swap, __l.__current_, __r.__current_);
+  }
+};
+
+template <input_range... _Views>
+  requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
+template <bool _Const>
+class zip_view<_Views...>::__sentinel {
+
+  __tuple_or_pair<sentinel_t<__maybe_const<_Const, _Views>>...> __end_;
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr explicit __sentinel(__tuple_or_pair<sentinel_t<__maybe_const<_Const, _Views>>...> __end) : __end_(__end) {}
+
+  friend class zip_view<_Views...>;
+
+  // hidden friend cannot access private member of iterator because
+  // they are friends of friends
+  template <bool _OtherConst>
+  _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto)
+  __iter_current(zip_view<_Views...>::__iterator<_OtherConst> const& __it) {
+    return (__it.__current_);
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI
+  __sentinel() = default;
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __sentinel(__sentinel<!_Const> __i)
+    requires _Const && (convertible_to<sentinel_t<_Views>, sentinel_t<__maybe_const<_Const, _Views>>> && ...)
+  : __end_(std::move(__i.__end_)) {}
+
+  template <bool _OtherConst>
+    requires(sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
+             ...)
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    const auto __it_equals = ranges::__tuple_zip_transform(std::equal_to<>(), __iter_current(__x), __y.__end_);
+    return std::apply([](auto... bs) { return (bs || ...); }, __it_equals);
+  }
+
+  template <bool _OtherConst>
+    requires(
+        sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
+        ...)
+  _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
+  operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_);
+    return std::apply(
+        [](auto... __ds) {
+          using _Diff = common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>;
+          return ranges::min({_Diff(__ds)...},
+                             [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); });
+        },
+        __diffs);
+  }
+
+  template <bool _OtherConst>
+    requires(
+        sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
+        ...)
+  _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
+  operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) {
+    return -(__x - __y);
+  }
+};
+
+template <class... _Views>
+inline constexpr bool enable_borrowed_range<zip_view<_Views...>> = (enable_borrowed_range<_Views> && ...);
+
+namespace views {
+namespace __zip {
+
+struct __fn {
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()() const noexcept { return empty_view<tuple<>>{}; }
+
+  template <class... _Ranges>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Ranges&&... rs) const
+      noexcept(noexcept(zip_view(std::forward<_Ranges>(rs)...))) -> decltype(zip_view(std::forward<_Ranges>(rs)...)) {
+    return zip_view(std::forward<_Ranges>(rs)...);
+  }
+};
+
+} // namespace __zip
+inline namespace __cpo {
+  inline constexpr auto zip = __zip::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_ZIP_VIEW_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -853,6 +853,7 @@
       }
       module view_interface         { private header "__ranges/view_interface.h" }
       module views                  { private header "__ranges/views.h" }
+      module zip_view               { private header "__ranges/zip_view.h" }
     }
   }
   module ratio {
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -196,6 +196,17 @@
   template<input_range V>
     requires view<V> && input_range<range_reference_t<V>>
   class join_view;
+
+  // [range.zip], zip view
+  template<input_range... Views>
+    requires (view<Views> && ...) && (sizeof...(Views) > 0)
+  class zip_view;        // C++2b
+
+  template<class... Views>
+    inline constexpr bool enable_borrowed_range<zip_view<Views...>> =    // C++2b
+      (enable_borrowed_range<Views> && ...);
+
+  namespace views { inline constexpr unspecified zip = unspecified; }    // C++2b
 }
 
 namespace std {
@@ -257,6 +268,7 @@
 #include <__ranges/transform_view.h>
 #include <__ranges/view_interface.h>
 #include <__ranges/views.h>
+#include <__ranges/zip_view.h>
 #include <__tuple> // TODO: <ranges> has to export std::tuple_size. Replace this, once <tuple> is granularized.
 #include <compare>          // Required by the standard.
 #include <initializer_list> // Required by the standard.
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -402,6 +402,7 @@
 #include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}}
 #include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}}
 #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}}
+#include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}}
 #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}}
 #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}}
 #include <__string> // expected-error@*:* {{use of private header from outside its module: '__string'}}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.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
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr auto begin() requires (!(simple-view<Views> && ...));
+// constexpr auto begin() const requires (range<const Views> && ...);
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <tuple>
+#include <utility>
+
+#include "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();
+};
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  {
+    std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.));
+    std::same_as<std::tuple<int&, int, double&>> decltype(auto) val = *v.begin();
+    assert(val == std::make_tuple(1, 0, 2.0));
+    assert(&(std::get<0>(val)) == &buffer[0]);
+  }
+  {
+    std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view<int>());
+    assert(v.begin() == v.end());
+  }
+  {
+    // underlying ranges all model simple-view
+    std::ranges::zip_view v(SimpleCommon{buffer}, SimpleCommon{buffer});
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+    assert(v.begin() == std::as_const(v).begin());
+    auto [x, y] = *std::as_const(v).begin();
+    assert(&x == &buffer[0]);
+    assert(&y == &buffer[0]);
+  }
+  {
+    // not all underlying ranges model simple-view
+    std::ranges::zip_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer});
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+    assert(v.begin() == std::as_const(v).begin());
+    auto [x, y] = *std::as_const(v).begin();
+    assert(&x == &buffer[0]);
+    assert(&y == &buffer[0]);
+  }
+  {
+    using View = std::ranges::zip_view<SimpleCommon, SimpleCommon>;
+    static_assert(HasOnlyConstBegin<View>);
+    static_assert(!HasOnlyNonConstBegin<View>);
+    static_assert(!HasConstAndNonConstBegin<View>);
+  }
+  {
+    using View = std::ranges::zip_view<SimpleCommon, NonSimpleCommon>;
+    static_assert(!HasOnlyConstBegin<View>);
+    static_assert(!HasOnlyNonConstBegin<View>);
+    static_assert(HasConstAndNonConstBegin<View>);
+  }
+  {
+    using View = std::ranges::zip_view<SimpleCommon, NoConstBeginView>;
+    static_assert(!HasOnlyConstBegin<View>);
+    static_assert(HasOnlyNonConstBegin<View>);
+    static_assert(!HasConstAndNonConstBegin<View>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<class... Views>
+// inline constexpr bool enable_borrowed_range<zip_view<Views...>> =
+//      (enable_borrowed_range<Views> && ...);
+
+#include <ranges>
+#include <tuple>
+
+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 borrwed_range
+static_assert(std::ranges::borrowed_range<std::ranges::zip_view<Borrowed>>);
+static_assert(std::ranges::borrowed_range<std::ranges::zip_view<Borrowed, Borrowed>>);
+static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<Borrowed, NonBorrowed>>);
+static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<NonBorrowed>>);
+static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<NonBorrowed, NonBorrowed>>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// std::views::zip
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "types.h"
+
+static_assert(std::is_invocable_v<decltype((std::views::zip))>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip)), int>);
+static_assert(std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView>);
+static_assert(std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView, std::ranges::iota_view<int, int>>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView, int>);
+
+constexpr bool test() {
+  {
+    auto v = std::views::zip();
+    assert(std::ranges::empty(v));
+    static_assert(std::is_same_v<decltype(v), std::ranges::empty_view<std::tuple<>>>);
+  }
+  {
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    std::same_as<std::ranges::zip_view<SizedRandomAccessView>> auto v = std::views::zip(SizedRandomAccessView{buffer});
+    assert(std::ranges::size(v) == 8);
+    static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, std::tuple<int&>>);
+  }
+  {
+    std::array a{1, 2, 3};
+    std::same_as<std::ranges::zip_view<std::ranges::ref_view<std::array<int, 3>>>> auto v = std::views::zip(a);
+    assert(&(std::get<0>(*v.begin())) == &(a[0]));
+    static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, std::tuple<int&>>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template <class... Rs>
+// zip_view(Rs&&...) -> zip_view<views::all_t<Rs>...>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+struct Container {
+  int* begin() const;
+  int* end() const;
+};
+
+struct View : std::ranges::view_base {
+  int* begin() const;
+  int* end() const;
+};
+
+void testCTAD() {
+  static_assert(std::is_same_v<decltype(std::ranges::zip_view(Container{})),
+                               std::ranges::zip_view<std::ranges::owning_view<Container>>>);
+
+  static_assert(std::is_same_v<decltype(std::ranges::zip_view(Container{}, View{})),
+                               std::ranges::zip_view<std::ranges::owning_view<Container>, View>>);
+
+  Container c{};
+  static_assert(std::is_same_v<
+                decltype(std::ranges::zip_view(Container{}, View{}, c)),
+                std::ranges::zip_view<std::ranges::owning_view<Container>, View, std::ranges::ref_view<Container>>>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// zip_view() = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+constexpr int buff[] = {1, 2, 3};
+
+struct DefaultConstructibleView : std::ranges::view_base {
+  constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {}
+  constexpr int const* begin() const { return begin_; }
+  constexpr int const* end() const { return end_; }
+
+private:
+  int const* begin_;
+  int const* end_;
+};
+
+struct NoDefaultView : std::ranges::view_base {
+  NoDefaultView() = delete;
+  int* begin() const;
+  int* end() const;
+};
+
+static_assert(std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView>>);
+static_assert(
+    std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView, NoDefaultView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultView, NoDefaultView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultView>>);
+
+constexpr bool test() {
+  {
+    using View = std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>;
+    View v = View(); // the default constructor is not explicit
+    assert(v.size() == 3);
+    auto it = v.begin();
+    using Pair = std::pair<const int&, const int&>;
+    assert(*it++ == Pair(buff[0], buff[0]));
+    assert(*it++ == Pair(buff[1], buff[1]));
+    assert(*it == Pair(buff[2], buff[2]));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp
@@ -0,0 +1,96 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// constexpr explicit zip_view(Views...)
+
+#include <ranges>
+#include <tuple>
+
+#include "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::zip_view<SimpleCommon>, SimpleCommon>);
+static_assert(!implicitly_constructible_from<std::ranges::zip_view<SimpleCommon>, 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; }
+};
+
+constexpr bool test() {
+
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  int buffer2[4] = {9, 8, 7, 6};
+  {
+    // constructor from views
+    std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.));
+    auto [i, j, k] = *v.begin();
+    assert(i == 1);
+    assert(j == 0);
+    assert(k == 2.0);
+  }
+
+  {
+    // arguments are moved once
+    MoveAwareView mv;
+    std::ranges::zip_view v{std::move(mv), MoveAwareView{}};
+    auto [numMoves1, numMoves2] = *v.begin();
+    assert(numMoves1 == 2); // one move from stack to parameter, one move from parameter to member
+    assert(numMoves2 == 1);
+  }
+
+  // input and forward
+  {
+    std::ranges::zip_view v{InputCommonView{buffer}, ForwardSizedView{buffer2}};
+    auto [i, j] = *v.begin();
+    assert(i == 1);
+    assert(j == 9);
+  }
+
+  // bidi and random_access
+  {
+    std::ranges::zip_view v{BidiCommonView{buffer}, SizedRandomAccessView{buffer2}};
+    auto [i, j] = *v.begin();
+    assert(i == 1);
+    assert(j == 9);
+  }
+
+  // contiguous
+  {
+    std::ranges::zip_view v{buffer, buffer2};
+    auto [i, j] = *v.begin();
+    assert(i == 1);
+    assert(j == 9);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp
@@ -0,0 +1,396 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// constexpr auto end() requires(!(simple-view<Views> && ...))
+// constexpr auto end() const requires(range<const Views>&&...)
+
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+// ID | simple | common | bidi | random | sized | #views |     v.end()    | as_const(v)
+//    |        |        |      | access |       |        |                |   .end()
+// ---|--------|--------|------|--------|-------|--------|----------------|---------------
+// 1  |   Y    |   Y    |  Y   |    Y   |   Y   |   1    | iterator<true> | iterator<true>
+// 2  |   Y    |   Y    |  Y   |    Y   |   Y   |   >1   | iterator<true> | iterator<true>
+// 3  |   Y    |   N    |  Y   |    Y   |   N   |   1    | sentinel<true> | sentinel<true>
+// 4  |   Y    |   N    |  Y   |    Y   |   N   |   >1   | sentinel<true> | sentinel<true>
+// 5  |   Y    |   Y    |  Y   |    N   |   Y   |   1    | iterator<true> | iterator<true>
+// 6  |   Y    |   Y    |  Y   |    N   |   Y   |   >1   | sentinel<true> | sentinel<true>
+// 7  |   Y    |   Y    |  Y   |    N   |   N   |   1    | iterator<true> | iterator<true>
+// 8  |   Y    |   Y    |  Y   |    N   |   N   |   >1   | sentinel<true> | sentinel<true>
+// 9  |   Y    |   Y    |  N   |    N   |   Y   |   1    | iterator<true> | iterator<true>
+// 10 |   Y    |   Y    |  N   |    N   |   Y   |   >1   | iterator<true> | iterator<true>
+// 11 |   Y    |   Y    |  N   |    N   |   N   |   1    | iterator<true> | iterator<true>
+// 12 |   Y    |   Y    |  N   |    N   |   N   |   >1   | iterator<true> | iterator<true>
+// 13 |   Y    |   N    |  Y   |    Y   |   Y   |   1    | iterator<true> | iterator<true>
+// 14 |   Y    |   N    |  Y   |    Y   |   Y   |   >1   | iterator<true> | iterator<true>
+// 15 |   Y    |   N    |  Y   |    N   |   Y   |   1    | sentinel<true> | sentinel<true>
+// 16 |   Y    |   N    |  Y   |    N   |   Y   |   >1   | sentinel<true> | sentinel<true>
+// 17 |   Y    |   N    |  Y   |    N   |   N   |   1    | sentinel<true> | sentinel<true>
+// 18 |   Y    |   N    |  Y   |    N   |   N   |   >1   | sentinel<true> | sentinel<true>
+// 19 |   Y    |   N    |  N   |    N   |   Y   |   1    | sentinel<true> | sentinel<true>
+// 20 |   Y    |   N    |  N   |    N   |   Y   |   >1   | sentinel<true> | sentinel<true>
+// 21 |   Y    |   N    |  N   |    N   |   N   |   1    | sentinel<true> | sentinel<true>
+// 22 |   Y    |   N    |  N   |    N   |   N   |   >1   | sentinel<true> | sentinel<true>
+// 23 |   N    |   Y    |  Y   |    Y   |   Y   |   1    | iterator<false>| iterator<true>
+// 24 |   N    |   Y    |  Y   |    Y   |   Y   |   >1   | iterator<false>| iterator<true>
+// 25 |   N    |   N    |  Y   |    Y   |   N   |   1    | sentinel<false>| sentinel<true>
+// 26 |   N    |   N    |  Y   |    Y   |   N   |   >1   | sentinel<false>| sentinel<true>
+// 27 |   N    |   Y    |  Y   |    N   |   Y   |   1    | iterator<false>| iterator<true>
+// 28 |   N    |   Y    |  Y   |    N   |   Y   |   >1   | sentinel<false>| sentinel<true>
+// 29 |   N    |   Y    |  Y   |    N   |   N   |   1    | iterator<false>| iterator<true>
+// 30 |   N    |   Y    |  Y   |    N   |   N   |   >1   | sentinel<false>| sentinel<true>
+// 31 |   N    |   Y    |  N   |    N   |   Y   |   1    | iterator<false>| iterator<true>
+// 32 |   N    |   Y    |  N   |    N   |   Y   |   >1   | iterator<false>| iterator<true>
+// 33 |   N    |   Y    |  N   |    N   |   N   |   1    | iterator<false>| iterator<true>
+// 34 |   N    |   Y    |  N   |    N   |   N   |   >1   | iterator<false>| iterator<true>
+// 35 |   N    |   N    |  Y   |    Y   |   Y   |   1    | iterator<false>| iterator<true>
+// 36 |   N    |   N    |  Y   |    Y   |   Y   |   >1   | iterator<false>| iterator<true>
+// 37 |   N    |   N    |  Y   |    N   |   Y   |   1    | sentinel<false>| sentinel<true>
+// 38 |   N    |   N    |  Y   |    N   |   Y   |   >1   | sentinel<false>| sentinel<true>
+// 39 |   N    |   N    |  Y   |    N   |   N   |   1    | sentinel<false>| sentinel<true>
+// 40 |   N    |   N    |  Y   |    N   |   N   |   >1   | sentinel<false>| sentinel<true>
+// 41 |   N    |   N    |  N   |    N   |   Y   |   1    | sentinel<false>| sentinel<true>
+// 42 |   N    |   N    |  N   |    N   |   Y   |   >1   | sentinel<false>| sentinel<true>
+// 43 |   N    |   N    |  N   |    N   |   N   |   1    | sentinel<false>| sentinel<true>
+// 44 |   N    |   N    |  N   |    N   |   N   |   >1   | sentinel<false>| sentinel<true>
+
+constexpr bool test() {
+  int buffer1[5] = {1, 2, 3, 4, 5};
+  int buffer2[1] = {1};
+  int buffer3[3] = {1, 2, 3};
+  {
+    // test ID 1
+    std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 5 == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 2
+    std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1), SimpleCommonRandomAccessSized(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 1 == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 3
+    std::ranges::zip_view v{NonSizedRandomAccessView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 5 == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 4
+    std::ranges::zip_view v{NonSizedRandomAccessView(buffer1), NonSizedRandomAccessView(buffer3)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 3 == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 5
+    std::ranges::zip_view v{SizedBidiCommon(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 6
+    std::ranges::zip_view v{SizedBidiCommon(buffer1), SizedBidiCommon(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 7
+    std::ranges::zip_view v{BidiCommonView(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 8
+    std::ranges::zip_view v{BidiCommonView(buffer1), BidiCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 9
+    std::ranges::zip_view v{ForwardSizedView(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 10
+    std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 11
+    std::ranges::zip_view v{InputCommonView(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::ranges::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 12
+    std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 13
+    std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 5 == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 14
+    std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1), SimpleNonCommonRandomAcessSized(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 1 == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 15
+    std::ranges::zip_view v{SizedBidiNonCommonView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 16
+    std::ranges::zip_view v{SizedBidiNonCommonView(buffer1), SizedBidiNonCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 17
+    std::ranges::zip_view v{BidiNonCommonView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 18
+    std::ranges::zip_view v{BidiNonCommonView(buffer1), BidiNonCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 19
+    std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 20
+    std::ranges::zip_view v{ForwardSizedNonCommon(buffer1), ForwardSizedNonCommon(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 21
+    std::ranges::zip_view v{InputNonCommonView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::ranges::next(v.begin(), 5) == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 22
+    std::ranges::zip_view v{InputNonCommonView(buffer1), InputNonCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 23
+    std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 5 == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 24
+    std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1), NonSimpleCommonRandomAccessSized(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 1 == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 25
+    std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 5 == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 26
+    std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1), NonSimpleNonSizedRandomAccessView(buffer3)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 3 == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 27
+    std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 28
+    std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1), NonSimpleSizedBidiCommon(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 29
+    std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 30
+    std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1), NonSimpleBidiCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 31
+    std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 32
+    std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1), NonSimpleForwardSizedView(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 33
+    std::ranges::zip_view v{NonSimpleInputCommonView(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(std::ranges::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 34
+    std::ranges::zip_view v{NonSimpleInputCommonView(buffer1), NonSimpleInputCommonView(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 35
+    std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 5 == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 36
+    std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1), NonSimpleNonCommonRandomAcessSized(buffer2)};
+    static_assert(std::ranges::common_range<decltype(v)>);
+    assert(v.begin() + 1 == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 37
+    std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 38
+    std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1), NonSimpleSizedBidiNonCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 39
+    std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 40
+    std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1), NonSimpleBidiNonCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 41
+    std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 42
+    std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1), NonSimpleForwardSizedNonCommon(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 43
+    std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(std::ranges::next(v.begin(), 5) == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // test ID 44
+    std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1), NonSimpleInputNonCommonView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    assert(++v.begin() == v.end());
+    static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
+  }
+  {
+    // end should go to the minimum length when zip is common and random_access sized
+    std::ranges::zip_view v(std::views::iota(0, 4), std::views::iota(0, 8));
+    auto it = --(v.end());
+    auto [x, y] = *it;
+    assert(x == 3);
+    assert(y == 3); // y should not go to the end "7"
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// Some basic examples of how zip_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 <ranges>
+
+#include <array>
+#include <cassert>
+#include <vector>
+#include <string>
+
+int main(int, char**) {
+  {
+    std::ranges::zip_view v{
+        std::array{1, 2},
+        std::vector{4, 5, 6},
+        std::array{7},
+    };
+    assert(std::ranges::size(v) == 1);
+    assert(*v.begin() == std::make_tuple(1, 4, 7));
+  }
+  {
+    using namespace std::string_literals;
+    std::vector v{1, 2, 3, 4};
+    std::array a{"abc"s, "def"s, "gh"s};
+    auto view = std::views::zip(v, a);
+    auto it = view.begin();
+    assert(&(std::get<0>(*it)) == &(v[0]));
+    assert(&(std::get<1>(*it)) == &(a[0]));
+
+    ++it;
+    assert(&(std::get<0>(*it)) == &(v[1]));
+    assert(&(std::get<1>(*it)) == &(a[1]));
+
+    ++it;
+    assert(&(std::get<0>(*it)) == &(v[2]));
+    assert(&(std::get<1>(*it)) == &(a[2]));
+
+    ++it;
+    assert(it == view.end());
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.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
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// x += n;
+// x + n;
+// n + x;
+// x -= n;
+// x - n;
+// x - y;
+
+#include <ranges>
+
+#include <array>
+#include <concepts>
+#include <functional>
+
+#include "../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; };
+
+constexpr bool test() {
+
+  int buffer1[5] = {1, 2, 3, 4, 5};
+  int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+  SizedRandomAccessView a{buffer1};
+  static_assert(std::ranges::random_access_range<decltype(a)>);
+  std::array b{4.1, 3.2, 4.3, 0.1, 0.2};
+  static_assert(std::ranges::contiguous_range<decltype(b)>);
+  {
+    std::ranges::zip_view v(a, b);
+    auto it1 = v.begin();
+
+    auto it2 = it1 + 3;
+    auto [x2, y2] = *it2;
+    assert(&x2 == &(a[3]));
+    assert(&y2 == &(b[3]));
+
+    auto it3 = 3 + it1;
+    auto [x3, y3] = *it3;
+    assert(&x3 == &(a[3]));
+    assert(&y3 == &(b[3]));
+
+    it1 += 3;
+    assert(it1 == it2);
+    auto [x1, y1] = *it2;
+    assert(&x1 == &(a[3]));
+    assert(&y1 == &(b[3]));
+
+    using Iter = decltype(it1);
+    static_assert(std::invocable<std::plus<>, Iter, intptr_t>);
+    static_assert(std::invocable<std::plus<>, intptr_t, Iter>);
+    static_assert(canPlusEqual<Iter, intptr_t>);
+  }
+  {
+    std::ranges::zip_view v(a, b);
+    auto it1 = v.end();
+
+    auto it2 = it1 - 3;
+    auto [x2, y2] = *it2;
+    assert(&x2 == &(a[2]));
+    assert(&y2 == &(b[2]));
+
+    it1 -= 3;
+    assert(it1 == it2);
+    auto [x1, y1] = *it2;
+    assert(&x1 == &(a[2]));
+    assert(&y1 == &(b[2]));
+
+    using Iter = decltype(it1);
+    static_assert(std::invocable<std::minus<>, Iter, intptr_t>);
+    static_assert(std::invocable<std::minus<>, Iter, Iter>);
+    static_assert(canMinusEqual<Iter, intptr_t>);
+  }
+  {
+    std::ranges::zip_view v(a, b);
+    assert((v.end() - v.begin()) == 5);
+
+    auto it1 = v.begin() + 2;
+    auto it2 = v.end() - 1;
+    assert((it1 - it2) == -2);
+  }
+  {
+    // in this case sentinel is computed by getting each underlying sentinel, so the distance
+    // between begin and end for each underlying iterators can be different
+    std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)};
+    using View = decltype(v);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::random_access_range<View>);
+
+    auto it1 = v.begin();
+    auto it2 = v.end();
+    // it1 : <buffer1 + 0, buffer2 + 0>
+    // it2 : <buffer1 + 5, buffer2 + 9>
+    assert((it1 - it2) == -5);
+    assert((it2 - it1) == 5);
+  }
+  {
+    // One of range is not random access
+    std::ranges::zip_view v(a, b, ForwardSizedView{buffer1});
+    using Iter = decltype(v.begin());
+    static_assert(!std::invocable<std::plus<>, Iter, intptr_t>);
+    static_assert(!std::invocable<std::plus<>, intptr_t, Iter>);
+    static_assert(!canPlusEqual<Iter, intptr_t>);
+    static_assert(!std::invocable<std::minus<>, Iter, intptr_t>);
+    static_assert(std::invocable<std::minus<>, Iter, Iter>);
+    static_assert(!canMinusEqual<Iter, intptr_t>);
+  }
+  {
+    // One of range does not have sized sentinel
+    std::ranges::zip_view v(a, b, InputCommonView{buffer1});
+    using Iter = decltype(v.begin());
+    static_assert(!std::invocable<std::minus<>, Iter, Iter>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.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
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// zip_view::<iterator>::operator{<,>,<=,>=,==,!=,<=>}
+
+#include <ranges>
+#include <compare>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+template <class T>
+concept canSmallerThan = requires(T&& t1, T&& t2) { t1 < t2; };
+
+struct smaller_than_iterator {
+  int* it_ = nullptr;
+  smaller_than_iterator() = default;
+  constexpr smaller_than_iterator(int* it) : it_(it) {}
+
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = int;
+  using difference_type = intptr_t;
+
+  constexpr int& operator*() const { return *it_; }
+  constexpr int& operator[](difference_type n) const { return it_[n]; }
+  constexpr smaller_than_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr smaller_than_iterator& operator--() {
+    --it_;
+    return *this;
+  }
+  constexpr smaller_than_iterator operator++(int) { return smaller_than_iterator(it_++); }
+  constexpr smaller_than_iterator operator--(int) { return smaller_than_iterator(it_--); }
+
+  constexpr smaller_than_iterator& operator+=(difference_type n) {
+    it_ += n;
+    return *this;
+  }
+  constexpr smaller_than_iterator& operator-=(difference_type n) {
+    it_ -= n;
+    return *this;
+  }
+
+  constexpr friend smaller_than_iterator operator+(smaller_than_iterator x, difference_type n) {
+    x += n;
+    return x;
+  }
+  constexpr friend smaller_than_iterator operator+(difference_type n, smaller_than_iterator x) {
+    x += n;
+    return x;
+  }
+  constexpr friend smaller_than_iterator operator-(smaller_than_iterator x, difference_type n) {
+    x -= n;
+    return x;
+  }
+  constexpr friend difference_type operator-(smaller_than_iterator x, smaller_than_iterator y) { return x.it_ - y.it_; }
+
+  constexpr friend bool operator==(smaller_than_iterator const&, smaller_than_iterator const&) = default;
+  friend bool operator!=(smaller_than_iterator const&, smaller_than_iterator const&);
+
+  constexpr friend bool operator<(smaller_than_iterator const& x, smaller_than_iterator const& y) {
+    return x.it_ < y.it_;
+  }
+  friend bool operator<=(smaller_than_iterator const&, smaller_than_iterator const&);
+  friend bool operator>(smaller_than_iterator const&, smaller_than_iterator const&);
+  friend bool operator>=(smaller_than_iterator const&, smaller_than_iterator const&);
+};
+static_assert(std::random_access_iterator<smaller_than_iterator>);
+
+struct SmallerThanRange : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  constexpr smaller_than_iterator begin() const { return {buffer_}; }
+  constexpr smaller_than_iterator end() const { return {buffer_ + size_}; }
+};
+static_assert(std::ranges::random_access_range<SmallerThanRange>);
+
+struct ForwardCommonView : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  using iterator = forward_iterator<int*>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr iterator end() const { return iterator(buffer_ + size_); }
+};
+
+constexpr bool 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::zip_view<SubRange, SubRange>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int a[] = {1, 2, 3, 4};
+    int b[] = {5, 6, 7, 8, 9};
+    auto r = std::views::zip(SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5)));
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    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));
+
+    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 transform 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::zip_view<SubRange, SubRange>;
+    static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int a[] = {1, 2, 3, 4};
+    int b[] = {5, 6, 7, 8, 9};
+    auto r = std::views::zip(SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5)));
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    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));
+  }
+  {
+    int buffer1[1] = {1};
+    int buffer2[2] = {1, 2};
+
+    std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)};
+    using View = decltype(v);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(std::ranges::input_range<View>);
+    static_assert(std::ranges::common_range<View>);
+
+    auto it1 = v.begin();
+    auto it2 = v.end();
+    assert(it1 != it2);
+
+    ++it1;
+    assert(it1 == it2);
+
+    using Iter = decltype(it1);
+    static_assert(!canSmallerThan<Iter>);
+  }
+  {
+    // in this case sentinel is computed by getting each underlying sentinel, so only one
+    // underlying iterator is comparing equal
+    int buffer1[1] = {1};
+    int buffer2[2] = {1, 2};
+    std::ranges::zip_view v{ForwardCommonView(buffer1), ForwardCommonView(buffer2)};
+    using View = decltype(v);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::bidirectional_range<View>);
+
+    auto it1 = v.begin();
+    auto it2 = v.end();
+    assert(it1 != it2);
+
+    ++it1;
+    // it1:  <buffer1 + 1, buffer2 + 1>
+    // it2:  <buffer1 + 1, buffer2 + 2>
+    assert(it1 == it2);
+  }
+  {
+    // only < and == are needed
+    int a[] = {1, 2, 3, 4};
+    int b[] = {5, 6, 7, 8, 9};
+    auto r = std::views::zip(SmallerThanRange(a), SmallerThanRange(b));
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    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));
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/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
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// iterator() = default;
+
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+struct PODIter {
+  int i; // deliberately uninitialised
+
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = int;
+  using difference_type = intptr_t;
+
+  constexpr int operator*() const { return i; }
+
+  constexpr PODIter& operator++() { return *this; }
+  constexpr void operator++(int) {}
+
+  friend constexpr bool operator==(const PODIter&, const PODIter&) = default;
+};
+
+struct Default : std::ranges::view_base {
+  PODIter begin() const;
+  PODIter end() const;
+};
+
+struct NonDefault : std::ranges::view_base {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class... Views>
+using zip_iter = std::ranges::iterator_t<std::ranges::zip_view<Views...>>;
+
+static_assert(!std::default_initializable<zip_iter<NonDefault>>);
+static_assert(!std::default_initializable<zip_iter<NonDefault, Default>>);
+static_assert(!std::default_initializable<zip_iter<NonDefault, NonDefault>>);
+static_assert(std::default_initializable<zip_iter<Default>>);
+static_assert(std::default_initializable<zip_iter<Default, Default>>);
+
+constexpr bool test() {
+  using ZipIter = std::ranges::iterator_t<std::ranges::zip_view<Default>>;
+  {
+    ZipIter iter;
+    auto [x] = *iter;
+    assert(x == 0); // PODIter has to be initialised to have value 0
+  }
+  {
+    ZipIter iter = {};
+    auto [x] = *iter;
+    assert(x == 0); // PODIter has to be initialised to have value 0
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator(iterator<!Const> i);
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int buffer[3] = {1, 2, 3};
+
+  std::ranges::zip_view v(NonSimpleCommon{buffer});
+  auto iter1 = v.begin();
+  std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
+  assert(iter1 == iter2);
+
+  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)>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.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
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator& operator--();
+// constexpr iterator operator--(int);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+template <class Iter>
+concept canDecrement = requires(Iter it) { --it; };
+
+struct NonBidi : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  using iterator = forward_iterator<int*>;
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); }
+};
+
+constexpr bool test() {
+  std::array a{1, 2, 3, 4};
+  std::array b{4.1, 3.2, 4.3};
+  {
+    // all random access
+    std::ranges::zip_view v(a, b, std::views::iota(0, 5));
+    auto it = v.end();
+    using Iter = decltype(it);
+
+    static_assert(std::is_same_v<decltype(--it), Iter&>);
+    auto& it_ref = --it;
+    assert(&it_ref == &it);
+
+    assert(&(std::get<0>(*it)) == &(a[2]));
+    assert(&(std::get<1>(*it)) == &(b[2]));
+    assert(std::get<2>(*it) == 2);
+
+    static_assert(std::is_same_v<decltype(it--), Iter>);
+    it--;
+    assert(&(std::get<0>(*it)) == &(a[1]));
+    assert(&(std::get<1>(*it)) == &(b[1]));
+    assert(std::get<2>(*it) == 1);
+  }
+  {
+    // all bidi+
+    int buffer[2] = {1, 2};
+
+    std::ranges::zip_view v(BidiCommonView{buffer}, std::views::iota(0, 5));
+    auto it = v.begin();
+    using Iter = decltype(it);
+
+    ++it;
+    ++it;
+
+    static_assert(std::is_same_v<decltype(--it), Iter&>);
+    auto& it_ref = --it;
+    assert(&it_ref == &it);
+
+    assert(it == ++v.begin());
+
+    static_assert(std::is_same_v<decltype(it--), Iter>);
+    auto tmp = it--;
+    assert(it == v.begin());
+    assert(tmp == ++v.begin());
+  }
+  {
+    // non bidi
+    int buffer[3] = {4, 5, 6};
+    std::ranges::zip_view v(a, NonBidi{buffer});
+    using Iter = std::ranges::iterator_t<decltype(v)>;
+    static_assert(!canDecrement<Iter>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// constexpr auto operator*() const;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+constexpr bool test() {
+  std::array a{1, 2, 3, 4};
+  std::array b{4.1, 3.2, 4.3};
+  {
+    std::ranges::zip_view v(a);
+    auto it = v.begin();
+    assert(&(std::get<0>(*it)) == &(a[0]));
+    static_assert(std::is_same_v<decltype(*it), std::tuple<int&>>);
+  }
+  {
+    // operator* is const
+    std::ranges::zip_view v(a);
+    const auto it = v.begin();
+    assert(&(std::get<0>(*it)) == &(a[0]));
+  }
+  {
+    std::ranges::zip_view v(a, b);
+    auto it = v.begin();
+    auto [x, y] = *it;
+    assert(&x == &(a[0]));
+    assert(&y == &(b[0]));
+    static_assert(std::is_same_v<decltype(*it), std::pair<int&, double&>>);
+
+    x = 5;
+    y = 0.1;
+    assert(a[0] == 5);
+    assert(b[0] == 0.1);
+  }
+  {
+    std::ranges::zip_view v(a, b, std::views::iota(0, 5));
+    auto it = v.begin();
+    assert(&(std::get<0>(*it)) == &(a[0]));
+    assert(&(std::get<1>(*it)) == &(b[0]));
+    assert(std::get<2>(*it) == 0);
+    static_assert(std::is_same_v<decltype(*it), std::tuple<int&, double&, int>>);
+  }
+  {
+    std::ranges::zip_view v(a, std::as_const(a));
+    auto it = v.begin();
+    assert(&(std::get<0>(*it)) == &(a[0]));
+    assert(&(std::get<1>(*it)) == &(a[0]));
+    static_assert(std::is_same_v<decltype(*it), std::pair<int&, int const&>>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+// constexpr iterator operator++(int) requires all_forward<Const, Views...>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+struct InputRange : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  using iterator = cpp20_input_iterator<int*>;
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); }
+};
+
+constexpr bool test() {
+  std::array a{1, 2, 3, 4};
+  std::array b{4.1, 3.2, 4.3};
+  {
+    // random/contiguous
+    std::ranges::zip_view v(a, b, std::views::iota(0, 5));
+    auto it = v.begin();
+    using Iter = decltype(it);
+
+    assert(&(std::get<0>(*it)) == &(a[0]));
+    assert(&(std::get<1>(*it)) == &(b[0]));
+    assert(std::get<2>(*it) == 0);
+
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+
+    auto& it_ref = ++it;
+    assert(&it_ref == &it);
+
+    assert(&(std::get<0>(*it)) == &(a[1]));
+    assert(&(std::get<1>(*it)) == &(b[1]));
+    assert(std::get<2>(*it) == 1);
+
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    auto original = it;
+    auto copy = it++;
+    assert(original == copy);
+    assert(&(std::get<0>(*it)) == &(a[2]));
+    assert(&(std::get<1>(*it)) == &(b[2]));
+    assert(std::get<2>(*it) == 2);
+  }
+  {
+    //  bidi
+    int buffer[2] = {1, 2};
+
+    std::ranges::zip_view v(BidiCommonView{buffer});
+    auto it = v.begin();
+    using Iter = decltype(it);
+
+    assert(&(std::get<0>(*it)) == &(buffer[0]));
+
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+    auto& it_ref = ++it;
+    assert(&it_ref == &it);
+    assert(&(std::get<0>(*it)) == &(buffer[1]));
+
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    auto original = it;
+    auto copy = it++;
+    assert(copy == original);
+    assert(&(std::get<0>(*it)) == &(buffer[2]));
+  }
+  {
+    //  forword
+    int buffer[2] = {1, 2};
+
+    std::ranges::zip_view v(ForwardSizedView{buffer});
+    auto it = v.begin();
+    using Iter = decltype(it);
+
+    assert(&(std::get<0>(*it)) == &(buffer[0]));
+
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+    auto& it_ref = ++it;
+    assert(&it_ref == &it);
+    assert(&(std::get<0>(*it)) == &(buffer[1]));
+
+    static_assert(std::is_same_v<decltype(it++), Iter>);
+    auto original = it;
+    auto copy = it++;
+    assert(copy == original);
+    assert(&(std::get<0>(*it)) == &(buffer[2]));
+  }
+  {
+    // all input+
+    int buffer[3] = {4, 5, 6};
+    std::ranges::zip_view v(a, InputRange{buffer});
+    auto it = v.begin();
+    using Iter = decltype(it);
+
+    assert(&(std::get<0>(*it)) == &(a[0]));
+    assert(&(std::get<1>(*it)) == &(buffer[0]));
+
+    static_assert(std::is_same_v<decltype(++it), Iter&>);
+    auto& it_ref = ++it;
+    assert(&it_ref == &it);
+    assert(&(std::get<0>(*it)) == &(a[1]));
+    assert(&(std::get<1>(*it)) == &(buffer[1]));
+
+    static_assert(std::is_same_v<decltype(it++), void>);
+    it++;
+    assert(&(std::get<0>(*it)) == &(a[2]));
+    assert(&(std::get<1>(*it)) == &(buffer[2]));
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// friend constexpr decltype(auto) iter_move(const iterator& i);
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+struct ThrowingMove {
+  ThrowingMove() = default;
+  ThrowingMove(ThrowingMove&&){};
+};
+
+constexpr bool test() {
+  {
+    std::array a1{1, 2, 3, 4};
+    const std::array a2{3.0, 4.0};
+
+    std::ranges::zip_view v(a1, a2, std::views::iota(3L));
+    assert(std::ranges::iter_move(v.begin()) == std::make_tuple(1, 3.0, 3L));
+    static_assert(std::is_same_v<decltype(std::ranges::iter_move(v.begin())), std::tuple<int&&, const double&&, long>>);
+
+    static_assert(noexcept(std::ranges::iter_move(std::declval<decltype(v.begin())>())));
+  }
+  {
+    auto throwingMoveRange =
+        std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; });
+    std::ranges::zip_view v(throwingMoveRange);
+    static_assert(!noexcept(std::ranges::iter_move(std::declval<decltype(v.begin())>())));
+  }
+  {
+    IterMoveSwapRange r1{}, r2{};
+    assert(r1.iter_move_called_times == 0);
+    assert(r2.iter_move_called_times == 0);
+    std::ranges::zip_view v(r1, r2);
+    auto it = v.begin();
+    {
+      [[maybe_unused]] auto&& i = std::ranges::iter_move(it);
+      assert(r1.iter_move_called_times == 1);
+      assert(r2.iter_move_called_times == 1);
+    }
+    {
+      [[maybe_unused]] auto&& i = std::ranges::iter_move(it);
+      assert(r1.iter_move_called_times == 2);
+      assert(r2.iter_move_called_times == 2);
+    }
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// friend constexpr void iter_swap(const iterator& x, const iterator& y);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+constexpr bool test() {
+  {
+    std::array a1{1, 2, 3, 4};
+    std::array a2{0.1, 0.2, 0.3};
+    std::ranges::zip_view v(a1, a2);
+    auto iter1 = v.begin();
+    auto iter2 = ++v.begin();
+
+    std::ranges::iter_swap(iter1, iter2);
+
+    assert(a1[0] == 2);
+    assert(a1[1] == 1);
+    assert(a2[0] == 0.2);
+    assert(a2[1] == 0.1);
+
+    auto [x1, y1] = *iter1;
+    assert(&x1 == &a1[0]);
+    assert(&y1 == &a2[0]);
+
+    auto [x2, y2] = *iter2;
+    assert(&x2 == &a1[1]);
+    assert(&y2 == &a2[1]);
+
+    static_assert(noexcept(std::ranges::iter_swap(iter1, iter2)));
+  }
+  {
+    IterMoveSwapRange r1, r2;
+    assert(r1.iter_swap_called_times == 0);
+    assert(r2.iter_swap_called_times == 0);
+
+    std::ranges::zip_view v{r1, r2};
+    auto it1 = v.begin();
+    auto it2 = std::ranges::next(it1, 3);
+
+    std::ranges::iter_swap(it1, it2);
+    assert(r1.iter_swap_called_times == 2);
+    assert(r2.iter_swap_called_times == 2);
+
+    std::ranges::iter_swap(it1, it2);
+    assert(r1.iter_swap_called_times == 4);
+    assert(r2.iter_swap_called_times == 4);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp
@@ -0,0 +1,155 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// Iterator traits and member typedefs in zip_view::<iterator>.
+
+#include <array>
+#include <ranges>
+#include <tuple>
+
+#include "test_iterators.h"
+
+#include "../types.h"
+
+template <class T>
+struct ForwardView : std::ranges::view_base {
+  forward_iterator<T*> begin() const;
+  sentinel_wrapper<forward_iterator<T*>> end() const;
+};
+
+template <class T>
+struct InputView : std::ranges::view_base {
+  cpp17_input_iterator<T*> begin() const;
+  sentinel_wrapper<cpp17_input_iterator<T*>> end() const;
+};
+
+template <class T>
+concept HasIterCategory = requires { typename T::iterator_category; };
+
+template <class T>
+struct diff_type_iter {
+  using iterator_category = std::input_iterator_tag;
+  using value_type = int;
+  using difference_type = T;
+
+  int operator*() const;
+  diff_type_iter& operator++();
+  void operator++(int);
+  friend constexpr bool operator==(diff_type_iter, diff_type_iter) = default;
+};
+
+template <class T>
+struct DiffTypeRange {
+  diff_type_iter<T> begin() const;
+  diff_type_iter<T> end() const;
+};
+
+struct Foo {};
+struct Bar {};
+
+struct ConstVeryDifferentRange {
+  int* begin();
+  int* end();
+
+  forward_iterator<double*> begin() const;
+  forward_iterator<double*> end() const;
+};
+
+void test() {
+  {
+    int buffer[4];
+    std::ranges::zip_view v(buffer, buffer);
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<Iter::value_type, std::pair<int, int>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+  {
+    int buffer[4];
+    std::ranges::zip_view v(buffer, buffer, buffer);
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int, int, int>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+  {
+    using Iter = std::ranges::iterator_t<std::ranges::zip_view<ForwardView<int>>>;
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+  {
+    int buffer[4];
+    std::ranges::zip_view v(buffer, buffer);
+    std::ranges::zip_view v2(buffer, v);
+    using Iter = decltype(v2.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<Iter::value_type, std::pair<int, std::pair<int, int>>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+  {
+    using Iter = std::ranges::iterator_t<std::ranges::zip_view<InputView<int>>>;
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>);
+    static_assert(!HasIterCategory<Iter>);
+    static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+  }
+  {
+    std::ranges::zip_view v{DiffTypeRange<intptr_t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::difference_type, intptr_t>);
+  }
+  {
+    std::ranges::zip_view v{DiffTypeRange<intptr_t>{}, DiffTypeRange<std::ptrdiff_t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::difference_type, std::common_type_t<intptr_t, std::ptrdiff_t>>);
+  }
+  const std::array foos{Foo{}};
+  std::array bars{Bar{}, Bar{}};
+  {
+    std::ranges::zip_view v{foos};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<Foo>>);
+  }
+  {
+    std::ranges::zip_view v{foos, bars};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::value_type, std::pair<Foo, Bar>>);
+  }
+  {
+    std::ranges::zip_view v{ConstVeryDifferentRange{}};
+    using Iter = decltype(v.begin());
+    using ConstIter = decltype(std::as_const(v).begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+
+    static_assert(std::is_same_v<ConstIter::iterator_concept, std::forward_iterator_tag>);
+    static_assert(std::is_same_v<ConstIter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<ConstIter::difference_type, std::ptrdiff_t>);
+    static_assert(std::is_same_v<ConstIter::value_type, std::tuple<double>>);
+  }
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// constexpr auto operator[](difference_type __n) const requires
+//        all_random_access<_Const, _Views...>
+
+#include <ranges>
+#include <cassert>
+
+#include "../types.h"
+
+constexpr bool test() {
+  int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  {
+    std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0));
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+    assert(it[4] == *(it + 4));
+
+    static_assert(std::is_same_v<decltype(it[2]), std::pair<int&, int>>);
+  }
+  {
+    std::ranges::zip_view v(ContiguousCommonView{buffer}, ContiguousCommonView{buffer});
+    auto it = v.begin();
+    assert(it[0] == *it);
+    assert(it[2] == *(it + 2));
+    assert(it[4] == *(it + 4));
+
+    static_assert(std::is_same_v<decltype(it[2]), std::pair<int&, int&>>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
@@ -0,0 +1,322 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// test if zip_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 "types.h"
+
+void testConceptPair() {
+  int buffer1[2] = {1, 2};
+  int buffer2[3] = {1, 2, 3};
+
+  {
+    std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}};
+    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::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2}};
+    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::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2}};
+    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::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2}};
+    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::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2}};
+    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::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2}};
+    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::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}};
+    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::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}};
+    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::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}};
+    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::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}};
+    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::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}};
+    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::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}};
+    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::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}};
+    using View = decltype(v);
+    static_assert(std::ranges::input_range<View>);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+  {
+    std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}};
+    using View = decltype(v);
+    static_assert(std::ranges::input_range<View>);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+  {
+    std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}};
+    using View = decltype(v);
+    static_assert(std::ranges::input_range<View>);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+}
+
+void testConceptTuple() {
+
+  int buffer1[2] = {1, 2};
+  int buffer2[3] = {1, 2, 3};
+  int buffer3[4] = {1, 2, 3, 4};
+
+  // TODO: uncomment all the static_assert once [tuple.tuple] section in P2321R2 is implemented
+  // This is because convertible_to<tuple<int&,int&,int&>&, tuple<int,int,int>> is false without
+  // the above implementation, thus the zip iterator does not model indirectly_readable
+
+  {
+    std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2},
+                            ContiguousCommonView{buffer3}};
+    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::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2},
+                            ContiguousNonCommonView{buffer3}};
+    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::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2},
+                            ContiguousNonCommonSized{buffer3}};
+    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::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2},
+                            ContiguousCommonView{buffer3}};
+    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::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2},
+                            SizedRandomAccessView{buffer3}};
+    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::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2},
+                            NonSizedRandomAccessView{buffer3}};
+    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::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}, SizedRandomAccessView{buffer3}};
+    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::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}, BidiCommonView{buffer3}};
+    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::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
+    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::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
+    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::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
+    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::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
+    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::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
+    using View = decltype(v);
+    //  static_assert(std::ranges::input_range<View>);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+  {
+    std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}};
+    using View = decltype(v);
+    //   static_assert(std::ranges::input_range<View>);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+  {
+    std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}};
+    using View = decltype(v);
+    //   static_assert(std::ranges::input_range<View>);
+    static_assert(!std::ranges::forward_range<View>);
+    static_assert(!std::ranges::common_range<View>);
+    static_assert(!std::ranges::sized_range<View>);
+  }
+}
+
+struct OutputIter {
+  using It = int*;
+  It it;
+  struct proxy {
+    It it;
+    void operator=(int i) const;
+  };
+  proxy operator*();
+
+  OutputIter& operator++();
+
+  OutputIter operator++(int);
+
+  bool operator==(OutputIter other) const;
+  using difference_type = std::ptrdiff_t;
+};
+static_assert(std::output_iterator<OutputIter, int>);
+
+struct OutputView : std::ranges::view_base {
+  OutputIter begin() const;
+  OutputIter end() const;
+};
+static_assert(std::ranges::output_range<OutputView, int>);
+
+template <class... Ts>
+concept zippable = requires { 
+  typename std::ranges::zip_view<Ts...>;  
+};
+
+// output_range is not supported
+static_assert(!zippable<OutputView>);
+static_assert(!zippable<SimpleCommon, OutputView>);
+static_assert(zippable<SimpleCommon>);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// 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();
+};
+
+constexpr bool test() {
+  {
+    using R = std::ranges::zip_view<Range>;
+    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
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// constexpr sentinel(sentinel<!Const> s);
+
+#include <cassert>
+#include <ranges>
+
+#include "../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 NonSimpleNonCommonConveritbleView : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+
+  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<NonSimpleNonCommonConveritbleView>);
+static_assert(std::ranges::random_access_range<NonSimpleNonCommonConveritbleView>);
+static_assert(!std::ranges::sized_range<NonSimpleNonCommonConveritbleView>);
+static_assert(std::convertible_to<std::ranges::sentinel_t<NonSimpleNonCommonConveritbleView>,
+                                  std::ranges::sentinel_t<NonSimpleNonCommonConveritbleView const>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonConveritbleView>);
+
+constexpr bool test() {
+  int buffer1[4] = {1, 2, 3, 4};
+  int buffer2[5] = {1, 2, 3, 4, 5};
+  std::ranges::zip_view v{NonSimpleNonCommonConveritbleView(buffer1), NonSimpleNonCommonConveritbleView(buffer2)};
+  static_assert(!std::ranges::common_range<decltype(v)>);
+  auto sent1 = v.end();
+  std::ranges::sentinel_t<const decltype(v)> sent2 = sent1;
+  static_assert(!std::is_same_v<decltype(sent1), decltype(sent2)>);
+
+  assert(v.begin() != sent2);
+  assert(std::as_const(v).begin() != sent2);
+  assert(v.begin() + 4 == sent2);
+  assert(std::as_const(v).begin() + 4 == sent2);
+
+  // Cannot create a non-const iterator from a const iterator.
+  static_assert(!std::constructible_from<decltype(sent1), decltype(sent2)>);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp
@@ -0,0 +1,152 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// 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 "../types.h"
+
+using Iterator = random_access_iterator<int*>;
+using ConstIterator = random_access_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 : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+
+  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 : std::ranges::view_base {
+  cpp17_input_iterator<int*> begin();
+  forward_iterator<const int*> begin() const;
+  sentinel_wrapper<cpp17_input_iterator<int*>> end();
+  sentinel_wrapper<forward_iterator<const int*>> end() const;
+};
+
+template <class Iter, class Sent>
+concept EqualComparable = std::invocable < std::equal_to<>,
+const Iter&, const Sent& > ;
+
+constexpr bool test() {
+  int buffer1[4] = {1, 2, 3, 4};
+  int buffer2[5] = {1, 2, 3, 4, 5};
+  int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  {
+    std::ranges::zip_view v{SimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<decltype(v)>);
+
+    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() + 4 == v.end());
+
+    using Iter = std::ranges::iterator_t<decltype(v)>;
+    using Sentinel = std::ranges::sentinel_t<decltype(v)>;
+    static_assert(EqualComparable<Iter, Sentinel>);
+  }
+  {
+    std::ranges::zip_view v{NonSimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
+
+    assert(v.begin() != v.end());
+    assert(v.begin() + 4 == v.end());
+
+    // const_iterator (const int*) converted to iterator (int*)
+    assert(v.begin() + 4 == std::as_const(v).end());
+
+    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 ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(EqualComparable<Iter, Sentinel>);
+    static_assert(!EqualComparable<ConstIter, Sentinel>);
+    static_assert(EqualComparable<Iter, ConstSentinel>);
+    static_assert(EqualComparable<ConstIter, ConstSentinel>);
+  }
+  {
+    std::ranges::zip_view v{ComparableView(buffer1), ComparableView(buffer2)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
+
+    assert(v.begin() != v.end());
+    assert(v.begin() + 4 == v.end());
+    assert(std::as_const(v).begin() + 4 == v.end());
+    assert(std::as_const(v).begin() + 4 == std::as_const(v).end());
+    assert(v.begin() + 4 == std::as_const(v).end());
+
+    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 ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(EqualComparable<Iter, Sentinel>);
+    static_assert(EqualComparable<ConstIter, Sentinel>);
+    static_assert(EqualComparable<Iter, ConstSentinel>);
+    static_assert(EqualComparable<ConstIter, ConstSentinel>);
+  }
+  {
+    std::ranges::zip_view v{ComparableView(buffer1), ConstIncompatibleView{}};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<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 ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
+    static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
+
+    static_assert(EqualComparable<Iter, Sentinel>);
+    static_assert(!EqualComparable<ConstIter, Sentinel>);
+    static_assert(!EqualComparable<Iter, ConstSentinel>);
+    static_assert(EqualComparable<ConstIter, ConstSentinel>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
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
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp
@@ -0,0 +1,229 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: libcpp-has-no-incomplete-ranges
+
+// template <bool OtherConst>
+// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>,
+//                             iterator_t<maybe-const<OtherConst, Views>>>&&...)
+// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...>
+// 	operator-(const iterator<OtherConst>&, const sentinel&)
+//
+// template <bool OtherConst>
+// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>,
+//                             iterator_t<maybe-const<OtherConst, Views>>>&&...)
+// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...>
+//   operator-(const sentinel&, const iterator<OtherConst>&)
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <ranges>
+#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 = 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 : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+
+  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_}; }
+};
+
+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 >> ;
+
+constexpr bool test() {
+  int buffer1[5] = {1, 2, 3, 4, 5};
+
+  {
+    std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)};
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<decltype(v)>);
+
+    auto it = v.begin();
+    auto st = v.end();
+    assert(st - it == 5);
+    assert(st - std::ranges::next(it, 1) == 4);
+
+    assert(it - st == -5);
+    assert(std::ranges::next(it, 1) - st == -4);
+    static_assert(SentinelHasMinus<decltype(v)>);
+  }
+
+  {
+    std::ranges::zip_view v(std::views::iota(0, 3), ForwardSizedNonCommon(buffer1));
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    auto it = v.begin();
+    auto st = v.end();
+    assert(st - it == 3);
+    assert(st - std::ranges::next(it, 1) == 2);
+
+    assert(it - st == -3);
+    assert(std::ranges::next(it, 1) - st == -2);
+    static_assert(SentinelHasMinus<decltype(v)>);
+  }
+
+  {
+    std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1));
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    static_assert(!SentinelHasMinus<decltype(v)>);
+  }
+  {
+    // const incompatible
+    std::ranges::zip_view v(NonSimpleForwardSizedNonCommon{buffer1});
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<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 ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
+    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>);
+    auto it = v.begin();
+    auto const_it = std::as_const(v).begin();
+    auto st = v.end();
+    auto const_st = std::as_const(v).end();
+    assert(it - st == -5);
+    assert(st - it == 5);
+    assert(const_it - const_st == -5);
+    assert(const_st - const_it == 5);
+
+    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
+    std::ranges::zip_view v(ConstCompatibleForwardSized{buffer1});
+    static_assert(!std::ranges::common_range<decltype(v)>);
+    LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<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 ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
+    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>);
+
+    auto it = v.begin();
+    auto const_it = std::as_const(v).begin();
+    auto st = v.end();
+    auto const_st = std::as_const(v).end();
+
+    assert(it - st == -5);
+    assert(st - it == 5);
+    assert(const_it - const_st == -5);
+    assert(const_st - const_it == 5);
+    assert(it - const_st == -5);
+    assert(const_st - it == 5);
+    assert(const_it - st == -5);
+    assert(st - const_it == 5);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// constexpr auto size() requires(sized_range<Views>&&...)
+// constexpr auto size() const requires(sized_range<const Views>&&...)
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "test_iterators.h"
+#include "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; }
+};
+
+constexpr bool test() {
+  {
+    std::ranges::zip_view v(View(8));
+    assert(v.size() == 8);
+    assert(std::as_const(v).size() == 8);
+  }
+  {
+    std::ranges::zip_view v(View(2), View(3));
+    assert(v.size() == 2);
+    assert(std::as_const(v).size() == 2);
+  }
+  {
+    std::ranges::zip_view v(std::views::iota(0, 500), View(3));
+    assert(v.size() == 3);
+    assert(std::as_const(v).size() == 3);
+  }
+  {
+    std::ranges::zip_view v(SizedNonConst(2), View(3));
+    assert(v.size() == 2);
+    static_assert(std::ranges::sized_range<decltype(v)>);
+    static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>);
+  }
+  {
+    std::ranges::zip_view v(StrangeSizeView{});
+    assert(v.size() == 5);
+    assert(std::as_const(v).size() == 6);
+  }
+  {
+    std::ranges::zip_view v(InputCommonView{buffer});
+    static_assert(!std::ranges::sized_range<decltype(v)>);
+    static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h
new file mode 100644
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h
@@ -0,0 +1,467 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_ZIP_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H
+
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+#if TEST_STD_VER <= 20
+#  error "range.zip/types.h" can only be included in builds supporting C++20
+#endif // TEST_STD_VER <= 20
+
+template <class T>
+struct Buffer {
+  T* buffer_;
+  std::size_t size_;
+
+  template <std::size_t N>
+  constexpr Buffer(T (&b)[N]) : buffer_(b), size_(N) {}
+};
+
+using IntBuffer = Buffer<int>;
+
+template <bool Simple>
+struct Common : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+
+  constexpr int* begin()
+    requires(!Simple)
+  {
+    return buffer_;
+  }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr int* end()
+    requires(!Simple)
+  {
+    return buffer_ + size_;
+  }
+  constexpr const int* end() const { return buffer_ + size_; }
+};
+using SimpleCommon = Common<true>;
+using NonSimpleCommon = Common<false>;
+
+using SimpleCommonRandomAccessSized = SimpleCommon;
+using NonSimpleCommonRandomAccessSized = NonSimpleCommon;
+
+static_assert(std::ranges::common_range<Common<true>>);
+static_assert(std::ranges::random_access_range<SimpleCommon>);
+static_assert(std::ranges::sized_range<SimpleCommon>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommon>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommon>);
+
+template <bool Simple>
+struct CommonNonRandom : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  using const_iterator = forward_iterator<const int*>;
+  using iterator = forward_iterator<int*>;
+  constexpr iterator begin()
+    requires(!Simple)
+  {
+    return iterator(buffer_);
+  }
+  constexpr const_iterator begin() const { return const_iterator(buffer_); }
+  constexpr iterator end()
+    requires(!Simple)
+  {
+    return iterator(buffer_ + size_);
+  }
+  constexpr const_iterator end() const { return const_iterator(buffer_ + size_); }
+};
+
+using SimpleCommonNonRandom = CommonNonRandom<true>;
+using NonSimpleCommonNonRandom = CommonNonRandom<false>;
+
+static_assert(std::ranges::common_range<SimpleCommonNonRandom>);
+static_assert(!std::ranges::random_access_range<SimpleCommonNonRandom>);
+static_assert(!std::ranges::sized_range<SimpleCommonNonRandom>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommonNonRandom>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommonNonRandom>);
+
+template <bool Simple>
+struct NonCommon : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  constexpr int* begin()
+    requires(!Simple)
+  {
+    return buffer_;
+  }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr sentinel_wrapper<int*> end()
+    requires(!Simple)
+  {
+    return sentinel_wrapper<int*>(buffer_ + size_);
+  }
+  constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); }
+};
+
+using SimpleNonCommon = NonCommon<true>;
+using NonSimpleNonCommon = NonCommon<false>;
+
+static_assert(!std::ranges::common_range<SimpleNonCommon>);
+static_assert(std::ranges::random_access_range<SimpleNonCommon>);
+static_assert(!std::ranges::sized_range<SimpleNonCommon>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommon>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommon>);
+
+template <bool Simple>
+struct NonCommonSized : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  constexpr int* begin()
+    requires(!Simple)
+  {
+    return buffer_;
+  }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr sentinel_wrapper<int*> end()
+    requires(!Simple)
+  {
+    return sentinel_wrapper<int*>(buffer_ + size_);
+  }
+  constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); }
+  constexpr std::size_t size() const { return size_; }
+};
+
+using SimpleNonCommonSized = NonCommonSized<true>;
+using SimpleNonCommonRandomAcessSized = SimpleNonCommonSized;
+using NonSimpleNonCommonSized = NonCommonSized<false>;
+using NonSimpleNonCommonRandomAcessSized = NonSimpleNonCommonSized;
+
+static_assert(!std::ranges::common_range<SimpleNonCommonSized>);
+static_assert(std::ranges::random_access_range<SimpleNonCommonSized>);
+static_assert(std::ranges::sized_range<SimpleNonCommonSized>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommonSized>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonSized>);
+
+template <bool Simple>
+struct NonCommonNonRandom : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+
+  using const_iterator = forward_iterator<const int*>;
+  using iterator = forward_iterator<int*>;
+
+  constexpr iterator begin()
+    requires(!Simple)
+  {
+    return iterator(buffer_);
+  }
+  constexpr const_iterator begin() const { return const_iterator(buffer_); }
+  constexpr sentinel_wrapper<iterator> end()
+    requires(!Simple)
+  {
+    return sentinel_wrapper<iterator>(iterator(buffer_ + size_));
+  }
+  constexpr sentinel_wrapper<const_iterator> end() const {
+    return sentinel_wrapper<const_iterator>(const_iterator(buffer_ + size_));
+  }
+};
+
+using SimpleNonCommonNonRandom = NonCommonNonRandom<true>;
+using NonSimpleNonCommonNonRandom = NonCommonNonRandom<false>;
+
+static_assert(!std::ranges::common_range<SimpleNonCommonNonRandom>);
+static_assert(!std::ranges::random_access_range<SimpleNonCommonNonRandom>);
+static_assert(!std::ranges::sized_range<SimpleNonCommonNonRandom>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommonNonRandom>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonNonRandom>);
+
+template <class Iter, class Sent = Iter, class NonConstIter = Iter, class NonConstSent = Sent>
+struct BasicView : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+
+  constexpr NonConstIter begin()
+    requires(!std::is_same_v<Iter, NonConstIter>)
+  {
+    return NonConstIter(buffer_);
+  }
+  constexpr Iter begin() const { return Iter(buffer_); }
+
+  constexpr NonConstSent end()
+    requires(!std::is_same_v<Sent, NonConstSent>)
+  {
+    if constexpr (std::is_same_v<NonConstIter, NonConstSent>) {
+      return NonConstIter(buffer_ + size_);
+    } else {
+      return NonConstSent(NonConstIter(buffer_ + size_));
+    }
+  }
+
+  constexpr Sent end() const {
+    if constexpr (std::is_same_v<Iter, Sent>) {
+      return Iter(buffer_ + size_);
+    } else {
+      return Sent(Iter(buffer_ + size_));
+    }
+  }
+};
+
+template <class Base = int*>
+struct forward_sized_iterator {
+  Base it_ = nullptr;
+
+  using iterator_category = std::forward_iterator_tag;
+  using value_type = int;
+  using difference_type = intptr_t;
+  using pointer = Base;
+  using reference = decltype(*Base{});
+
+  forward_sized_iterator() = default;
+  constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+  constexpr reference operator*() const { return *it_; }
+
+  constexpr forward_sized_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+  friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+  friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
+
+using ForwardSizedView = BasicView<forward_sized_iterator<>>;
+static_assert(std::ranges::forward_range<ForwardSizedView>);
+static_assert(std::ranges::sized_range<ForwardSizedView>);
+static_assert(std::ranges::common_range<ForwardSizedView>);
+static_assert(!std::ranges::random_access_range<ForwardSizedView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<ForwardSizedView>);
+
+using NonSimpleForwardSizedView = BasicView<forward_sized_iterator<const int*>, forward_sized_iterator<const int*>,
+                                            forward_sized_iterator<int*>, forward_sized_iterator<int*>>;
+static_assert(std::ranges::forward_range<NonSimpleForwardSizedView>);
+static_assert(std::ranges::sized_range<NonSimpleForwardSizedView>);
+static_assert(std::ranges::common_range<NonSimpleForwardSizedView>);
+static_assert(!std::ranges::random_access_range<NonSimpleForwardSizedView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardSizedView>);
+
+using ForwardSizedNonCommon = BasicView<forward_sized_iterator<>, sized_sentinel<forward_sized_iterator<>>>;
+static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<ForwardSizedNonCommon>);
+
+using NonSimpleForwardSizedNonCommon =
+    BasicView<forward_sized_iterator<const int*>, sized_sentinel<forward_sized_iterator<const int*>>,
+              forward_sized_iterator<int*>, sized_sentinel<forward_sized_iterator<int*>>>;
+static_assert(std::ranges::forward_range<NonSimpleForwardSizedNonCommon>);
+static_assert(std::ranges::sized_range<NonSimpleForwardSizedNonCommon>);
+static_assert(!std::ranges::common_range<NonSimpleForwardSizedNonCommon>);
+static_assert(!std::ranges::random_access_range<NonSimpleForwardSizedNonCommon>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardSizedNonCommon>);
+
+struct SizedRandomAccessView : std::ranges::view_base, IntBuffer {
+  using IntBuffer::IntBuffer;
+  using iterator = random_access_iterator<int*>;
+
+  constexpr auto begin() const { return iterator(buffer_); }
+  constexpr auto end() const { return sized_sentinel<iterator>(iterator(buffer_ + size_)); }
+
+  constexpr decltype(auto) operator[](std::size_t n) const { return *(begin() + n); }
+};
+static_assert(std::ranges::view<SizedRandomAccessView>);
+static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
+static_assert(std::ranges::sized_range<SizedRandomAccessView>);
+
+using NonSizedRandomAccessView =
+    BasicView<random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>>>;
+static_assert(!std::ranges::contiguous_range<NonSizedRandomAccessView>);
+static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
+static_assert(!std::ranges::common_range<NonSizedRandomAccessView>);
+static_assert(!std::ranges::sized_range<NonSizedRandomAccessView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<NonSizedRandomAccessView>);
+
+using NonSimpleNonSizedRandomAccessView =
+    BasicView<random_access_iterator<const int*>, sentinel_wrapper<random_access_iterator<const int*>>,
+              random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>> >;
+static_assert(!std::ranges::contiguous_range<NonSimpleNonSizedRandomAccessView>);
+static_assert(std::ranges::random_access_range<NonSimpleNonSizedRandomAccessView>);
+static_assert(!std::ranges::common_range<NonSimpleNonSizedRandomAccessView>);
+static_assert(!std::ranges::sized_range<NonSimpleNonSizedRandomAccessView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonSizedRandomAccessView>);
+
+using ContiguousCommonView = BasicView<int*>;
+static_assert(std::ranges::contiguous_range<ContiguousCommonView>);
+static_assert(std::ranges::common_range<ContiguousCommonView>);
+static_assert(std::ranges::sized_range<ContiguousCommonView>);
+
+using ContiguousNonCommonView = BasicView<int*, sentinel_wrapper<int*>>;
+static_assert(std::ranges::contiguous_range<ContiguousNonCommonView>);
+static_assert(!std::ranges::common_range<ContiguousNonCommonView>);
+static_assert(!std::ranges::sized_range<ContiguousNonCommonView>);
+
+using ContiguousNonCommonSized = BasicView<int*, sized_sentinel<int*>>;
+;
+static_assert(std::ranges::contiguous_range<ContiguousNonCommonSized>);
+static_assert(!std::ranges::common_range<ContiguousNonCommonSized>);
+static_assert(std::ranges::sized_range<ContiguousNonCommonSized>);
+
+template <class Base = int*>
+struct common_input_iterator {
+  Base it_;
+
+  using value_type = int;
+  using difference_type = std::intptr_t;
+  using iterator_concept = std::input_iterator_tag;
+
+  constexpr common_input_iterator() = default;
+  constexpr explicit common_input_iterator(Base it) : it_(it) {}
+
+  constexpr common_input_iterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr void operator++(int) { ++it_; }
+
+  constexpr int& operator*() const { return *it_; }
+
+  friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default;
+};
+
+using InputCommonView = BasicView<common_input_iterator<>>;
+static_assert(std::ranges::input_range<InputCommonView>);
+static_assert(!std::ranges::forward_range<InputCommonView>);
+static_assert(std::ranges::common_range<InputCommonView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InputCommonView>);
+
+using NonSimpleInputCommonView = BasicView<common_input_iterator<const int*>, common_input_iterator<const int*>,
+                                           common_input_iterator<int*>, common_input_iterator<int*>>;
+static_assert(std::ranges::input_range<NonSimpleInputCommonView>);
+static_assert(!std::ranges::forward_range<NonSimpleInputCommonView>);
+static_assert(std::ranges::common_range<NonSimpleInputCommonView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputCommonView>);
+
+using InputNonCommonView = BasicView<common_input_iterator<>, sentinel_wrapper<common_input_iterator<>>>;
+static_assert(std::ranges::input_range<InputNonCommonView>);
+static_assert(!std::ranges::forward_range<InputNonCommonView>);
+static_assert(!std::ranges::common_range<InputNonCommonView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InputNonCommonView>);
+
+using NonSimpleInputNonCommonView =
+    BasicView<common_input_iterator<const int*>, sentinel_wrapper<common_input_iterator<const int*>>,
+              common_input_iterator<int*>, sentinel_wrapper<common_input_iterator<int*>>>;
+static_assert(std::ranges::input_range<InputNonCommonView>);
+static_assert(!std::ranges::forward_range<InputNonCommonView>);
+static_assert(!std::ranges::common_range<InputNonCommonView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputNonCommonView>);
+
+using BidiCommonView = BasicView<bidirectional_iterator<int*>>;
+static_assert(!std::ranges::sized_range<BidiCommonView>);
+static_assert(std::ranges::bidirectional_range<BidiCommonView>);
+static_assert(!std::ranges::random_access_range<BidiCommonView>);
+static_assert(std::ranges::common_range<BidiCommonView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiCommonView>);
+
+using NonSimpleBidiCommonView = BasicView<bidirectional_iterator<const int*>, bidirectional_iterator<const int*>,
+                                          bidirectional_iterator<int*>, bidirectional_iterator<int*>>;
+static_assert(!std::ranges::sized_range<NonSimpleBidiCommonView>);
+static_assert(std::ranges::bidirectional_range<NonSimpleBidiCommonView>);
+static_assert(!std::ranges::random_access_range<NonSimpleBidiCommonView>);
+static_assert(std::ranges::common_range<NonSimpleBidiCommonView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleBidiCommonView>);
+
+struct SizedBidiCommon : BidiCommonView {
+  using BidiCommonView::BidiCommonView;
+  std::size_t size() const { return base(end()) - base(begin()); }
+};
+static_assert(std::ranges::sized_range<SizedBidiCommon>);
+static_assert(std::ranges::bidirectional_range<SizedBidiCommon>);
+static_assert(!std::ranges::random_access_range<SizedBidiCommon>);
+static_assert(std::ranges::common_range<SizedBidiCommon>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SizedBidiCommon>);
+
+struct NonSimpleSizedBidiCommon : NonSimpleBidiCommonView {
+  using NonSimpleBidiCommonView::NonSimpleBidiCommonView;
+  std::size_t size() const { return base(end()) - base(begin()); }
+};
+static_assert(std::ranges::sized_range<NonSimpleSizedBidiCommon>);
+static_assert(std::ranges::bidirectional_range<NonSimpleSizedBidiCommon>);
+static_assert(!std::ranges::random_access_range<NonSimpleSizedBidiCommon>);
+static_assert(std::ranges::common_range<NonSimpleSizedBidiCommon>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleSizedBidiCommon>);
+
+using BidiNonCommonView = BasicView<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>;
+static_assert(!std::ranges::sized_range<BidiNonCommonView>);
+static_assert(std::ranges::bidirectional_range<BidiNonCommonView>);
+static_assert(!std::ranges::random_access_range<BidiNonCommonView>);
+static_assert(!std::ranges::common_range<BidiNonCommonView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiNonCommonView>);
+
+using NonSimpleBidiNonCommonView =
+    BasicView<bidirectional_iterator<const int*>, sentinel_wrapper<bidirectional_iterator<const int*>>,
+              bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>;
+static_assert(!std::ranges::sized_range<NonSimpleBidiNonCommonView>);
+static_assert(std::ranges::bidirectional_range<NonSimpleBidiNonCommonView>);
+static_assert(!std::ranges::random_access_range<NonSimpleBidiNonCommonView>);
+static_assert(!std::ranges::common_range<NonSimpleBidiNonCommonView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleBidiNonCommonView>);
+
+using SizedBidiNonCommonView = BasicView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+static_assert(std::ranges::sized_range<SizedBidiNonCommonView>);
+static_assert(std::ranges::bidirectional_range<SizedBidiNonCommonView>);
+static_assert(!std::ranges::random_access_range<SizedBidiNonCommonView>);
+static_assert(!std::ranges::common_range<SizedBidiNonCommonView>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SizedBidiNonCommonView>);
+
+using NonSimpleSizedBidiNonCommonView =
+    BasicView<bidirectional_iterator<const int*>, sized_sentinel<bidirectional_iterator<const int*>>,
+              bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
+static_assert(std::ranges::sized_range<NonSimpleSizedBidiNonCommonView>);
+static_assert(std::ranges::bidirectional_range<NonSimpleSizedBidiNonCommonView>);
+static_assert(!std::ranges::random_access_range<NonSimpleSizedBidiNonCommonView>);
+static_assert(!std::ranges::common_range<NonSimpleSizedBidiNonCommonView>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleSizedBidiNonCommonView>);
+
+struct iter_move_swap_iterator {
+
+  std::reference_wrapper<int> iter_move_called_times;
+  std::reference_wrapper<int> iter_swap_called_times;
+  int i = 0;
+
+  using iterator_category = std::input_iterator_tag;
+  using value_type = int;
+  using difference_type = intptr_t;
+
+  constexpr int operator*() const { return i; }
+
+  constexpr iter_move_swap_iterator& operator++() {
+    ++i;
+    return *this;
+  }
+  constexpr void operator++(int) { ++i; }
+
+  friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; }
+
+  friend constexpr int iter_move(iter_move_swap_iterator const& it) {
+    ++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;
+  }
+};
+
+struct IterMoveSwapRange {
+  int iter_move_called_times = 0;
+  int iter_swap_called_times = 0;
+  constexpr auto begin() { return iter_move_swap_iterator{iter_move_called_times, iter_swap_called_times}; }
+  constexpr auto end() const { return std::default_sentinel; }
+};
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H