[libcxx-commits] [libcxx] [libc++][ranges] implement `std::ranges::zip_transform_view` (PR #79605)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jan 29 14:35:49 PST 2024
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/79605
>From 5fd084bf56e4da90fecb88d8fa71d3b2041e89cf Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Fri, 26 Jan 2024 15:12:33 +0000
Subject: [PATCH 1/2] [libc++][ranges] implement
`std::ranges::zip_transform_view`
---
libcxx/docs/Status/RangesViews.csv | 2 +-
libcxx/docs/Status/ZipProjects.csv | 2 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/zip_transform_view.h | 357 +++++++++++++
libcxx/include/__ranges/zip_view.h | 9 +
libcxx/include/module.modulemap.in | 1 +
libcxx/include/ranges | 11 +
.../no_unique_address.compile.pass.cpp | 38 ++
.../range.zip.transform/begin.pass.cpp | 74 +++
.../range.zip.transform/cpo.pass.cpp | 101 ++++
.../range.zip.transform/ctad.compile.pass.cpp | 37 ++
.../range.zip.transform/ctor.default.pass.cpp | 86 +++
.../range.zip.transform/ctor.views.pass.cpp | 89 ++++
.../range.zip.transform/end.pass.cpp | 100 ++++
.../range.zip.transform/general.pass.cpp | 29 +
.../range.zip.transform/size.pass.cpp | 92 ++++
.../range.zip.transform/types.h | 497 ++++++++++++++++++
..._robust_against_no_unique_address.pass.cpp | 1 +
18 files changed, 1525 insertions(+), 2 deletions(-)
create mode 100644 libcxx/include/__ranges/zip_transform_view.h
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index f141656eb131a2..1a8d6c3f940268 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -25,7 +25,7 @@ C++20,`istream <https://wg21.link/P1035R7>`_,Hui Xie,`D133317 <https://llvm.org/
C++23,`repeat <https://wg21.link/P2474R2>`_,Yrong,`D141699 <https://llvm.org/D141699>`_,✅
C++23,`cartesian_product <https://wg21.link/P2374R4>`_,Unassigned,No patch yet,Not started
C++23,`zip <https://wg21.link/P2321R2>`_,Hui Xie,`D122806 <https://llvm.org/D122806>`_,✅
-C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
+C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,✅
C++23,`adjacent <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,In progress
diff --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv
index 699a382ff66b73..b6d66acd233796 100644
--- a/libcxx/docs/Status/ZipProjects.csv
+++ b/libcxx/docs/Status/ZipProjects.csv
@@ -12,7 +12,7 @@ Section,Description,Dependencies,Assignee,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`", Hui Xie, |Not Started|
+| `zip_transform_view::sentinel`", Hui Xie, |Complete|
| `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Hui Xie, |Not Started|
| `[range.zip.transform.sentinel] <https://wg21.link/range.zip.transform.sentinel>`_, "zip_transform_view::sentinel", None, Hui Xie, |Not Started|
| `[range.adjacent.view] <https://wg21.link/range.adjacent.view>`_, "adjacent_view", "| `adjacent_view::iterator`
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ed721d467e94f4..15d4de2fbe6bcc 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -653,6 +653,7 @@ set(files
__ranges/view_interface.h
__ranges/views.h
__ranges/zip_view.h
+ __ranges/zip_transform_view.h
__split_buffer
__std_clang_module
__std_mbstate_t.h
diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h
new file mode 100644
index 00000000000000..4cc6ef55e8181e
--- /dev/null
+++ b/libcxx/include/__ranges/zip_transform_view.h
@@ -0,0 +1,357 @@
+// -*- 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_TRANSFORM_VIEW_H
+#define _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H
+
+#include <__config>
+
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
+#include <__concepts/invocable.h>
+#include <__functional/invoke.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/empty_view.h>
+#include <__ranges/movable_box.h>
+#include <__ranges/view_interface.h>
+#include <__ranges/zip_view.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/maybe_const.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <move_constructible _Fn, input_range... _Views>
+ requires(view<_Views> && ...) &&
+ (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> &&
+ __can_reference<invoke_result_t<_Fn&, range_reference_t<_Views>...>>
+class zip_transform_view : public view_interface<zip_transform_view<_Fn, _Views...>> {
+ _LIBCPP_NO_UNIQUE_ADDRESS zip_view<_Views...> __zip_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Fn> __fun_;
+
+ using _InnerView = zip_view<_Views...>;
+ template <bool _Const>
+ using __ziperator = iterator_t<__maybe_const<_Const, _InnerView>>;
+ template <bool Const>
+ using __zentinel = sentinel_t<__maybe_const<Const, _InnerView>>;
+
+ template <bool>
+ class __iterator;
+
+ template <bool>
+ class __sentinel;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI zip_transform_view() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views)
+ : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __zip_.begin()); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
+ {
+ return __iterator<true>(*this, __zip_.begin());
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+ if constexpr (common_range<_InnerView>) {
+ return __iterator<false>(*this, __zip_.end());
+ } else {
+ return __sentinel<false>(__zip_.end());
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires range<const _InnerView> && regular_invocable<const _Fn&, range_reference_t<const _Views>...>
+ {
+ if constexpr (common_range<const _InnerView>) {
+ return __iterator<true>(*this, __zip_.end());
+ } else {
+ return __sentinel<true>(__zip_.end());
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires sized_range<_InnerView>
+ {
+ return __zip_.size();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires sized_range<const _InnerView>
+ {
+ return __zip_.size();
+ }
+};
+
+template <class _Fn, class... _Ranges>
+zip_transform_view(_Fn, _Ranges&&...) -> zip_transform_view<_Fn, views::all_t<_Ranges>...>;
+
+template <bool _Const, class... _Views>
+concept __base_forward = forward_range<__maybe_const<_Const, zip_view<_Views...>>>;
+
+template <bool _Const, class _Fn, class... _Views>
+struct __zip_transform_iterator_category_base {};
+
+template <bool _Const, class _Fn, class... _Views>
+ requires __base_forward<_Const, _Views...>
+struct __zip_transform_iterator_category_base<_Const, _Fn, _Views...> {
+private:
+ template <class _View>
+ using __tag = typename iterator_traits<iterator_t<__maybe_const<_Const, _View>>>::iterator_category;
+
+ static consteval auto __get_iterator_category() {
+ if constexpr (!is_reference_v<invoke_result_t<__maybe_const<_Const, _Fn>&,
+ range_reference_t<__maybe_const<_Const, _Views>>...>>) {
+ return input_iterator_tag();
+ } else if constexpr ((derived_from<__tag<_Views>, random_access_iterator_tag> && ...)) {
+ return random_access_iterator_tag();
+ } else if constexpr ((derived_from<__tag<_Views>, bidirectional_iterator_tag> && ...)) {
+ return bidirectional_iterator_tag();
+ } else if constexpr ((derived_from<__tag<_Views>, forward_iterator_tag> && ...)) {
+ return forward_iterator_tag();
+ } else {
+ return input_iterator_tag();
+ }
+ }
+
+public:
+ using iterator_category = decltype(__get_iterator_category());
+};
+
+template <move_constructible _Fn, input_range... _Views>
+ requires(view<_Views> && ...) &&
+ (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> &&
+ __can_reference<invoke_result_t<_Fn&, range_reference_t<_Views>...>>
+template <bool _Const>
+class zip_transform_view<_Fn, _Views...>::__iterator
+ : public __zip_transform_iterator_category_base<_Const, _Fn, _Views...> {
+ using _Parent = __maybe_const<_Const, zip_transform_view>;
+ using _Base = __maybe_const<_Const, _InnerView>;
+
+ friend zip_transform_view<_Fn, _Views...>;
+
+ _Parent* __parent_ = nullptr;
+ __ziperator<_Const> __inner_;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, __ziperator<_Const> __inner)
+ : __parent_(std::addressof(__parent)), __inner_(std::move(__inner)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto __get_deref_and_invoke() const noexcept {
+ return [this](const auto&... __iters) noexcept(
+ noexcept(std::invoke(*__parent_->__fun_, *__iters...))) -> decltype(auto) {
+ return std::invoke(*__parent_->__fun_, *__iters...);
+ };
+ }
+
+public:
+ using iterator_concept = typename __ziperator<_Const>::iterator_concept;
+ using value_type =
+ remove_cvref_t<invoke_result_t<__maybe_const<_Const, _Fn>&, range_reference_t<__maybe_const<_Const, _Views>>...>>;
+ using difference_type = range_difference_t<_Base>;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && convertible_to<__ziperator<false>, __ziperator<_Const>>
+ : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+ noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) {
+ return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ ++__inner_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+ requires forward_range<_Base>
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+ requires bidirectional_range<_Base>
+ {
+ --__inner_;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+ requires bidirectional_range<_Base>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x)
+ requires random_access_range<_Base>
+ {
+ __inner_ += __x;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x)
+ requires random_access_range<_Base>
+ {
+ __inner_ -= __x;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+ requires random_access_range<_Base>
+ {
+ return std::apply(
+ [&]<class... _Is>(const _Is&... __iters) -> decltype(auto) {
+ return std::invoke(*__parent_->__fun_, __iters[iter_difference_t<_Is>(__n)]...);
+ },
+ __zip_view_iterator_access::__get_underlying(__inner_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
+ requires equality_comparable<__ziperator<_Const>>
+ {
+ return __x.__inner_ == __y.__inner_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __x.__inner_ <=> __y.__inner_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
+ requires random_access_range<_Base>
+ {
+ return __iterator(*__i.__parent_, __i.__inner_ + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
+ requires random_access_range<_Base>
+ {
+ return __iterator(*__i.__parent_, __i.__inner_ + __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
+ requires random_access_range<_Base>
+ {
+ return __iterator(*__i.__parent_, __i.__inner_ - __n);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+ requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>>
+ {
+ return __x.__inner_ - __y.__inner_;
+ }
+};
+
+template <move_constructible _Fn, input_range... _Views>
+ requires(view<_Views> && ...) &&
+ (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> &&
+ __can_reference<invoke_result_t<_Fn&, range_reference_t<_Views>...>>
+template <bool _Const>
+class zip_transform_view<_Fn, _Views...>::__sentinel {
+ __zentinel<_Const> __inner_;
+
+ friend zip_transform_view<_Fn, _Views...>;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(__zentinel<_Const> __inner) : __inner_(__inner) {}
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __i)
+ requires _Const && convertible_to<__zentinel<false>, __zentinel<_Const>>
+ : __inner_(__i.__inner_) {}
+
+ template <bool _OtherConst>
+ requires sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+ return __x.__inner_ == __y.__inner_;
+ }
+
+ template <bool _OtherConst>
+ requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+ operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+ return __x.__inner_ - __y.__inner_;
+ }
+
+ template <bool _OtherConst>
+ requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>>
+ _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
+ operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
+ return __x.__inner_ - __y.__inner_;
+ }
+};
+
+namespace views {
+namespace __zip_transform {
+
+struct __fn {
+ template <class _Fn>
+ requires(move_constructible<decay_t<_Fn>> && regular_invocable<decay_t<_Fn>&> &&
+ is_object_v<invoke_result_t<decay_t<_Fn>&>>)
+ _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const
+ noexcept(noexcept(auto(views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>))) {
+ return views::empty<decay_t<invoke_result_t<decay_t<_Fn>&>>>;
+ }
+
+ template <class _Fn, class... _Ranges>
+ requires(sizeof...(_Ranges) > 0)
+ _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const
+ noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)))
+ -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) {
+ return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...);
+ }
+};
+
+} // namespace __zip_transform
+inline namespace __cpo {
+inline constexpr auto zip_transform = __zip_transform::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H
diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h
index 4898c0afc87a6e..deb87d359c6025 100644
--- a/libcxx/include/__ranges/zip_view.h
+++ b/libcxx/include/__ranges/zip_view.h
@@ -245,6 +245,13 @@ struct __zip_view_iterator_category_base<_Const, _Views...> {
using iterator_category = input_iterator_tag;
};
+struct __zip_view_iterator_access {
+ template <class _Iter>
+ _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_underlying(_Iter& __iter) noexcept {
+ return (__iter.__current_);
+ }
+};
+
template <input_range... _Views>
requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
template <bool _Const>
@@ -263,6 +270,8 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base
friend class zip_view<_Views...>;
+ friend __zip_view_iterator_access;
+
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>>...>;
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 194a74a1e07b14..0a3ba7ea49d3b1 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1719,6 +1719,7 @@ module std_private_ranges_transform_view [system] {
module std_private_ranges_view_interface [system] { header "__ranges/view_interface.h" }
module std_private_ranges_views [system] { header "__ranges/views.h" }
module std_private_ranges_zip_view [system] { header "__ranges/zip_view.h" }
+module std_private_ranges_zip_transform_view [system] { header "__ranges/zip_transform_view.h" }
module std_private_span_span_fwd [system] { header "__fwd/span.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 660d533b2a7830..2a692567261e4c 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -325,6 +325,16 @@ namespace std::ranges {
namespace views { inline constexpr unspecified zip = unspecified; } // C++23
+ // [range.zip.transform], zip transform view
+ template<move_constructible F, input_range... Views>
+ requires (view<Views> && ...) && (sizeof...(Views) > 0) && is_object_v<F> &&
+ regular_invocable<F&, range_reference_t<Views>...> &&
+ can-reference<invoke_result_t<F&, range_reference_t<Views>...>>
+ class zip_transform_view; // C++23
+
+ namespace views { inline constexpr unspecified zip_transform = unspecified; } // C++23
+
+
// [range.as.rvalue]
template <view V>
requires input_range<V>
@@ -413,6 +423,7 @@ namespace std {
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <__ranges/views.h>
+#include <__ranges/zip_transform_view.h>
#include <__ranges/zip_view.h>
#include <version>
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp
new file mode 100644
index 00000000000000..7d15a819a38189
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: msvc
+
+// This test ensures that we use `[[no_unique_address]]` in `zip_transform_view`.
+
+#include <ranges>
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ template <class... Args>
+ bool operator()(const Args&...) const;
+};
+
+template <class View>
+struct Test {
+ [[no_unique_address]] View view;
+ char c;
+};
+
+// [[no_unique_address]] applied to movable-box
+struct PredWithPadding : Pred {
+ alignas(128) char c;
+};
+
+static_assert(sizeof(Test<std::ranges::zip_transform_view<PredWithPadding, View>>) ==
+ sizeof(std::ranges::zip_transform_view<PredWithPadding, View>));
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp
new file mode 100644
index 00000000000000..f63a944bfde19c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.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
+
+// constexpr auto begin();
+// constexpr auto begin() const
+// requires range<const InnerView> &&
+// regular_invocable<const F&, range_reference_t<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(); };
+
+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_transform_view v(
+ MakeTuple{}, SimpleCommon{buffer}, std::views::iota(0), std::ranges::single_view(2.));
+ auto it = v.begin();
+ assert(*it == std::make_tuple(1, 0, 2.0));
+
+ auto const_it = std::as_const(v).begin();
+ assert(*const_it == *it);
+
+ static_assert(!std::same_as<decltype(it), decltype(const_it)>);
+ }
+
+ {
+ // with empty range
+ std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view<int>());
+ assert(v.begin() == v.end());
+ assert(std::as_const(v).begin() == std::as_const(v).end());
+ }
+
+ {
+ // underlying const R is not a range
+ using View = std::ranges::zip_transform_view<MakeTuple, SimpleCommon, NoConstBeginView>;
+ static_assert(HasBegin<View>);
+ static_assert(!HasConstBegin<View>);
+ }
+
+ {
+ // Fn cannot be invoked on const range
+ using View = std::ranges::zip_transform_view<NonConstOnlyFn, ConstNonConstDifferentView>;
+ static_assert(HasBegin<View>);
+ static_assert(!HasConstBegin<View>);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp
new file mode 100644
index 00000000000000..ec3f734a0b05bd
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// std::views::zip_transform
+
+#include <ranges>
+
+#include <array>
+#include <algorithm>
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct NotMoveConstructible {
+ NotMoveConstructible() = default;
+ NotMoveConstructible(NotMoveConstructible&&) = delete;
+ int operator()() const { return 5; }
+};
+
+struct NotInvocable {};
+
+template <class... Args>
+struct Invocable {
+ int operator()(Args...) const { return 5; }
+};
+
+struct ReturnNotObject {
+ void operator()() const {}
+};
+
+static_assert(!std::is_invocable_v<decltype((std::views::zip_transform))>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip_transform)), NotMoveConstructible>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip_transform)), NotInvocable>);
+static_assert(std::is_invocable_v<decltype((std::views::zip_transform)), Invocable<>>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip_transform)), ReturnNotObject>);
+
+static_assert(std::is_invocable_v<decltype((std::views::zip_transform)), //
+ Invocable<int>, //
+ std::ranges::iota_view<int, int>>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip_transform)), //
+ Invocable<>, //
+ std::ranges::iota_view<int, int>>);
+static_assert(!std::is_invocable_v<decltype((std::views::zip_transform)),
+ Invocable<int>,
+ std::ranges::iota_view<int, int>,
+ std::ranges::iota_view<int, int>>);
+static_assert(std::is_invocable_v<decltype((std::views::zip_transform)),
+ Invocable<int, int>,
+ std::ranges::iota_view<int, int>,
+ std::ranges::iota_view<int, int>>);
+
+constexpr bool test() {
+ {
+ // zip_transform function with no ranges
+ auto v = std::views::zip_transform(Invocable<>{});
+ assert(std::ranges::empty(v));
+ static_assert(std::is_same_v<decltype(v), std::ranges::empty_view<int>>);
+ }
+
+ {
+ // zip_transform views
+ int buffer1[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ int buffer2[] = {9, 10, 11, 12};
+ auto view1 = std::views::all(buffer1);
+ auto view2 = std::views::all(buffer2);
+ std::same_as<std::ranges::zip_transform_view<std::plus<>, decltype(view1), decltype(view2)>> decltype(auto) v =
+ std::views::zip_transform(std::plus{}, buffer1, buffer2);
+ assert(std::ranges::size(v) == 4);
+ auto expected = {10, 12, 14, 16};
+ assert(std::ranges::equal(v, expected));
+ static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, int>);
+ }
+
+ {
+ // zip_transform a viewable range
+ std::array a{1, 2, 3};
+ auto id = [](auto& x) -> decltype(auto) { return (x); };
+ std::same_as<
+ std::ranges::zip_transform_view<decltype(id), std::ranges::ref_view<std::array<int, 3>>>> decltype(auto) v =
+ std::views::zip_transform(id, a);
+ assert(&v[0] == &a[0]);
+ static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, int&>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp
new file mode 100644
index 00000000000000..c576e6612ececb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<class F, class... Rs>
+// zip_transform_view(F, Rs&&...) -> zip_transform_view<F, views::all_t<Rs>...>;
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+
+#include "types.h"
+struct Container {
+ int* begin() const;
+ int* end() const;
+};
+
+void testCTAD() {
+ static_assert(std::is_same_v<decltype(std::ranges::zip_transform_view(Fn{}, Container{})),
+ std::ranges::zip_transform_view<Fn, std::ranges::owning_view<Container>>>);
+
+ static_assert(std::is_same_v<decltype(std::ranges::zip_transform_view(Fn{}, Container{}, View{})),
+ std::ranges::zip_transform_view<Fn, std::ranges::owning_view<Container>, View>>);
+
+ Container c{};
+ static_assert(
+ std::is_same_v<
+ decltype(std::ranges::zip_transform_view(Fn{}, Container{}, View{}, c)),
+ std::ranges::
+ zip_transform_view<Fn, std::ranges::owning_view<Container>, View, std::ranges::ref_view<Container>>>);
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..96d625ae7dc32e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// zip_transform_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 NonDefaultConstructibleView : std::ranges::view_base {
+ NonDefaultConstructibleView() = delete;
+ int* begin() const;
+ int* end() const;
+};
+
+struct DefaultConstructibleFn {
+ constexpr int operator()(const auto&... x) const { return (x + ...); }
+};
+
+struct NonDefaultConstructibleFn {
+ NonDefaultConstructibleFn() = delete;
+ constexpr int operator()(const auto&... x) const;
+};
+
+// The default constructor requires all underlying views to be default constructible.
+// It is implicitly required by the zip_view's constructor.
+static_assert(std::is_default_constructible_v<std::ranges::zip_transform_view< //
+ DefaultConstructibleFn, //
+ DefaultConstructibleView>>);
+static_assert(std::is_default_constructible_v<std::ranges::zip_transform_view< //
+ DefaultConstructibleFn, //
+ DefaultConstructibleView,
+ DefaultConstructibleView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_transform_view< //
+ NonDefaultConstructibleFn, //
+ DefaultConstructibleView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_transform_view< //
+ DefaultConstructibleFn, //
+ NonDefaultConstructibleView>>);
+static_assert(!std::is_default_constructible_v<std::ranges::zip_transform_view< //
+ DefaultConstructibleFn, //
+ DefaultConstructibleView,
+ NonDefaultConstructibleView>>);
+
+constexpr bool test() {
+ {
+ using View =
+ std::ranges::zip_transform_view<DefaultConstructibleFn, DefaultConstructibleView, DefaultConstructibleView>;
+ View v = View(); // the default constructor is not explicit
+ assert(v.size() == 3);
+ auto it = v.begin();
+ assert(*it++ == 2);
+ assert(*it++ == 4);
+ assert(*it == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp
new file mode 100644
index 00000000000000..99a2e6caaa4fb8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr explicit zip_transform_view(F, Views...)
+
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T, class... Args>
+concept IsImplicitlyConstructible = requires(T val, Args... args) { val = {std::forward<Args>(args)...}; };
+
+// test constructor is explicit
+static_assert(std::constructible_from<std::ranges::zip_transform_view<Fn, View>, Fn, View>);
+static_assert(!IsImplicitlyConstructible<std::ranges::zip_transform_view<Fn, View>, Fn, View>);
+
+static_assert(std::constructible_from<std::ranges::zip_transform_view<Fn, View, View>, Fn, View, View>);
+static_assert(!IsImplicitlyConstructible<std::ranges::zip_transform_view<Fn, View, View>, Fn, View, View>);
+
+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_transform_view v{MakeTuple{}, 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_transform_view v(
+ MakeTuple{}, 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_transform_view v{MakeTuple{}, std::move(mv), MoveAwareView{}};
+ auto [numMoves1, numMoves2] = *v.begin();
+ assert(numMoves1 == 3); // one move from the local variable to parameter, one move from parameter to member
+ assert(numMoves2 == 2);
+ }
+
+ // 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.transform/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp
new file mode 100644
index 00000000000000..820c4675b8d2db
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.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
+
+// constexpr auto end()
+// constexpr auto end() const
+// requires range<const InnerView> &&
+// regular_invocable<const F&, range_reference_t<const Views>...>;
+
+#include <ranges>
+#include <tuple>
+
+#include "types.h"
+
+template <class T>
+concept HasConstEnd = requires(const T& ct) { ct.end(); };
+
+template <class T>
+concept HasEnd = requires(T& t) { t.end(); };
+
+constexpr bool test() {
+ int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+ {
+ // simple test
+ std::ranges::zip_transform_view v(
+ MakeTuple{}, SimpleCommon{buffer}, std::views::iota(0), std::ranges::single_view(2.));
+ assert(v.begin() != v.end());
+ assert(std::as_const(v).begin() != std::as_const(v).end());
+ assert(v.begin() + 1 == v.end());
+ assert(std::as_const(v).begin() + 1 == std::as_const(v).end());
+ }
+
+ {
+ // with empty range
+ std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view<int>());
+ assert(v.begin() == v.end());
+ assert(std::as_const(v).begin() == std::as_const(v).end());
+ }
+
+ {
+ // common_range<InnerView>
+ std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer});
+ auto it = v.begin();
+ auto const_it = std::as_const(v).begin();
+ auto st = v.end();
+ auto const_st = std::as_const(v).end();
+
+ static_assert(!std::same_as<decltype(it), decltype(const_it)>);
+ static_assert(!std::same_as<decltype(st), decltype(const_st)>);
+ static_assert(std::same_as<decltype(it), decltype(st)>);
+ static_assert(std::same_as<decltype(const_it), decltype(const_st)>);
+
+ assert(it + 8 == st);
+ assert(const_it + 8 == const_st);
+ }
+ {
+ // !common_range<InnerView>
+ std::ranges::zip_transform_view v(MakeTuple{}, SimpleNonCommon{buffer});
+ auto it = v.begin();
+ auto const_it = std::as_const(v).begin();
+ auto st = v.end();
+ auto const_st = std::as_const(v).end();
+
+ static_assert(!std::same_as<decltype(it), decltype(const_it)>);
+ static_assert(!std::same_as<decltype(st), decltype(const_st)>);
+ static_assert(!std::same_as<decltype(it), decltype(st)>);
+ static_assert(!std::same_as<decltype(const_it), decltype(const_st)>);
+
+ assert(it + 8 == st);
+ assert(const_it + 8 == const_st);
+ }
+
+ {
+ // underlying const R is not a range
+ using View = std::ranges::zip_transform_view<MakeTuple, SimpleCommon, NoConstBeginView>;
+ static_assert(HasEnd<View>);
+ static_assert(!HasConstEnd<View>);
+ }
+
+ {
+ // Fn cannot invoke on const range
+ using View = std::ranges::zip_transform_view<NonConstOnlyFn, ConstNonConstDifferentView>;
+ static_assert(HasEnd<View>);
+ static_assert(!HasConstEnd<View>);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp
new file mode 100644
index 00000000000000..3c35de27deb692
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Some basic examples of how zip_tranform_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 <algorithm>
+#include <cassert>
+#include <functional>
+#include <vector>
+
+int main(int, char**) {
+ std::vector v1 = {1, 2};
+ std::vector v2 = {4, 5, 6};
+ auto ztv = std::views::zip_transform(std::plus(), v1, v2);
+ auto expected = {5, 7};
+ assert(std::ranges::equal(ztv, expected));
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp
new file mode 100644
index 00000000000000..c973f7cc4fcbc3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr auto size() requires sized_range<InnerView>
+// constexpr auto size() const requires sized_range<const InnerView>
+
+#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 SizedView : std::ranges::view_base {
+ std::size_t size_ = 0;
+ constexpr SizedView(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 ConstNonConstDifferentSize : 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_transform_view v(MakeTuple{}, SizedView(8));
+ assert(v.size() == 8);
+ assert(std::as_const(v).size() == 8);
+ }
+
+ {
+ // multiple ranges
+ std::ranges::zip_transform_view v(MakeTuple{}, SizedView(2), SizedView(3));
+ assert(v.size() == 2);
+ assert(std::as_const(v).size() == 2);
+ }
+
+ {
+ // const-view non-sized range
+ std::ranges::zip_transform_view v(MakeTuple{}, SizedNonConst(2), SizedView(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 different sizes
+ std::ranges::zip_transform_view v(MakeTuple{}, ConstNonConstDifferentSize{});
+ assert(v.size() == 5);
+ assert(std::as_const(v).size() == 6);
+ }
+
+ {
+ // underlying range not sized
+ std::ranges::zip_transform_view v(MakeTuple{}, 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.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h
new file mode 100644
index 00000000000000..02ce0131a78fc5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h
@@ -0,0 +1,497 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_TRANSFORM_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TRANSFORM_TYPES_H
+
+#include <functional>
+#include <ranges>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+#if TEST_STD_VER <= 20
+# error "range.zip.transform/types.h" can only be included in builds supporting C++20
+#endif // TEST_STD_VER <= 20
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Fn {
+ int operator()(auto&&...) const;
+};
+
+struct MakeTuple {
+ constexpr auto operator()(auto&&... args) const { return std::tuple(std::forward<decltype(args)>(args)...); }
+};
+
+struct NoConstBeginView : std::ranges::view_base {
+ int* begin();
+ int* end();
+};
+
+struct ConstNonConstDifferentView : std::ranges::view_base {
+ int* begin();
+ const int* begin() const;
+ int* end();
+ const int* end() const;
+};
+
+struct NonConstOnlyFn {
+ int operator()(int&) const;
+ int operator()(const int&) const = delete;
+};
+
+
+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>);
+static_assert(simple_view<SimpleCommon>);
+static_assert(!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>);
+static_assert(simple_view<SimpleCommonNonRandom>);
+static_assert(!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>);
+static_assert(simple_view<SimpleNonCommon>);
+static_assert(!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 SimpleNonCommonRandomAccessSized = SimpleNonCommonSized;
+using NonSimpleNonCommonSized = NonCommonSized<false>;
+using NonSimpleNonCommonRandomAccessSized = NonSimpleNonCommonSized;
+
+static_assert(!std::ranges::common_range<SimpleNonCommonSized>);
+static_assert(std::ranges::random_access_range<SimpleNonCommonSized>);
+static_assert(std::ranges::sized_range<SimpleNonCommonSized>);
+static_assert(simple_view<SimpleNonCommonSized>);
+static_assert(!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>);
+static_assert(simple_view<SimpleNonCommonNonRandom>);
+static_assert(!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 difference_type = std::intptr_t;
+ using pointer = Base;
+ using reference = decltype(*Base{});
+
+ forward_sized_iterator() = default;
+ constexpr forward_sized_iterator(Base it) : it_(it) {}
+
+ constexpr reference operator*() const { return *it_; }
+
+ constexpr forward_sized_iterator& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+ friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
+
+ friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
+ return x.it_ - y.it_;
+ }
+};
+static_assert(std::forward_iterator<forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
+
+using ForwardSizedView = BasicView<forward_sized_iterator<>>;
+static_assert(std::ranges::forward_range<ForwardSizedView>);
+static_assert(std::ranges::sized_range<ForwardSizedView>);
+static_assert(std::ranges::common_range<ForwardSizedView>);
+static_assert(!std::ranges::random_access_range<ForwardSizedView>);
+static_assert(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>);
+static_assert(!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>);
+static_assert(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>);
+static_assert(!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>);
+static_assert(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>);
+static_assert(!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>);
+
+using InputCommonView = BasicView<common_input_iterator<int*>>;
+static_assert(std::ranges::input_range<InputCommonView>);
+static_assert(!std::ranges::forward_range<InputCommonView>);
+static_assert(std::ranges::common_range<InputCommonView>);
+static_assert(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>);
+static_assert(!simple_view<NonSimpleInputCommonView>);
+
+using InputNonCommonView = BasicView<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>);
+static_assert(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>);
+static_assert(!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>);
+static_assert(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>);
+static_assert(!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>);
+static_assert(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>);
+static_assert(!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>);
+static_assert(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>);
+static_assert(!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>);
+static_assert(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>);
+static_assert(!simple_view<NonSimpleSizedBidiNonCommonView>);
+
+namespace adltest {
+struct iter_move_swap_iterator {
+ std::reference_wrapper<int> iter_move_called_times;
+ std::reference_wrapper<int> iter_swap_called_times;
+ int i = 0;
+
+ using iterator_category = std::input_iterator_tag;
+ using value_type = int;
+ using difference_type = std::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_TRANSFORM_TYPES_H
diff --git a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp
index 3b35271d649c38..06186656f01222 100644
--- a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp
+++ b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp
@@ -58,6 +58,7 @@ constexpr bool test() {
#if TEST_STD_VER >= 23
testOne<std::ranges::chunk_by_view<View, Pred>>();
testOne<std::ranges::repeat_view<Pred>>();
+ testOne<std::ranges::zip_transform_view<Pred, View>>();
#endif
return true;
}
>From ba11db4eea274850371a20385653ae17a0180f6d Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Mon, 29 Jan 2024 22:35:37 +0000
Subject: [PATCH 2/2] add tests
---
.../range.zip.transform/begin.pass.cpp | 2 -
.../range.zip.transform/cpo.pass.cpp | 4 +-
.../range.zip.transform/ctad.compile.pass.cpp | 1 -
.../range.zip.transform/ctor.default.pass.cpp | 1 -
.../range.zip.transform/ctor.views.pass.cpp | 1 -
.../range.zip.transform/end.pass.cpp | 1 -
.../iterator/arithmetic.pass.cpp | 139 ++++++++++++
.../iterator/compare.pass.cpp | 160 ++++++++++++++
.../iterator/ctor.default.pass.cpp | 50 +++++
.../iterator/ctor.other.pass.cpp | 63 ++++++
.../iterator/decrement.pass.cpp | 84 +++++++
.../iterator/deref.pass.cpp | 97 ++++++++
.../iterator/increment.pass.cpp | 80 +++++++
.../iterator/member_types.compile.pass.cpp | 158 +++++++++++++
.../iterator/subscript.pass.cpp | 68 ++++++
.../sentinel/ctor.default.pass.cpp | 51 +++++
.../sentinel/ctor.other.pass.cpp | 76 +++++++
.../range.zip.transform/sentinel/eq.pass.cpp | 142 ++++++++++++
.../sentinel/minus.pass.cpp | 209 ++++++++++++++++++
.../range.zip.transform/size.pass.cpp | 2 -
.../range.zip.transform/types.h | 7 +-
21 files changed, 1383 insertions(+), 13 deletions(-)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/deref.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.other.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp
index f63a944bfde19c..fe91fe09830131 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp
@@ -17,8 +17,6 @@
#include <cassert>
#include <concepts>
-#include <tuple>
-#include <utility>
#include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp
index ec3f734a0b05bd..9d6be5815fa35f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp
@@ -12,12 +12,10 @@
#include <ranges>
-#include <array>
#include <algorithm>
+#include <array>
#include <cassert>
-#include <tuple>
#include <type_traits>
-#include <utility>
struct NotMoveConstructible {
NotMoveConstructible() = default;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp
index c576e6612ececb..9eed2fb37d49f8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp
@@ -13,7 +13,6 @@
#include <cassert>
#include <ranges>
-#include <utility>
#include "types.h"
struct Container {
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp
index 96d625ae7dc32e..87d05d49e956df 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp
@@ -14,7 +14,6 @@
#include <cassert>
#include <type_traits>
-#include <utility>
constexpr int buff[] = {1, 2, 3};
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp
index 99a2e6caaa4fb8..0c101431513794 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp
@@ -11,7 +11,6 @@
// constexpr explicit zip_transform_view(F, Views...)
#include <ranges>
-#include <tuple>
#include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp
index 820c4675b8d2db..c887adcf4a1d36 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp
@@ -14,7 +14,6 @@
// regular_invocable<const F&, range_reference_t<const Views>...>;
#include <ranges>
-#include <tuple>
#include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp
new file mode 100644
index 00000000000000..99e604307b9a62
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr iterator& operator+=(difference_type x) requires random_access_range<Base>;
+// constexpr iterator& operator-=(difference_type x) requires random_access_range<Base>;
+// friend constexpr iterator operator+(const iterator& i, difference_type n)
+// requires random_access_range<Base>;
+// friend constexpr iterator operator+(difference_type n, const iterator& i)
+// requires random_access_range<Base>;
+// friend constexpr iterator operator-(const iterator& i, difference_type n)
+// requires random_access_range<Base>;
+// friend constexpr difference_type operator-(const iterator& x, const iterator& y)
+// requires sized_sentinel_for<ziperator<Const>, ziperator<Const>>;
+
+#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 canPlus = requires(T& t, U& u) { t + u; };
+
+template <class T, class U>
+concept canMinusEqual = requires(T& t, U& u) { t -= u; };
+
+template <class T, class U>
+concept canMinus = requires(T& t, U& u) { t - u; };
+
+constexpr bool test() {
+ int buffer1[5] = {1, 2, 3, 4, 5};
+ 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_transform_view v(MakeTuple{}, a, b);
+ auto it1 = v.begin();
+ using Iter = decltype(it1);
+
+ std::same_as<Iter> decltype(auto) it2 = it1 + 3;
+ assert(*it2 == std::tuple(4, 0.1));
+
+ std::same_as<Iter> decltype(auto) it3 = 3 + it1;
+ assert(*it3 == std::tuple(4, 0.1));
+
+ std::same_as<Iter&> decltype(auto) it1_ref = it1 += 3;
+ assert(&it1_ref == &it1);
+ assert(*it1_ref == std::tuple(4, 0.1));
+ assert(*it1 == std::tuple(4, 0.1));
+
+ static_assert(canPlus<Iter, std::intptr_t>);
+ static_assert(canPlusEqual<Iter, std::intptr_t>);
+ }
+
+ {
+ // operator-(x, n) and operator-=
+ std::ranges::zip_transform_view v(MakeTuple{}, a, b);
+ auto it1 = v.end();
+ using Iter = decltype(it1);
+
+ std::same_as<Iter> decltype(auto) it2 = it1 - 3;
+ assert(*it2 == std::tuple(3, 4.3));
+
+ std::same_as<Iter&> decltype(auto) it1_ref = it1 -= 3;
+ assert(&it1_ref == &it1);
+ assert(*it1_ref == std::tuple(3, 4.3));
+ assert(*it1 == std::tuple(3, 4.3));
+
+ static_assert(canMinusEqual<Iter, std::intptr_t>);
+ static_assert(canMinus<Iter, std::intptr_t>);
+ }
+
+ {
+ // operator-(x, y)
+ std::ranges::zip_transform_view v(MakeTuple{}, a, b);
+ assert((v.end() - v.begin()) == 5);
+
+ auto it1 = v.begin() + 2;
+ auto it2 = v.end() - 1;
+
+ using Iter = decltype(it1);
+
+ std::same_as<std::iter_difference_t<Iter>> decltype(auto) n = it1 - it2;
+ assert(n == -2);
+ }
+
+ {
+ // One of the ranges is not random access
+ std::ranges::zip_transform_view v(MakeTuple{}, a, b, ForwardSizedView{buffer1});
+ auto it1 = v.begin();
+ using Iter = decltype(it1);
+ static_assert(!canPlus<Iter, std::intptr_t>);
+ static_assert(!canPlus<std::intptr_t, Iter>);
+ static_assert(!canPlusEqual<Iter, std::intptr_t>);
+ static_assert(!canMinus<Iter, std::intptr_t>);
+ static_assert(canMinus<Iter, Iter>);
+ static_assert(!canMinusEqual<Iter, std::intptr_t>);
+
+ auto it2 = ++v.begin();
+ assert((it2 - it1) == 1);
+ }
+
+ {
+ // One of the ranges does not have sized sentinel
+ std::ranges::zip_transform_view v(MakeTuple{}, a, b, InputCommonView{buffer1});
+ using Iter = decltype(v.begin());
+ static_assert(!canPlus<Iter, std::intptr_t>);
+ static_assert(!canPlus<std::intptr_t, Iter>);
+ static_assert(!canPlusEqual<Iter, std::intptr_t>);
+ static_assert(!canMinus<Iter, std::intptr_t>);
+ static_assert(!canMinus<Iter, Iter>);
+ static_assert(!canMinusEqual<Iter, std::intptr_t>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp
new file mode 100644
index 00000000000000..e879a181bbf562
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp
@@ -0,0 +1,160 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// friend constexpr bool operator==(const iterator& x, const iterator& y)
+// requires equality_comparable<ziperator<Const>>;
+
+// friend constexpr auto operator<=>(const iterator& x, const iterator& y)
+// requires random_access_range<Base>;
+
+#include <ranges>
+
+#include <compare>
+
+#include "test_iterators.h"
+#include "../types.h"
+
+constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) {
+ assert(!(iter1 < iter1));
+ assert(iter1 < iter2);
+ assert(!(iter2 < iter1));
+ assert(iter1 <= iter1);
+ assert(iter1 <= iter2);
+ assert(!(iter2 <= iter1));
+ assert(!(iter1 > iter1));
+ assert(!(iter1 > iter2));
+ assert(iter2 > iter1);
+ assert(iter1 >= iter1);
+ assert(!(iter1 >= iter2));
+ assert(iter2 >= iter1);
+ assert(iter1 == iter1);
+ assert(!(iter1 == iter2));
+ assert(iter2 == iter2);
+ assert(!(iter1 != iter1));
+ assert(iter1 != iter2);
+ assert(!(iter2 != iter2));
+}
+
+constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) {
+ using Iter1 = decltype(iter1);
+ using Iter2 = decltype(iter2);
+ static_assert(!std::is_invocable_v<std::less<>, Iter1, Iter2>);
+ static_assert(!std::is_invocable_v<std::less_equal<>, Iter1, Iter2>);
+ static_assert(!std::is_invocable_v<std::greater<>, Iter1, Iter2>);
+ static_assert(!std::is_invocable_v<std::greater_equal<>, Iter1, Iter2>);
+}
+
+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>);
+
+ int a[] = {1, 2, 3, 4};
+ int b[] = {5, 6, 7, 8, 9};
+ auto r = std::views::zip_transform(MakeTuple{}, SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5)));
+ auto iter1 = r.begin();
+ auto iter2 = iter1 + 1;
+ using Iter = decltype(iter1);
+ static_assert(std::three_way_comparable<Iter>);
+ 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>);
+
+ int a[] = {1, 2, 3, 4};
+ int b[] = {5, 6, 7, 8, 9};
+ auto r = std::views::zip_transform(MakeTuple{}, Subrange(It(a), It(a + 4)), Subrange(It(b), It(b + 5)));
+ auto iter1 = r.begin();
+ using Iter = decltype(iter1);
+#ifndef _LIBCPP_VERSION
+ // libc++ hasn't implemented LWG-3692 "zip_transform_view::iterator's operator<=> is overconstrained"
+ auto iter2 = iter1 + 1;
+
+ compareOperatorTest(iter1, iter2);
+ static_assert(std::three_way_comparable<Iter>);
+ assert((iter1 <=> iter2) == std::strong_ordering::less);
+ assert((iter1 <=> iter1) == std::strong_ordering::equal);
+ assert((iter2 <=> iter1) == std::strong_ordering::greater);
+#endif
+ }
+
+ {
+ // non random_access_range
+ int buffer1[1] = {1};
+ int buffer2[2] = {1, 2};
+
+ std::ranges::zip_transform_view v{MakeTuple{}, 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_transform_view v{MakeTuple{}, ForwardSizedView(buffer1), ForwardSizedView(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);
+ }
+
+ {
+ // underlying iterator does not support ==
+ using IterNoEqualView = BasicView<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>;
+ int buffer[] = {1};
+ std::ranges::zip_transform_view r(MakeTuple{}, 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.transform/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..4e16d839204cd5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// iterator() = default;
+
+#include <ranges>
+
+#include "../types.h"
+
+struct IterDefaultCtrView : std::ranges::view_base {
+ int* begin() const;
+ int* 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 Iter = std::ranges::iterator_t<std::ranges::zip_transform_view<MakeTuple, Views...>>;
+
+static_assert(!std::default_initializable<Iter<IterNoDefaultCtrView>>);
+static_assert(!std::default_initializable<Iter<IterNoDefaultCtrView, IterDefaultCtrView>>);
+static_assert(!std::default_initializable<Iter<IterNoDefaultCtrView, IterNoDefaultCtrView>>);
+static_assert(std::default_initializable<Iter<IterDefaultCtrView>>);
+static_assert(std::default_initializable<Iter<IterDefaultCtrView, IterDefaultCtrView>>);
+
+constexpr bool test() {
+ using ZipTransformIter = std::ranges::iterator_t<std::ranges::zip_transform_view<MakeTuple, IterDefaultCtrView>>;
+ ZipTransformIter iter1 = {};
+ ZipTransformIter iter2;
+ assert(iter1 == iter2);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.pass.cpp
new file mode 100644
index 00000000000000..ce7b9eb1d486cc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/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
+
+// constexpr iterator(iterator<!Const> i)
+// requires Const && convertible_to<ziperator<false>, ziperator<Const>>;
+
+#include <ranges>
+
+#include <cassert>
+
+#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_transform_view v(MakeTuple{}, 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_transform_view v(MakeTuple{}, 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.transform/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp
new file mode 100644
index 00000000000000..15d5e7d8c92832
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr iterator& operator--() requires bidirectional_range<Base>;
+// constexpr iterator operator--(int) requires bidirectional_range<Base>;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+template <class Iter>
+concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; };
+
+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_transform_view v(MakeTuple{}, a, b, std::views::iota(0, 5));
+ auto it = v.end();
+ using Iter = decltype(it);
+ static_assert(canDecrement<Iter>);
+
+ std::same_as<Iter&> decltype(auto) it_ref = --it;
+ assert(&it_ref == &it);
+
+ assert(*it == std::tuple(3, 4.3, 2));
+
+ auto original = it;
+ std::same_as<Iter> decltype(auto) it2 = it--;
+ assert(original == it2);
+ assert(*it == std::tuple(2, 3.2, 1));
+ }
+
+ {
+ // all bidi+
+ int buffer[2] = {1, 2};
+
+ std::ranges::zip_transform_view v(MakeTuple{}, BidiCommonView{buffer}, std::views::iota(0, 5));
+ auto it = v.begin();
+ using Iter = decltype(it);
+ static_assert(canDecrement<Iter>);
+
+ ++it;
+ ++it;
+
+ std::same_as<Iter&> decltype(auto) it_ref = --it;
+ assert(&it_ref == &it);
+
+ assert(it == ++v.begin());
+ assert(*it == std::tuple(2, 1));
+
+ auto original = it;
+ std::same_as<Iter> decltype(auto) it2 = it--;
+ assert(original == it2);
+ assert(*it == std::tuple(1, 0));
+ }
+
+ {
+ // non bidi
+ int buffer[3] = {4, 5, 6};
+ std::ranges::zip_transform_view v(MakeTuple{}, a, InputCommonView{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.transform/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/deref.pass.cpp
new file mode 100644
index 00000000000000..de8dde76982a6a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/deref.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// constexpr decltype(auto) operator*() const noexcept(see below);
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+// Test noexcept
+// Remarks: Let Is be the pack 0, 1, …, (sizeof...(Views)-1). The exception specification is equivalent to:
+// noexcept(invoke(*parent_->fun_, *std::get<Is>(inner_.current_)...)).
+
+template <class ZipTransformView>
+concept DerefNoexcept = requires(std::ranges::iterator_t<ZipTransformView> iter) { requires noexcept(*iter); };
+
+struct ThrowingDerefIter {
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = int;
+ using difference_type = std::intptr_t;
+
+ int operator*() const noexcept(false);
+
+ ThrowingDerefIter& operator++();
+ void operator++(int);
+
+ friend constexpr bool operator==(const ThrowingDerefIter&, const ThrowingDerefIter&) = default;
+};
+
+using NoexceptDerefIter = int*;
+
+template <bool NoExceptDeref>
+struct TestView : std::ranges::view_base {
+ using Iter = std::conditional_t<NoExceptDeref, NoexceptDerefIter, ThrowingDerefIter>;
+ Iter begin() const;
+ Iter end() const;
+};
+
+template <bool NoExceptCall>
+struct TestFn {
+ int operator()(auto&&...) const noexcept(NoExceptCall);
+};
+
+static_assert(DerefNoexcept<std::ranges::zip_transform_view<TestFn<true>, TestView<true>>>);
+static_assert(DerefNoexcept<std::ranges::zip_transform_view<TestFn<true>, TestView<true>, TestView<true>>>);
+static_assert(!DerefNoexcept<std::ranges::zip_transform_view<TestFn<true>, TestView<false>>>);
+static_assert(!DerefNoexcept<std::ranges::zip_transform_view<TestFn<false>, TestView<true>>>);
+static_assert(!DerefNoexcept<std::ranges::zip_transform_view<TestFn<false>, TestView<false>>>);
+static_assert(!DerefNoexcept<std::ranges::zip_transform_view<TestFn<false>, TestView<false>, TestView<true>>>);
+static_assert(!DerefNoexcept<std::ranges::zip_transform_view<TestFn<true>, TestView<false>, TestView<true>>>);
+static_assert(!DerefNoexcept<std::ranges::zip_transform_view<TestFn<false>, TestView<false>, TestView<false>>>);
+
+constexpr bool test() {
+ std::array a{1, 2, 3, 4};
+ std::array b{4.1, 3.2, 4.3};
+ {
+ // Function returns reference
+ std::ranges::zip_transform_view v(GetFirst{}, a);
+ auto it = v.begin();
+ std::same_as<int&> decltype(auto) val = *it;
+ assert(&val == &a[0]);
+ }
+
+ {
+ // function returns PRValue
+ std::ranges::zip_transform_view v(MakeTuple{}, a, b);
+ auto it = v.begin();
+ std::same_as<std::tuple<int, double>> decltype(auto) val = *it;
+ assert(val == std::tuple(1, 4.1));
+ }
+
+ {
+ // operator* is const
+ std::ranges::zip_transform_view v(GetFirst{}, a);
+ const auto it = v.begin();
+ std::same_as<int&> decltype(auto) val = *it;
+ assert(&val == &a[0]);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.pass.cpp
new file mode 100644
index 00000000000000..f4ba3a157d6dc3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.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
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+// constexpr iterator operator++(int) requires forward_range<Base>;;
+
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#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_)); }
+};
+
+template <class View>
+constexpr void testForwardPlus() {
+ int buffer[] = {1, 2, 3, 4};
+
+ std::ranges::zip_transform_view v(GetFirst{}, View{buffer}, View{buffer});
+ auto it = v.begin();
+ using Iter = decltype(it);
+
+ assert(&(*it) == &(buffer[0]));
+
+ std::same_as<Iter&> decltype(auto) it_ref = ++it;
+ assert(&it_ref == &it);
+ assert(&(*it) == &(buffer[1]));
+
+ static_assert(std::is_same_v<decltype(it++), Iter>);
+ auto original = it;
+ std::same_as<Iter> decltype(auto) copy = it++;
+ assert(original == copy);
+ assert(&(*it) == &(buffer[2]));
+}
+
+constexpr bool test() {
+ testForwardPlus<SizedRandomAccessView>();
+ testForwardPlus<BidiCommonView>();
+ testForwardPlus<ForwardSizedView>();
+
+ {
+ // test input_range
+ int buffer[3] = {4, 5, 6};
+ std::ranges::zip_transform_view v(MakeTuple{}, InputRange{buffer}, InputRange{buffer});
+ auto it = v.begin();
+ using Iter = decltype(it);
+
+ assert(*it == std::tuple(4, 4));
+
+ std::same_as<Iter&> decltype(auto) it_ref = ++it;
+ assert(&it_ref == &it);
+ assert(*it == std::tuple(5, 5));
+
+ static_assert(std::is_same_v<decltype(it++), void>);
+ it++;
+ assert(*it == std::tuple(6, 6));
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp
new file mode 100644
index 00000000000000..8976d7e0ded2a5
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Iterator traits and member typedefs in zip_transform_view::iterator.
+
+#include <array>
+#include <ranges>
+
+#include "test_iterators.h"
+
+#include "../types.h"
+
+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 difference_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 {};
+
+void test() {
+ int buffer[] = {1, 2, 3, 4};
+ {
+ // C++20 random_access C++17 random_access
+ std::ranges::zip_transform_view v(GetFirst{}, 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::random_access_iterator_tag>);
+ static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ static_assert(HasIterCategory<Iter>);
+ }
+
+ {
+ // C++20 random_access C++17 input
+ std::ranges::zip_transform_view v(MakeTuple{}, buffer);
+ using Iter = decltype(v.begin());
+
+ static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
+ static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+ static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+ static_assert(HasIterCategory<Iter>);
+ }
+
+ {
+ // C++20 bidirectional C++17 bidirectional
+ std::ranges::zip_transform_view v(GetFirst{}, 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::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ static_assert(HasIterCategory<Iter>);
+ }
+
+ {
+ // C++20 bidirectional C++17 input
+ std::ranges::zip_transform_view v(MakeTuple{}, 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::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+ static_assert(HasIterCategory<Iter>);
+ }
+
+ {
+ // C++20 forward C++17 bidirectional
+ std::ranges::zip_transform_view v(GetFirst{}, ForwardSizedView{buffer});
+ using Iter = decltype(v.begin());
+
+ static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<Iter::iterator_category, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ static_assert(HasIterCategory<Iter>);
+ }
+
+ {
+ // C++20 forward C++17 input
+ std::ranges::zip_transform_view v(MakeTuple{}, ForwardSizedView{buffer});
+ using Iter = decltype(v.begin());
+
+ static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
+ static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
+ static_assert(HasIterCategory<Iter>);
+ }
+
+ {
+ // C++20 input C++17 not a range
+ std::ranges::zip_transform_view v(GetFirst{}, InputCommonView{buffer});
+ using Iter = decltype(v.begin());
+
+ static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>);
+ static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ static_assert(!HasIterCategory<Iter>);
+ }
+
+ {
+ // difference_type of one view
+ std::ranges::zip_transform_view v{MakeTuple{}, DiffTypeRange<std::intptr_t>{}};
+ using Iter = decltype(v.begin());
+ static_assert(std::is_same_v<Iter::difference_type, std::intptr_t>);
+ }
+
+ {
+ // difference_type of multiple views should be the common type
+ std::ranges::zip_transform_view v{MakeTuple{}, DiffTypeRange<std::intptr_t>{}, DiffTypeRange<std::ptrdiff_t>{}};
+ using Iter = decltype(v.begin());
+ static_assert(std::is_same_v<Iter::difference_type, std::common_type_t<std::intptr_t, std::ptrdiff_t>>);
+ }
+
+ const std::array foos{Foo{}};
+ std::array bars{Bar{}, Bar{}};
+ {
+ // value_type of one view
+ std::ranges::zip_transform_view v{MakeTuple{}, foos};
+ using Iter = decltype(v.begin());
+ static_assert(std::is_same_v<Iter::value_type, std::tuple<Foo>>);
+ }
+
+ {
+ // value_type of multiple views with different value_type
+ std::ranges::zip_transform_view v{MakeTuple{}, foos, bars};
+ using Iter = decltype(v.begin());
+ static_assert(std::is_same_v<Iter::value_type, std::tuple<Foo, Bar>>);
+ }
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp
new file mode 100644
index 00000000000000..b624177b400243
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.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
+
+// constexpr decltype(auto) operator[](difference_type n) const
+// requires random_access_range<Base>;
+
+#include <ranges>
+#include <cassert>
+
+#include "../types.h"
+
+template <class T>
+concept CanSubscript = requires(T t) { t[0]; };
+
+constexpr bool test() {
+ int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+
+ {
+ // F returns PR value
+ std::ranges::zip_transform_view v(MakeTuple{}, SizedRandomAccessView{buffer}, std::views::iota(0));
+ auto it = v.begin();
+ using Iter = decltype(it);
+ static_assert(CanSubscript<Iter>);
+
+ std::same_as<std::tuple<int, int>> decltype(auto) val = it[0];
+ assert(val == *it);
+ assert(it[2] == *(it + 2));
+ assert(it[4] == *(it + 4));
+ }
+
+ {
+ // F return by reference
+ std::ranges::zip_transform_view v(GetFirst{}, ContiguousCommonView{buffer}, ContiguousCommonView{buffer});
+ auto it = v.begin();
+ using Iter = decltype(it);
+ static_assert(CanSubscript<Iter>);
+
+ std::same_as<int&> decltype(auto) val = it[0];
+ assert(&val == &buffer[0]);
+ assert(val == *it);
+ assert(it[2] == *(it + 2));
+ assert(it[4] == *(it + 4));
+ }
+
+ {
+ // non random_access_range
+ std::ranges::zip_transform_view v(GetFirst{}, BidiCommonView{buffer});
+ auto it = v.begin();
+ using Iter = decltype(it);
+ 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.transform/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..91c327a57ecf3d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/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
+
+// sentinel() = default;
+
+#include <cassert>
+#include <ranges>
+
+#include "../types.h"
+
+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_transform_view<Fn, 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.transform/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.other.pass.cpp
new file mode 100644
index 00000000000000..943359c29fecf1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/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
+
+// constexpr sentinel(sentinel<!Const> i)
+// requires Const && convertible_to<zentinel<false>, zentinel<Const>>;
+
+#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_);
+ }
+};
+
+// convertible_to<zentinel<false>, zentinel<Const>>
+static_assert(std::convertible_to< //
+ std::ranges::sentinel_t<std::ranges::zip_view<NonSimpleNonCommonConvertibleView>>,
+ std::ranges::sentinel_t<std::ranges::zip_view<NonSimpleNonCommonConvertibleView> const>>);
+
+constexpr bool test() {
+ int buffer1[4] = {1, 2, 3, 4};
+ int buffer2[5] = {1, 2, 3, 4, 5};
+ std::ranges::zip_transform_view v{
+ MakeTuple{}, NonSimpleNonCommonConvertibleView(buffer1), NonSimpleNonCommonConvertibleView(buffer2)};
+ using ZipTransformView = decltype(v);
+ static_assert(!std::ranges::common_range<ZipTransformView>);
+ auto sent1 = v.end();
+ std::ranges::sentinel_t<const ZipTransformView> 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.transform/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp
new file mode 100644
index 00000000000000..88f1a7d1089d03
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp
@@ -0,0 +1,142 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<bool OtherConst>
+// requires sentinel_for<zentinel<Const>, ziperator<OtherConst>>
+// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
+
+#include <cassert>
+#include <compare>
+#include <ranges>
+
+#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;
+};
+
+template <class Iter, class Sent>
+concept EqualComparable = std::invocable<std::equal_to<>, const Iter&, const Sent&>;
+
+constexpr bool test() {
+ int buffer1[4] = {1, 2, 3, 4};
+ int buffer2[5] = {1, 2, 3, 4, 5};
+ int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+ {
+ // const and non-const have different iterator/sentinel types
+ std::ranges::zip_transform_view v{
+ MakeTuple{}, NonSimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)};
+ using ZipTransformView = decltype(v);
+ static_assert(!std::ranges::common_range<ZipTransformView>);
+ static_assert(!simple_view<ZipTransformView>);
+
+ 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_transform_view v{MakeTuple{}, ComparableView(buffer1), ComparableView(buffer2)};
+ using ZipTransformView = decltype(v);
+ static_assert(!std::ranges::common_range<ZipTransformView>);
+ static_assert(!simple_view<ZipTransformView>);
+
+ 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_transform_view v{MakeTuple{}, ComparableView(buffer1), ConstIncompatibleView{}};
+ using ZipTransformView = decltype(v);
+ static_assert(!std::ranges::common_range<ZipTransformView>);
+ static_assert(!simple_view<ZipTransformView>);
+
+ 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.transform/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp
new file mode 100644
index 00000000000000..58c35e22db46ac
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp
@@ -0,0 +1,209 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<bool OtherConst>
+// requires sized_sentinel_for<zentinel<Const>, ziperator<OtherConst>>
+// friend constexpr range_difference_t<maybe-const<OtherConst, InnerView>>
+// operator-(const sentinel& x, const iterator<OtherConst>& y);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <ranges>
+#include <tuple>
+
+#include "../types.h"
+
+template <class Base = int*>
+struct convertible_forward_sized_iterator {
+ Base it_ = nullptr;
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = int;
+ using difference_type = std::intptr_t;
+
+ convertible_forward_sized_iterator() = default;
+ constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}
+
+ template <std::convertible_to<Base> U>
+ constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}
+
+ constexpr decltype(*Base{}) operator*() const { return *it_; }
+
+ constexpr convertible_forward_sized_iterator& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
+
+ friend constexpr bool
+ operator==(const convertible_forward_sized_iterator&, const convertible_forward_sized_iterator&) = default;
+
+ friend constexpr difference_type
+ operator-(const convertible_forward_sized_iterator& x, const convertible_forward_sized_iterator& y) {
+ return x.it_ - y.it_;
+ }
+};
+static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
+
+template <class Base>
+struct convertible_sized_sentinel {
+ Base base_;
+ explicit convertible_sized_sentinel() = default;
+ constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
+
+ template <std::convertible_to<Base> U>
+ constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
+
+ template <class U>
+ requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+ friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
+ return s.base_ == base;
+ }
+ template <class U>
+ requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+ friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
+ return s.base_ - i;
+ }
+
+ template <class U>
+ requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
+ friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
+ return i - s.base_;
+ }
+};
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
+ convertible_forward_sized_iterator<>>);
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
+ convertible_forward_sized_iterator<int*>>);
+static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
+ convertible_forward_sized_iterator<const int*>>);
+
+struct ConstCompatibleForwardSized : IntBufferView {
+ using IntBufferView::IntBufferView;
+
+ using iterator = convertible_forward_sized_iterator<int*>;
+ using const_iterator = convertible_forward_sized_iterator<const int*>;
+
+ constexpr iterator begin() { return {buffer_}; }
+ constexpr const_iterator begin() const { return {buffer_}; }
+ constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
+ constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
+};
+
+template <class T, class U>
+concept HasMinus = std::invocable<std::minus<>, const T&, const U&>;
+
+template <class T>
+concept SentinelHasMinus = HasMinus<std::ranges::sentinel_t<T>, std::ranges::iterator_t<T>>;
+
+constexpr bool test() {
+ int buffer1[5] = {1, 2, 3, 4, 5};
+
+ {
+ // shortest range
+ std::ranges::zip_transform_view v(MakeTuple{}, 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_transform_view v(MakeTuple{}, std::views::iota(0), SizedRandomAccessView(buffer1));
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!SentinelHasMinus<decltype(v)>);
+ }
+
+ {
+ // const incompatible:
+ // underlying const sentinels cannot subtract underlying iterators
+ // underlying sentinels cannot subtract underlying const iterators
+ std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleForwardSizedNonCommon{buffer1});
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!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_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1});
+ static_assert(!std::ranges::common_range<decltype(v)>);
+ static_assert(!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.transform/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp
index c973f7cc4fcbc3..bbe469f2e3e0d9 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp
@@ -14,8 +14,6 @@
#include <ranges>
#include <cassert>
-#include <tuple>
-#include <utility>
#include "test_iterators.h"
#include "types.h"
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h
index 02ce0131a78fc5..0184d78873b1e6 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h
@@ -26,13 +26,17 @@ struct View : std::ranges::view_base {
};
struct Fn {
- int operator()(auto&&...) const;
+ int operator()(auto&&...) const { return 5; }
};
struct MakeTuple {
constexpr auto operator()(auto&&... args) const { return std::tuple(std::forward<decltype(args)>(args)...); }
};
+struct GetFirst {
+ constexpr decltype(auto) operator()(auto&& first, auto&&...) const { return std::forward<decltype(first)>(first); }
+};
+
struct NoConstBeginView : std::ranges::view_base {
int* begin();
int* end();
@@ -50,7 +54,6 @@ struct NonConstOnlyFn {
int operator()(const int&) const = delete;
};
-
template <class T>
struct BufferView : std::ranges::view_base {
T* buffer_;
More information about the libcxx-commits
mailing list