[libcxx-commits] [libcxx] 042dc3c - [libc++] add zip_view and views::zip for C++23

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Mon Apr 25 03:22:34 PDT 2022


Author: Hui Xie
Date: 2022-04-25T12:22:22+02:00
New Revision: 042dc3c46d730b3111189363463b431290e41f29

URL: https://github.com/llvm/llvm-project/commit/042dc3c46d730b3111189363463b431290e41f29
DIFF: https://github.com/llvm/llvm-project/commit/042dc3c46d730b3111189363463b431290e41f29.diff

LOG: [libc++] add zip_view and views::zip for C++23

- add zip_view and views::zip for C++23
- added unit tests
- implemented section 5.6 (zip) in P2321R2

I used clang-format to format the files but they look nothing like the rest of the code base. Manually indenting each line to match the styles sounds like an impossible task. Is there any clang-format file which can format it reasonable similar to the rest of the code base so that I can manually format the rest lines that look weird?

Reviewed By: ldionne, #libc, philnik, var-const

Spies: Mordante, philnik, libcxx-commits, mgorny

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

Added: 
    libcxx/include/__ranges/zip_view.h
    libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.zip/types.h

Modified: 
    libcxx/docs/Status/ZipProjects.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/module.modulemap
    libcxx/include/ranges
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv
index 30fd859293e50..0b345f3d98d59 100644
--- a/libcxx/docs/Status/ZipProjects.csv
+++ b/libcxx/docs/Status/ZipProjects.csv
@@ -7,10 +7,10 @@ Section,Description,Dependencies,Assignee,Complete
 | `[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
index e97e5fbfb67da..423a1ff8808b9 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -396,6 +396,7 @@ set(files
   __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
index 0000000000000..560452aa7c69e
--- /dev/null
+++ b/libcxx/include/__ranges/zip_view.h
@@ -0,0 +1,511 @@
+// -*- 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>());
+}
+
+template <class _Tuple1, class _Tuple2>
+_LIBCPP_HIDE_FROM_ABI constexpr bool __tuple_any_equals(const _Tuple1& __tuple1, const _Tuple2& __tuple2) {
+  const auto __equals = ranges::__tuple_zip_transform(std::equal_to<>(), __tuple1, __tuple2);
+  return std::apply([](auto... __bools) { return (__bools || ...); }, __equals);
+}
+
+// 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_
diff erence_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_
diff erence_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 
diff erence_type = common_type_t<range_
diff erence_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+=(
diff erence_type __x)
+    requires __zip_all_random_access<_Const, _Views...> {
+    ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i += iter_
diff erence_t<_Iter>(__x); }, __current_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr __iterator& operator-=(
diff erence_type __x)
+    requires __zip_all_random_access<_Const, _Views...> {
+    ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i -= iter_
diff erence_t<_Iter>(__x); }, __current_);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator[](
diff erence_type __n) const
+    requires __zip_all_random_access<_Const, _Views...> {
+    return ranges::__tuple_transform(
+        [&]<class _Iter>(_Iter& __i) -> decltype(auto) { return __i[iter_
diff erence_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 {
+      return ranges::__tuple_any_equals(__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 __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, 
diff erence_type __n)
+    requires __zip_all_random_access<_Const, _Views...> {
+    auto __r = __i;
+    __r += __n;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr __iterator operator+(
diff erence_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, 
diff erence_type __n)
+    requires __zip_all_random_access<_Const, _Views...> {
+    auto __r = __i;
+    __r -= __n;
+    return __r;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr 
diff erence_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 __
diff s = ranges::__tuple_zip_transform(minus<>(), __x.__current_, __y.__current_);
+    return std::apply(
+        [](auto... __ds) {
+          return ranges::min({
diff erence_type(__ds)...},
+                             [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); });
+        },
+        __
diff s);
+  }
+
+  _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) {
+    return ranges::__tuple_any_equals(__iter_current(__x), __y.__end_);
+  }
+
+  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_
diff erence_t<__maybe_const<_OtherConst, _Views>>...>
+  operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+    const auto __
diff s = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_);
+    return std::apply(
+        [](auto... __ds) {
+          using _Diff = common_type_t<range_
diff erence_t<__maybe_const<_OtherConst, _Views>>...>;
+          return ranges::min({_Diff(__ds)...},
+                             [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); });
+        },
+        __
diff s);
+  }
+
+  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_
diff erence_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<all_t<_Ranges&&>...>(std::forward<_Ranges>(rs)...)))
+          -> decltype(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(rs)...)) {
+    return zip_view<all_t<_Ranges>...>(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
index cd4fc9148b1f1..f6a7179259f93 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -878,6 +878,7 @@ module std [system] {
       }
       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
index aa945f1154daf..82378a6b0daa2 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -227,6 +227,17 @@ namespace std::ranges {
   namespace views {
     inline constexpr unspecified lazy_split = unspecified;
   }
+
+  // [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 {
@@ -290,6 +301,7 @@ namespace std {
 #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
index 8b4305cd0e9b9..f98e7ec7bcc7b 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -427,6 +427,7 @@ END-SCRIPT
 #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.transform/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp
index 674d9f6db378d..23d4b284d0950 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp
@@ -34,8 +34,8 @@ struct DefaultConstructibleFunction {
   constexpr int operator()(int i) const { return i + state_; }
 };
 
-struct NoDefaultView : std::ranges::view_base {
-  NoDefaultView() = delete;
+struct NoDefaultCtrView : std::ranges::view_base {
+  NoDefaultCtrView() = delete;
   int* begin() const;
   int* end() const;
 };
@@ -62,9 +62,9 @@ constexpr bool test() {
     assert(view[2] == 103);
   }
 
-  static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultView,            DefaultConstructibleFunction>>);
+  static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultCtrView,            DefaultConstructibleFunction>>);
   static_assert(!std::is_default_constructible_v<std::ranges::transform_view<DefaultConstructibleView, NoDefaultFunction>>);
-  static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultView,            NoDefaultFunction>>);
+  static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultCtrView,            NoDefaultFunction>>);
 
   return true;
 }

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
index 0000000000000..eb0d80df1dc7a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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};
+  {
+    // all underlying iterators should be at the begin position
+    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]);
+  }
+
+  {
+    // with empty range
+    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]);
+
+    using View = decltype(v);
+    static_assert(HasOnlyConstBegin<View>);
+    static_assert(!HasOnlyNonConstBegin<View>);
+    static_assert(!HasConstAndNonConstBegin<View>);
+  }
+
+  {
+    // 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 = decltype(v);
+    static_assert(!HasOnlyConstBegin<View>);
+    static_assert(!HasOnlyNonConstBegin<View>);
+    static_assert(HasConstAndNonConstBegin<View>);
+  }
+
+  {
+    // underlying const R is not a range
+    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
index 0000000000000..b5ec1b18ae760
--- /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 borrowed_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
index 0000000000000..98fdfd4d3e5cd
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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() {
+  {
+    // zip zero arguments
+    auto v = std::views::zip();
+    assert(std::ranges::empty(v));
+    static_assert(std::is_same_v<decltype(v), std::ranges::empty_view<std::tuple<>>>);
+  }
+
+  {
+    // zip a view
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    std::same_as<std::ranges::zip_view<SizedRandomAccessView>> decltype(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&>>);
+  }
+
+  {
+    // zip a viewable range
+    std::array a{1, 2, 3};
+    std::same_as<std::ranges::zip_view<std::ranges::ref_view<std::array<int, 3>>>> decltype(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&>>);
+  }
+
+  {
+    // zip the zip_view
+    int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+    std::same_as<std::ranges::zip_view<SizedRandomAccessView, SizedRandomAccessView>> decltype(auto) v =
+        std::views::zip(SizedRandomAccessView{buffer}, SizedRandomAccessView{buffer});
+
+    std::same_as<
+        std::ranges::zip_view<std::ranges::zip_view<SizedRandomAccessView, SizedRandomAccessView>>> decltype(auto) v2 =
+        std::views::zip(v);
+
+    static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v2)>, std::tuple<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/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..42278cdb5672d
--- /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
index 0000000000000..41870f905c4be
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 NoDefaultCtrView : std::ranges::view_base {
+  NoDefaultCtrView() = delete;
+  int* begin() const;
+  int* end() const;
+};
+
+// The default constructor requires all underlying views to be default constructible.
+// It is implicitly required by the tuple's constructor. If any of the iterators are 
+// not default constructible, zip iterator's =default would be implicitly deleted.
+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, NoDefaultCtrView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultCtrView, NoDefaultCtrView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultCtrView>>);
+
+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
index 0000000000000..8be10cec12713
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>);
+
+static_assert(std::constructible_from<std::ranges::zip_view<SimpleCommon, SimpleCommon>, SimpleCommon, SimpleCommon>);
+static_assert(
+    !implicitly_constructible_from<std::ranges::zip_view<SimpleCommon, SimpleCommon>, 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; }
+};
+
+template <class View1, class View2>
+constexpr void constructorTest(auto&& buffer1, auto&& buffer2) {
+  std::ranges::zip_view v{View1{buffer1}, View2{buffer2}};
+  auto [i, j] = *v.begin();
+  assert(i == buffer1[0]);
+  assert(j == buffer2[0]);
+};
+
+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 the local variable to parameter, one move from parameter to member
+    assert(numMoves2 == 1);
+  }
+
+  // input and forward
+  {
+    constructorTest<InputCommonView, ForwardSizedView>(buffer, buffer2);
+  }
+
+  // bidi and random_access
+  {
+    constructorTest<BidiCommonView, SizedRandomAccessView>(buffer, buffer2);
+  }
+
+  // contiguous
+  {
+    constructorTest<ContiguousCommonView, ContiguousCommonView>(buffer, buffer2);
+  }
+
+  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
index 0000000000000..317a5e1bad209
--- /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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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.end()), decltype(std::as_const(v).end())>);
+  }
+  {
+    // 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
index 0000000000000..5552b87b0183b
--- /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
index 0000000000000..30939fb7e7e2b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// x += n;
+// x + n;
+// n + x;
+// x -= n;
+// x - n;
+// x - y;
+// All the arithmetic operators have the constraint `requires all-random-access<Const, Views...>;`,
+// except `operator-(x, y)` which instead has the constraint 
+//    `requires (sized_­sentinel_­for<iterator_t<maybe-const<Const, Views>>,
+//                                  iterator_t<maybe-const<Const, Views>>> && ...);`
+
+#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)>);
+  {
+    // operator+(x, n) and operator+=
+    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(canPlusEqual<Iter, intptr_t>);
+  }
+
+  {
+    // operator-(x, n) and operator-=
+    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(canMinusEqual<Iter, intptr_t>);
+  }
+
+  {
+    // operator-(x, y)
+    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 of the underlying sentinels, so the distance
+    // between begin and end for each of the underlying iterators can be 
diff erent
+    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 the ranges 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 the ranges 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
index 0000000000000..c31d3063caa83
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp
@@ -0,0 +1,250 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 bool operator==(const iterator& x, const iterator& y)
+//   requires (equality_­comparable<iterator_t<maybe-const<Const, Views>>> && ...);
+// friend constexpr bool operator<(const iterator& x, const iterator& y)
+//   requires all-random-access<Const, Views...>;
+// friend constexpr bool operator>(const iterator& x, const iterator& y)
+//   requires all-random-access<Const, Views...>;
+// friend constexpr bool operator<=(const iterator& x, const iterator& y)
+//   requires all-random-access<Const, Views...>;
+// friend constexpr bool operator>=(const iterator& x, const iterator& y)
+//   requires all-random-access<Const, Views...>;
+// friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+//   requires all-random-access<Const, Views...> &&
+//            (three_­way_­comparable<iterator_t<maybe-const<Const, Views>>> && ...);
+
+#include <ranges>
+#include <compare>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+// This is for testing that zip iterator never calls underlying iterator's >, >=, <=, !=.
+// The spec indicates that zip iterator's >= is negating zip iterator's < instead of calling underlying iterator's >=.
+// Declare all the operations >, >=, <= etc to make it satisfy random_access_iterator concept,
+// but not define them. If the zip iterator's >,>=, <=, etc isn't implemented in the way defined by the standard
+// but instead calling underlying iterator's >,>=,<=, we will get a linker error for the runtime tests and
+// non-constant expression for the compile time tests.
+struct LessThanIterator {
+  int* it_ = nullptr;
+  LessThanIterator() = default;
+  constexpr LessThanIterator(int* it) : it_(it) {}
+
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = int;
+  using 
diff erence_type = intptr_t;
+
+  constexpr int& operator*() const { return *it_; }
+  constexpr int& operator[](
diff erence_type n) const { return it_[n]; }
+  constexpr LessThanIterator& operator++() {
+    ++it_;
+    return *this;
+  }
+  constexpr LessThanIterator& operator--() {
+    --it_;
+    return *this;
+  }
+  constexpr LessThanIterator operator++(int) { return LessThanIterator(it_++); }
+  constexpr LessThanIterator operator--(int) { return LessThanIterator(it_--); }
+
+  constexpr LessThanIterator& operator+=(
diff erence_type n) {
+    it_ += n;
+    return *this;
+  }
+  constexpr LessThanIterator& operator-=(
diff erence_type n) {
+    it_ -= n;
+    return *this;
+  }
+
+  constexpr friend LessThanIterator operator+(LessThanIterator x, 
diff erence_type n) {
+    x += n;
+    return x;
+  }
+  constexpr friend LessThanIterator operator+(
diff erence_type n, LessThanIterator x) {
+    x += n;
+    return x;
+  }
+  constexpr friend LessThanIterator operator-(LessThanIterator x, 
diff erence_type n) {
+    x -= n;
+    return x;
+  }
+  constexpr friend 
diff erence_type operator-(LessThanIterator x, LessThanIterator y) { return x.it_ - y.it_; }
+
+  constexpr friend bool operator==(LessThanIterator const&, LessThanIterator const&) = default;
+  friend bool operator!=(LessThanIterator const&, LessThanIterator const&);
+
+  constexpr friend bool operator<(LessThanIterator const& x, LessThanIterator const& y) { return x.it_ < y.it_; }
+  friend bool operator<=(LessThanIterator const&, LessThanIterator const&);
+  friend bool operator>(LessThanIterator const&, LessThanIterator const&);
+  friend bool operator>=(LessThanIterator const&, LessThanIterator const&);
+};
+static_assert(std::random_access_iterator<LessThanIterator>);
+
+struct SmallerThanRange : IntBufferView {
+  using IntBufferView::IntBufferView;
+  constexpr LessThanIterator begin() const { return {buffer_}; }
+  constexpr LessThanIterator end() const { return {buffer_ + size_}; }
+};
+static_assert(std::ranges::random_access_range<SmallerThanRange>);
+
+struct ForwardCommonView : IntBufferView {
+  using IntBufferView::IntBufferView;
+  using iterator = forward_iterator<int*>;
+
+  constexpr iterator begin() const { return iterator(buffer_); }
+  constexpr iterator end() const { return iterator(buffer_ + size_); }
+};
+
+constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) {
+  assert(!(iter1 < iter1));
+  assert(iter1 < iter2);
+  assert(!(iter2 < iter1));
+  assert(iter1 <= iter1);
+  assert(iter1 <= iter2);
+  assert(!(iter2 <= iter1));
+  assert(!(iter1 > iter1));
+  assert(!(iter1 > iter2));
+  assert(iter2 > iter1);
+  assert(iter1 >= iter1);
+  assert(!(iter1 >= iter2));
+  assert(iter2 >= iter1);
+  assert(iter1 == iter1);
+  assert(!(iter1 == iter2));
+  assert(iter2 == iter2);
+  assert(!(iter1 != iter1));
+  assert(iter1 != iter2);
+  assert(!(iter2 != iter2));
+}
+
+constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) {
+  using Iter1 = decltype(iter1);
+  using Iter2 = decltype(iter2);
+  static_assert(!std::is_invocable_v<std::less<>, Iter1, Iter2>);
+  static_assert(!std::is_invocable_v<std::less_equal<>, Iter1, Iter2>);
+  static_assert(!std::is_invocable_v<std::greater<>, Iter1, Iter2>);
+  static_assert(!std::is_invocable_v<std::greater_equal<>, Iter1, Iter2>);
+}
+
+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;
+
+    compareOperatorTest(iter1, iter2);
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
+  }
+
+  {
+    // Test an old-school iterator with no operator<=>; the 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;
+
+    compareOperatorTest(iter1, iter2);
+  }
+
+  {
+    // non random_access_range
+    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);
+
+    inequalityOperatorsDoNotExistTest(it1, it2);
+  }
+
+  {
+    // in this case sentinel is computed by getting each of the 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);
+
+    inequalityOperatorsDoNotExistTest(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;
+
+    compareOperatorTest(iter1, iter2);
+  }
+
+  {
+    // underlying iterator does not support ==
+    using IterNoEqualView = BasicView<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>;
+    int buffer[] = {1};
+    std::ranges::zip_view r(IterNoEqualView{buffer});
+    auto it = r.begin();
+    using Iter = decltype(it);
+    static_assert(!std::invocable<std::equal_to<>, Iter, Iter>);
+    inequalityOperatorsDoNotExistTest(it, it);
+  }
+  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
index 0000000000000..0fe4c9c3bfb30
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 
diff erence_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 IterDefaultCtrView : std::ranges::view_base {
+  PODIter begin() const;
+  PODIter end() const;
+};
+
+struct IterNoDefaultCtrView : std::ranges::view_base {
+  cpp20_input_iterator<int*> begin() const;
+  sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
+};
+
+template <class... Views>
+using zip_iter = std::ranges::iterator_t<std::ranges::zip_view<Views...>>;
+
+static_assert(!std::default_initializable<zip_iter<IterNoDefaultCtrView>>);
+static_assert(!std::default_initializable<zip_iter<IterNoDefaultCtrView, IterDefaultCtrView>>);
+static_assert(!std::default_initializable<zip_iter<IterNoDefaultCtrView, IterNoDefaultCtrView>>);
+static_assert(std::default_initializable<zip_iter<IterDefaultCtrView>>);
+static_assert(std::default_initializable<zip_iter<IterDefaultCtrView, IterDefaultCtrView>>);
+
+constexpr bool test() {
+  using ZipIter = zip_iter<IterDefaultCtrView>;
+  {
+    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
index 0000000000000..d53578688bd44
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// 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)
+//       requires Const && (convertible_­to<iterator_t<Views>,
+//                                         iterator_t<maybe-const<Const, Views>>> && ...);
+
+#include <ranges>
+
+#include <cassert>
+#include <tuple>
+
+#include "../types.h"
+
+using ConstIterIncompatibleView = BasicView<forward_iterator<int*>, forward_iterator<int*>,
+                                            random_access_iterator<const int*>, random_access_iterator<const int*>>;
+static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
+                                   std::ranges::iterator_t<const ConstIterIncompatibleView>>);
+
+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)>);
+  }
+
+  {
+    // underlying non-const to const not convertible
+    std::ranges::zip_view v(ConstIterIncompatibleView{buffer});
+    auto iter1 = v.begin();
+    auto iter2 = std::as_const(v).begin();
+
+    static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
+
+    static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+    static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
+  }
+
+  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
index 0000000000000..b86e63b5d0a6b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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--() requires all-bidirectional<Const, Views...>;
+// constexpr iterator operator--(int) requires all-bidirectional<Const, Views...>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+template <class Iter>
+concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; };
+
+struct NonBidi : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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
index 0000000000000..cbaa43ca63747
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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};
+  {
+    // single range
+    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]));
+  }
+
+  {
+    // two ranges with 
diff erent types
+    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);
+  }
+
+  {
+    // underlying range with prvalue range_reference_t
+    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>>);
+  }
+
+  {
+    // const-correctness
+    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
index 0000000000000..d96050821d7b5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.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
+
+// 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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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]));
+  }
+
+  {
+    //  forward
+    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
index 0000000000000..6dacd1b070431
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 auto iter_move(const iterator& i) noexcept(see below);
+
+#include <array>
+#include <cassert>
+#include <iterator>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+struct ThrowingMove {
+  ThrowingMove() = default;
+  ThrowingMove(ThrowingMove&&){};
+};
+
+constexpr bool test() {
+  {
+    // underlying iter_move noexcept
+    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>>);
+
+    auto it = v.begin();
+    static_assert(noexcept(std::ranges::iter_move(it)));
+  }
+
+  {
+    // underlying iter_move may throw
+    auto throwingMoveRange =
+        std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; });
+    std::ranges::zip_view v(throwingMoveRange);
+    auto it = v.begin();
+    static_assert(!noexcept(std::ranges::iter_move(it)));
+  }
+
+  {
+    // underlying iterators' iter_move are called through ranges::iter_move
+    adltest::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
index 0000000000000..4e7ec3793d07c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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& l, const iterator& r) noexcept(see below)
+//   requires (indirectly_­swappable<iterator_t<maybe-const<Const, Views>>> && ...);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+struct ThrowingMove {
+  ThrowingMove() = default;
+  ThrowingMove(ThrowingMove&&){};
+  ThrowingMove& operator=(ThrowingMove&&){return *this;}
+};
+
+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)));
+  }
+
+  {
+    // underlying iter_swap may throw
+    std::array<ThrowingMove, 2> iterSwapMayThrow{};
+    std::ranges::zip_view v(iterSwapMayThrow);
+    auto iter1 = v.begin();
+    auto iter2 = ++v.begin();
+    static_assert(!noexcept(std::ranges::iter_swap(iter1, iter2)));
+  }
+
+  {
+    // underlying iterators' iter_move are called through ranges::iter_swap
+    adltest::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
index 0000000000000..08ae87585d26b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// 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 DiffTypeIter {
+  using iterator_category = std::input_iterator_tag;
+  using value_type = int;
+  using 
diff erence_type = T;
+
+  int operator*() const;
+  DiffTypeIter& operator++();
+  void operator++(int);
+  friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default;
+};
+
+template <class T>
+struct DiffTypeRange {
+  DiffTypeIter<T> begin() const;
+  DiffTypeIter<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[] = {1, 2, 3, 4};
+  {
+    // 2 views should have pair value_type
+    // random_access_iterator_tag
+    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::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, std::pair<int, int>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // !=2 views should have tuple value_type
+    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::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int, int, int>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // bidirectional_iterator_tag
+    std::ranges::zip_view v(BidiCommonView{buffer});
+    using Iter = decltype(v.begin());
+
+    static_assert(std::is_same_v<Iter::iterator_concept, std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+  }
+
+  {
+    // forward_iterator_tag
+    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::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // nested zip_view
+    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::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, std::pair<int, std::pair<int, int>>>);
+    static_assert(HasIterCategory<Iter>);
+  }
+
+  {
+    // input_iterator_tag
+    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::
diff erence_type, std::ptr
diff _t>);
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+  }
+
+  {
+    // 
diff erence_type of single view
+    std::ranges::zip_view v{DiffTypeRange<intptr_t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::
diff erence_type, intptr_t>);
+  }
+
+  {
+    // 
diff erence_type of multiple views should be the common type
+    std::ranges::zip_view v{DiffTypeRange<intptr_t>{}, DiffTypeRange<std::ptr
diff _t>{}};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::
diff erence_type, std::common_type_t<intptr_t, std::ptr
diff _t>>);
+  }
+
+  const std::array foos{Foo{}};
+  std::array bars{Bar{}, Bar{}};
+  {
+    // value_type of single view
+    std::ranges::zip_view v{foos};
+    using Iter = decltype(v.begin());
+    static_assert(std::is_same_v<Iter::value_type, std::tuple<Foo>>);
+  }
+
+  {
+    // value_type of multiple views with 
diff erent value_type
+    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>>);
+  }
+
+  {
+    // const-iterator 
diff erent from iterator
+    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::
diff erence_type, std::ptr
diff _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::
diff erence_type, std::ptr
diff _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/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp
new file mode 100644
index 0000000000000..191c5aa8ee067
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, no-exceptions
+
+// If the invocation of any non-const member function of `iterator` exits via an
+// exception, the iterator acquires a singular value.
+
+#include <ranges>
+
+#include <tuple>
+
+#include "../types.h"
+
+struct ThrowOnIncrementIterator {
+  int* it_;
+
+  using value_type = int;
+  using 
diff erence_type = std::intptr_t;
+  using iterator_concept = std::input_iterator_tag;
+
+  ThrowOnIncrementIterator() = default;
+  explicit ThrowOnIncrementIterator(int* it) : it_(it) {}
+
+  ThrowOnIncrementIterator& operator++() {
+    ++it_;
+    throw 5;
+    return *this;
+  }
+  void operator++(int) { ++it_; }
+
+  int& operator*() const { return *it_; }
+
+  friend bool operator==(ThrowOnIncrementIterator const&, ThrowOnIncrementIterator const&) = default;
+};
+
+struct ThrowOnIncrementView : IntBufferView {
+  ThrowOnIncrementIterator begin() const { return ThrowOnIncrementIterator{buffer_}; }
+  ThrowOnIncrementIterator end() const { return ThrowOnIncrementIterator{buffer_ + size_}; }
+};
+
+// Cannot run the test at compile time because it is not allowed to throw exceptions
+void test() {
+  int buffer[] = {1, 2, 3};
+  {
+    // zip iterator should be able to be destroyed after member function throws
+    std::ranges::zip_view v{ThrowOnIncrementView{buffer}};
+    auto it = v.begin();
+    try {
+      ++it;
+      assert(false); // should not be reached as the above expression should throw.
+    } catch (int e) {
+      assert(e == 5);
+    }
+  }
+
+  {
+    // zip iterator should be able to be assigned after member function throws
+    std::ranges::zip_view v{ThrowOnIncrementView{buffer}};
+    auto it = v.begin();
+    try {
+      ++it;
+      assert(false); // should not be reached as the above expression should throw.
+    } catch (int e) {
+      assert(e == 5);
+    }
+    it = v.begin();
+    auto [x] = *it;
+    assert(x == 1);
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}

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
index 0000000000000..2641cf67f811a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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[](
diff erence_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};
+
+  {
+    // random_access_range
+    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>>);
+  }
+
+  {
+    // contiguous_range
+    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&>>);
+  }
+
+  {
+    // non random_access_range
+    std::ranges::zip_view v(BidiCommonView{buffer});
+    auto iter = v.begin();
+    const auto canSubscript = [](auto&& it) { return requires { it[0]; }; };
+    static_assert(!canSubscript(iter));
+  }
+
+  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
index 0000000000000..d1dcb75199f46
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
@@ -0,0 +1,332 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_asserts 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>);
+  }
+}
+
+using OutputIter = cpp17_output_iterator<int*>;
+static_assert(std::output_iterator<OutputIter, int>);
+
+struct OutputView : std::ranges::view_base {
+  OutputIter begin() const;
+  sentinel_wrapper<OutputIter> end() const;
+};
+static_assert(std::ranges::output_range<OutputView, int>);
+static_assert(!std::ranges::input_range<OutputView>);
+
+template <class... 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
index 0000000000000..5f8929ea5e3ce
--- /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
index 0000000000000..0af756f79e286
--- /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 NonSimpleNonCommonConvertibleView : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  constexpr int* begin() { return buffer_; }
+  constexpr const int* begin() const { return buffer_; }
+  constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_); }
+  constexpr convertible_sentinel_wrapper<const int*> end() const {
+    return convertible_sentinel_wrapper<const int*>(buffer_ + size_);
+  }
+};
+
+static_assert(!std::ranges::common_range<NonSimpleNonCommonConvertibleView>);
+static_assert(std::ranges::random_access_range<NonSimpleNonCommonConvertibleView>);
+static_assert(!std::ranges::sized_range<NonSimpleNonCommonConvertibleView>);
+static_assert(std::convertible_to<std::ranges::sentinel_t<NonSimpleNonCommonConvertibleView>,
+                                  std::ranges::sentinel_t<NonSimpleNonCommonConvertibleView const>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonConvertibleView>);
+
+constexpr bool test() {
+  int buffer1[4] = {1, 2, 3, 4};
+  int buffer2[5] = {1, 2, 3, 4, 5};
+  std::ranges::zip_view v{NonSimpleNonCommonConvertibleView(buffer1), NonSimpleNonCommonConvertibleView(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
index 0000000000000..0194c3451973d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 :  IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  constexpr auto begin() { return Iterator(buffer_); }
+  constexpr auto begin() const { return ConstIterator(buffer_); }
+  constexpr auto end() { return ComparableSentinel<false>(Iterator(buffer_ + size_)); }
+  constexpr auto end() const { return ComparableSentinel<true>(ConstIterator(buffer_ + size_)); }
+};
+
+struct ConstIncompatibleView : 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;
+};
+
+// clang-format off
+template <class Iter, class Sent>
+concept EqualComparable = std::invocable<std::equal_to<>, const Iter&, const Sent&>;
+// clang-format on
+
+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};
+  {
+    // simple-view: const and non-const have the same iterator/sentinel type
+    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());
+  }
+
+  {
+    // !simple-view: const and non-const have 
diff erent iterator/sentinel types
+    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>);
+  }
+
+  {
+    // underlying const/non-const sentinel can be compared with both const/non-const iterator
+    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>);
+  }
+
+  {
+    // underlying const/non-const sentinel cannot be compared with non-const/const iterator
+    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
index 0000000000000..5500bc99f5253
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp
@@ -0,0 +1,235 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_
diff erence_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_
diff erence_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 
diff erence_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 
diff erence_type operator-(const convertible_forward_sized_iterator& x,
+                                             const convertible_forward_sized_iterator& y) {
+    return x.it_ - y.it_;
+  }
+};
+static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
+
+template <class Base>
+struct convertible_sized_sentinel {
+  Base base_;
+  explicit convertible_sized_sentinel() = default;
+  constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
+
+  template <std::convertible_to<Base> U>
+  constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
+
+  template <class U>
+    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+  friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
+    return s.base_ == base;
+  }
+  template <class U>
+    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+  friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
+    return s.base_ - i;
+  }
+
+  template <class U>
+    requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+  friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
+    return i - s.base_;
+  }
+};
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
+                                      convertible_forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
+                                      convertible_forward_sized_iterator<int*>>);
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
+                                      convertible_forward_sized_iterator<const int*>>);
+
+struct ConstCompatibleForwardSized : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  using iterator = convertible_forward_sized_iterator<int*>;
+  using const_iterator = convertible_forward_sized_iterator<const int*>;
+
+  constexpr iterator begin() { return {buffer_}; }
+  constexpr const_iterator begin() const { return {buffer_}; }
+  constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
+  constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
+};
+
+// clang-format off
+template <class T, class U>
+concept HasMinus = std::invocable<std::minus<>,const T&, const U&>;
+
+template <class T>
+concept SentinelHasMinus = HasMinus<std::ranges::sentinel_t<T>, std::ranges::iterator_t<T>>;
+// clang-format on
+
+constexpr bool test() {
+  int buffer1[5] = {1, 2, 3, 4, 5};
+
+  {
+    // simple-view
+    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)>);
+  }
+
+  {
+    // shortest range
+    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)>);
+  }
+
+  {
+    // underlying sentinel does not model sized_sentinel_for
+    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 imcompatible:
+    // underlying const sentinels cannot substract underlying iterators
+    // underlying sentinels cannot substract underlying const iterators
+    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
index 0000000000000..776e34c79e1fc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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() {
+  {
+    // single range
+    std::ranges::zip_view v(View(8));
+    assert(v.size() == 8);
+    assert(std::as_const(v).size() == 8);
+  }
+
+  {
+    // multiple ranges same type
+    std::ranges::zip_view v(View(2), View(3));
+    assert(v.size() == 2);
+    assert(std::as_const(v).size() == 2);
+  }
+
+  {
+    // multiple ranges 
diff erent types
+    std::ranges::zip_view v(std::views::iota(0, 500), View(3));
+    assert(v.size() == 3);
+    assert(std::as_const(v).size() == 3);
+  }
+
+  {
+    // const-view non-sized range
+    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))>);
+  }
+
+  {
+    // const/non-const has 
diff erent sizes
+    std::ranges::zip_view v(StrangeSizeView{});
+    assert(v.size() == 5);
+    assert(std::as_const(v).size() == 6);
+  }
+
+  {
+    // underlying range not sized
+    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
index 0000000000000..662b87577675d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h
@@ -0,0 +1,459 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 BufferView : std::ranges::view_base {
+  T* buffer_;
+  std::size_t size_;
+
+  template <std::size_t N>
+  constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {}
+};
+
+using IntBufferView = BufferView<int>;
+
+template <bool Simple>
+struct Common :  IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+
+  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 
diff erence_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 
diff erence_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 : IntBufferView {
+  using IntBufferView::IntBufferView;
+  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 
diff erence_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>);
+
+namespace adltest{
+struct iter_move_swap_iterator {
+
+  std::reference_wrapper<int> iter_move_called_times;
+  std::reference_wrapper<int> iter_swap_called_times;
+  int i = 0;
+
+  using iterator_category = std::input_iterator_tag;
+  using value_type = int;
+  using 
diff erence_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; }
+};
+} // namespace adltest
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H


        


More information about the libcxx-commits mailing list