diff --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv --- a/libcxx/docs/Status/ZipProjects.csv +++ b/libcxx/docs/Status/ZipProjects.csv @@ -7,10 +7,10 @@ | `[allocator.uses.construction] `_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started| | `[vector.bool] `_, "[vector::reference] add const operator= overload", None, Nikolas Klauser, |Not Started| | `[iterator.concept.winc] `_, "Update weakly_comparable", None, Unassigned, |Not Started| -| `[range.zip] `_, "zip_view", "| `zip_view::iterator` -| `zip_view::sentinel`", Unassigned, |Not Started| -| `[range.zip.iterator] `_, "zip_view::iterator", None, Unassigned, |Not Started| -| `[range.zip.sentinel] `_, "zip_view::sentinel", None, Unassigned, |Not Started| +| `[range.zip] `_, "`zip_view `_", "| `zip_view::iterator` +| `zip_view::sentinel`", Hui Xie, |Complete| +| `[range.zip.iterator] `_, "`zip_view::iterator `_", None, Hui Xie, |Complete| +| `[range.zip.sentinel] `_, "`zip_view::sentinel `_", None, Hui Xie, |Complete| | `[range.zip.transform.view] `_, "zip_transform_view", "| `zip_transform_view::iterator` | `zip_transform_view::sentinel`", Unassigned, |Not Started| | `[range.zip.transform.iterator] `_, "zip_transform_view::iterator", None, Unassigned, |Not Started| diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -396,6 +396,7 @@ __ranges/transform_view.h __ranges/view_interface.h __ranges/views.h + __ranges/zip_view.h __split_buffer __std_stream __string diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/zip_view.h @@ -0,0 +1,511 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCPP___RANGES_ZIP_VIEW_H +#define _LIBCPP___RANGES_ZIP_VIEW_H + +#include <__config> + +#include <__algorithm/ranges_min.h> +#include <__compare/three_way_comparable.h> +#include <__concepts/convertible_to.h> +#include <__concepts/equality_comparable.h> +#include <__functional/invoke.h> +#include <__functional/operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty_view.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__utility/forward.h> +#include <__utility/integer_sequence.h> +#include <__utility/move.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +namespace ranges { + +template +concept __zip_is_common = (sizeof...(_Ranges) == 1 && (common_range<_Ranges> && ...)) || + (!(bidirectional_range<_Ranges> && ...) && (common_range<_Ranges> && ...)) || + ((random_access_range<_Ranges> && ...) && (sized_range<_Ranges> && ...)); + +template +auto __tuple_or_pair_test() -> pair<_Tp, _Up>; + +template + requires(sizeof...(_Types) != 2) +auto __tuple_or_pair_test() -> tuple<_Types...>; + +template +using __tuple_or_pair = decltype(__tuple_or_pair_test<_Types...>()); + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) { + return std::apply( + [&](_Types&&... __elements) { + return __tuple_or_pair...>( + std::invoke(__f, std::forward<_Types>(__elements))...); + }, + std::forward<_Tuple>(__tuple)); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_for_each(_Fun&& __f, _Tuple&& __tuple) { + std::apply( + [&](_Types&&... __elements) { (std::invoke(__f, std::forward<_Types>(__elements)), ...); }, + std::forward<_Tuple>(__tuple)); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr __tuple_or_pair< + invoke_result_t<_Fun&, typename tuple_element<_Indices, remove_cvref_t<_Tuple1>>::type, + typename tuple_element<_Indices, remove_cvref_t<_Tuple2>>::type>...> +__tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2, index_sequence<_Indices...>) { + return {std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)), + std::get<_Indices>(std::forward<_Tuple2>(__tuple2)))...}; +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) { + return ranges::__tuple_zip_transform(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2), + std::make_index_sequence>::value>()); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2, + index_sequence<_Indices...>) { + (std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)), + std::get<_Indices>(std::forward<_Tuple2>(__tuple2))), + ...); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) { + return ranges::__tuple_zip_for_each(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2), + std::make_index_sequence>::value>()); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr bool __tuple_any_equals(const _Tuple1& __tuple1, const _Tuple2& __tuple2) { + const auto __equals = ranges::__tuple_zip_transform(std::equal_to<>(), __tuple1, __tuple2); + return std::apply([](auto... __bools) { return (__bools || ...); }, __equals); +} + +// abs in cstdlib is not constexpr +// TODO : remove __abs once P0533R9 is implemented. +template +_LIBCPP_HIDE_FROM_ABI constexpr _Tp __abs(_Tp __t) { + return __t < 0 ? -__t : __t; +} + +template + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) +class zip_view : public view_interface> { + + _LIBCPP_NO_UNIQUE_ADDRESS tuple<_Views...> __views_; + + template + class __iterator; + + template + class __sentinel; + +public: + _LIBCPP_HIDE_FROM_ABI + zip_view() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() + requires(!(__simple_view<_Views> && ...)) { + return __iterator(ranges::__tuple_transform(ranges::begin, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const + requires(range && ...) { + return __iterator(ranges::__tuple_transform(ranges::begin, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() + requires(!(__simple_view<_Views> && ...)) { + if constexpr (!__zip_is_common<_Views...>) { + return __sentinel(ranges::__tuple_transform(ranges::end, __views_)); + } else if constexpr ((random_access_range<_Views> && ...)) { + return begin() + iter_difference_t<__iterator>(size()); + } else { + return __iterator(ranges::__tuple_transform(ranges::end, __views_)); + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const + requires(range && ...) { + if constexpr (!__zip_is_common) { + return __sentinel(ranges::__tuple_transform(ranges::end, __views_)); + } else if constexpr ((random_access_range && ...)) { + return begin() + iter_difference_t<__iterator>(size()); + } else { + return __iterator(ranges::__tuple_transform(ranges::end, __views_)); + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto size() + requires(sized_range<_Views> && ...) { + return std::apply( + [](auto... __sizes) { + using _CT = make_unsigned_t>; + return ranges::min({_CT(__sizes)...}); + }, + ranges::__tuple_transform(ranges::size, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto size() const + requires(sized_range && ...) { + return std::apply( + [](auto... __sizes) { + using _CT = make_unsigned_t>; + return ranges::min({_CT(__sizes)...}); + }, + ranges::__tuple_transform(ranges::size, __views_)); + } +}; + +template +zip_view(_Ranges&&...) -> zip_view...>; + +template +concept __zip_all_random_access = (random_access_range<__maybe_const<_Const, _Views>> && ...); + +template +concept __zip_all_bidirectional = (bidirectional_range<__maybe_const<_Const, _Views>> && ...); + +template +concept __zip_all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...); + +template +consteval auto __get_zip_view_iterator_tag() { + if constexpr (__zip_all_random_access<_Const, _Views...>) { + return random_access_iterator_tag(); + } else if constexpr (__zip_all_bidirectional<_Const, _Views...>) { + return bidirectional_iterator_tag(); + } else if constexpr (__zip_all_forward<_Const, _Views...>) { + return forward_iterator_tag(); + } else { + return input_iterator_tag(); + } +} + +template +struct __zip_view_iterator_category_base {}; + +template + requires __zip_all_forward<_Const, _Views...> +struct __zip_view_iterator_category_base<_Const, _Views...> { + using iterator_category = input_iterator_tag; +}; + +template + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) +template +class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base<_Const, _Views...> { + + __tuple_or_pair>...> __current_; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __iterator(__tuple_or_pair>...> __current) + : __current_(std::move(__current)) {} + + template + friend class zip_view<_Views...>::__iterator; + + template + friend class zip_view<_Views...>::__sentinel; + + friend class zip_view<_Views...>; + +public: + using iterator_concept = decltype(__get_zip_view_iterator_tag<_Const, _Views...>()); + using value_type = __tuple_or_pair>...>; + using difference_type = common_type_t>...>; + + _LIBCPP_HIDE_FROM_ABI + __iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator(__iterator __i) + requires _Const && (convertible_to, iterator_t<__maybe_const<_Const, _Views>>> && ...) + : __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator*() const { + return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator++() { + ranges::__tuple_for_each([](auto& __i) { ++__i; }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator operator++(int) + requires __zip_all_forward<_Const, _Views...> { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator--() + requires __zip_all_bidirectional<_Const, _Views...> { + ranges::__tuple_for_each([](auto& __i) { --__i; }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator operator--(int) + requires __zip_all_bidirectional<_Const, _Views...> { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator+=(difference_type __x) + requires __zip_all_random_access<_Const, _Views...> { + ranges::__tuple_for_each([&](_Iter& __i) { __i += iter_difference_t<_Iter>(__x); }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator-=(difference_type __x) + requires __zip_all_random_access<_Const, _Views...> { + ranges::__tuple_for_each([&](_Iter& __i) { __i -= iter_difference_t<_Iter>(__x); }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator[](difference_type __n) const + requires __zip_all_random_access<_Const, _Views...> { + return ranges::__tuple_transform( + [&](_Iter& __i) -> decltype(auto) { return __i[iter_difference_t<_Iter>(__n)]; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) + requires(equality_comparable>> && ...) { + if constexpr (__zip_all_bidirectional<_Const, _Views...>) { + return __x.__current_ == __y.__current_; + } else { + return ranges::__tuple_any_equals(__x.__current_, __y.__current_); + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> && + (three_way_comparable>> && ...) { + return __x.__current_ <=> __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr __iterator operator+(const __iterator& __i, difference_type __n) + requires __zip_all_random_access<_Const, _Views...> { + auto __r = __i; + __r += __n; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires __zip_all_random_access<_Const, _Views...> { + return __i + __n; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr __iterator operator-(const __iterator& __i, difference_type __n) + requires __zip_all_random_access<_Const, _Views...> { + auto __r = __i; + __r -= __n; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires(sized_sentinel_for>, iterator_t<__maybe_const<_Const, _Views>>> && + ...) { + const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __x.__current_, __y.__current_); + return std::apply( + [](auto... __ds) { + return ranges::min({difference_type(__ds)...}, + [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); }); + }, + __diffs); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr auto iter_move(const __iterator& __i) noexcept( + (noexcept(ranges::iter_move(declval>&>())) && ...) && + (is_nothrow_move_constructible_v>> && ...)) { + return ranges::__tuple_transform(ranges::iter_move, __i.__current_); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept( + (noexcept(ranges::iter_swap(declval>&>(), + declval>&>())) && + ...)) + requires(indirectly_swappable>> && ...) { + ranges::__tuple_zip_for_each(ranges::iter_swap, __l.__current_, __r.__current_); + } +}; + +template + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) +template +class zip_view<_Views...>::__sentinel { + + __tuple_or_pair>...> __end_; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __sentinel(__tuple_or_pair>...> __end) : __end_(__end) {} + + friend class zip_view<_Views...>; + + // hidden friend cannot access private member of iterator because they are friends of friends + template + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) + __iter_current(zip_view<_Views...>::__iterator<_OtherConst> const& __it) { + return (__it.__current_); + } + +public: + _LIBCPP_HIDE_FROM_ABI + __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr __sentinel(__sentinel __i) + requires _Const && (convertible_to, sentinel_t<__maybe_const<_Const, _Views>>> && ...) + : __end_(std::move(__i.__end_)) {} + + template + requires(sentinel_for>, iterator_t<__maybe_const<_OtherConst, _Views>>> && + ...) + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return ranges::__tuple_any_equals(__iter_current(__x), __y.__end_); + } + + template + requires( + sized_sentinel_for>, iterator_t<__maybe_const<_OtherConst, _Views>>> && + ...) + _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t>...> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_); + return std::apply( + [](auto... __ds) { + using _Diff = common_type_t>...>; + return ranges::min({_Diff(__ds)...}, + [](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); }); + }, + __diffs); + } + + template + requires( + sized_sentinel_for>, iterator_t<__maybe_const<_OtherConst, _Views>>> && + ...) + _LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t>...> + operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) { + return -(__x - __y); + } +}; + +template +inline constexpr bool enable_borrowed_range> = (enable_borrowed_range<_Views> && ...); + +namespace views { +namespace __zip { + +struct __fn { + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()() const noexcept { return empty_view>{}; } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Ranges&&... rs) const + noexcept(noexcept(zip_view...>(std::forward<_Ranges>(rs)...))) + -> decltype(zip_view...>(std::forward<_Ranges>(rs)...)) { + return zip_view...>(std::forward<_Ranges>(rs)...); + } +}; + +} // namespace __zip +inline namespace __cpo { + inline constexpr auto zip = __zip::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ZIP_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -878,6 +878,7 @@ } module view_interface { private header "__ranges/view_interface.h" } module views { private header "__ranges/views.h" } + module zip_view { private header "__ranges/zip_view.h" } } } module ratio { diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -227,6 +227,17 @@ namespace views { inline constexpr unspecified lazy_split = unspecified; } + + // [range.zip], zip view + template + requires (view && ...) && (sizeof...(Views) > 0) + class zip_view; // C++2b + + template + inline constexpr bool enable_borrowed_range> = // C++2b + (enable_borrowed_range && ...); + + namespace views { inline constexpr unspecified zip = unspecified; } // C++2b } namespace std { @@ -290,6 +301,7 @@ #include <__ranges/transform_view.h> #include <__ranges/view_interface.h> #include <__ranges/views.h> +#include <__ranges/zip_view.h> #include <__tuple> // TODO: has to export std::tuple_size. Replace this, once is granularized. #include // Required by the standard. #include // Required by the standard. diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -427,6 +427,7 @@ #include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}} #include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}} #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}} +#include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}} #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}} #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}} #include <__string> // expected-error@*:* {{use of private header from outside its module: '__string'}} diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp --- a/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/ctor.default.pass.cpp @@ -34,8 +34,8 @@ constexpr int operator()(int i) const { return i + state_; } }; -struct NoDefaultView : std::ranges::view_base { - NoDefaultView() = delete; +struct NoDefaultCtrView : std::ranges::view_base { + NoDefaultCtrView() = delete; int* begin() const; int* end() const; }; @@ -62,9 +62,9 @@ assert(view[2] == 103); } - static_assert(!std::is_default_constructible_v>); + static_assert(!std::is_default_constructible_v>); static_assert(!std::is_default_constructible_v>); - static_assert(!std::is_default_constructible_v>); + static_assert(!std::is_default_constructible_v>); return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto begin() requires (!(simple-view && ...)); +// constexpr auto begin() const requires (range && ...); + +#include + +#include +#include +#include +#include + +#include "types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = + HasConstBegin && + requires(T& t, const T& ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstBegin = HasBegin && ! +HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && ! +HasConstAndNonConstBegin; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + // all underlying iterators should be at the begin position + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + std::same_as> decltype(auto) val = *v.begin(); + assert(val == std::make_tuple(1, 0, 2.0)); + assert(&(std::get<0>(val)) == &buffer[0]); + } + + { + // with empty range + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + } + + { + // underlying ranges all model simple-view + std::ranges::zip_view v(SimpleCommon{buffer}, SimpleCommon{buffer}); + static_assert(std::is_same_v); + assert(v.begin() == std::as_const(v).begin()); + auto [x, y] = *std::as_const(v).begin(); + assert(&x == &buffer[0]); + assert(&y == &buffer[0]); + + using View = decltype(v); + static_assert(HasOnlyConstBegin); + static_assert(!HasOnlyNonConstBegin); + static_assert(!HasConstAndNonConstBegin); + } + + { + // not all underlying ranges model simple-view + std::ranges::zip_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer}); + static_assert(!std::is_same_v); + assert(v.begin() == std::as_const(v).begin()); + auto [x, y] = *std::as_const(v).begin(); + assert(&x == &buffer[0]); + assert(&y == &buffer[0]); + + using View = decltype(v); + static_assert(!HasOnlyConstBegin); + static_assert(!HasOnlyNonConstBegin); + static_assert(HasConstAndNonConstBegin); + } + + { + // underlying const R is not a range + using View = std::ranges::zip_view; + static_assert(!HasOnlyConstBegin); + static_assert(HasOnlyNonConstBegin); + static_assert(!HasConstAndNonConstBegin); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// inline constexpr bool enable_borrowed_range> = +// (enable_borrowed_range && ...); + +#include +#include + +struct Borrowed : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(std::ranges::borrowed_range); + +struct NonBorrowed : std::ranges::view_base { + int* begin() const; + int* end() const; +}; +static_assert(!std::ranges::borrowed_range); + +// test borrowed_range +static_assert(std::ranges::borrowed_range>); +static_assert(std::ranges::borrowed_range>); +static_assert(!std::ranges::borrowed_range>); +static_assert(!std::ranges::borrowed_range>); +static_assert(!std::ranges::borrowed_range>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::zip + +#include + +#include +#include +#include +#include +#include + +#include "types.h" + +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert( + std::is_invocable_v>); +static_assert(!std::is_invocable_v); + +constexpr bool test() { + { + // zip zero arguments + auto v = std::views::zip(); + assert(std::ranges::empty(v)); + static_assert(std::is_same_v>>); + } + + { + // zip a view + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + std::same_as> decltype(auto) v = + std::views::zip(SizedRandomAccessView{buffer}); + assert(std::ranges::size(v) == 8); + static_assert(std::is_same_v, std::tuple>); + } + + { + // zip a viewable range + std::array a{1, 2, 3}; + std::same_as>>> decltype(auto) v = + std::views::zip(a); + assert(&(std::get<0>(*v.begin())) == &(a[0])); + static_assert(std::is_same_v, std::tuple>); + } + + { + // zip the zip_view + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + std::same_as> decltype(auto) v = + std::views::zip(SizedRandomAccessView{buffer}, SizedRandomAccessView{buffer}); + + std::same_as< + std::ranges::zip_view>> decltype(auto) v2 = + std::views::zip(v); + + static_assert(std::is_same_v, std::tuple>>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctad.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// zip_view(Rs&&...) -> zip_view...>; + +#include +#include +#include + +struct Container { + int* begin() const; + int* end() const; +}; + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +void testCTAD() { + static_assert(std::is_same_v>>); + + static_assert(std::is_same_v, View>>); + + Container c{}; + static_assert(std::is_same_v< + decltype(std::ranges::zip_view(Container{}, View{}, c)), + std::ranges::zip_view, View, std::ranges::ref_view>>); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.default.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// zip_view() = default; + +#include + +#include +#include +#include + +constexpr int buff[] = {1, 2, 3}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NoDefaultCtrView : std::ranges::view_base { + NoDefaultCtrView() = delete; + int* begin() const; + int* end() const; +}; + +// The default constructor requires all underlying views to be default constructible. +// It is implicitly required by the tuple's constructor. If any of the iterators are +// not default constructible, zip iterator's =default would be implicitly deleted. +static_assert(std::is_default_constructible_v>); +static_assert( + std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); + +constexpr bool test() { + { + using View = std::ranges::zip_view; + View v = View(); // the default constructor is not explicit + assert(v.size() == 3); + auto it = v.begin(); + using Pair = std::pair; + assert(*it++ == Pair(buff[0], buff[0])); + assert(*it++ == Pair(buff[1], buff[1])); + assert(*it == Pair(buff[2], buff[2])); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/ctor.views.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr explicit zip_view(Views...) + +#include +#include + +#include "types.h" + +template +void conversion_test(T); + +template +concept implicitly_constructible_from = requires(Args&&... args) { conversion_test({std::move(args)...}); }; + +// test constructor is explicit +static_assert(std::constructible_from, SimpleCommon>); +static_assert(!implicitly_constructible_from, SimpleCommon>); + +static_assert(std::constructible_from, SimpleCommon, SimpleCommon>); +static_assert( + !implicitly_constructible_from, SimpleCommon, SimpleCommon>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +template +constexpr void constructorTest(auto&& buffer1, auto&& buffer2) { + std::ranges::zip_view v{View1{buffer1}, View2{buffer2}}; + auto [i, j] = *v.begin(); + assert(i == buffer1[0]); + assert(j == buffer2[0]); +}; + +constexpr bool test() { + + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[4] = {9, 8, 7, 6}; + + { + // constructor from views + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + auto [i, j, k] = *v.begin(); + assert(i == 1); + assert(j == 0); + assert(k == 2.0); + } + + { + // arguments are moved once + MoveAwareView mv; + std::ranges::zip_view v{std::move(mv), MoveAwareView{}}; + auto [numMoves1, numMoves2] = *v.begin(); + assert(numMoves1 == 2); // one move from the local variable to parameter, one move from parameter to member + assert(numMoves2 == 1); + } + + // input and forward + { + constructorTest(buffer, buffer2); + } + + // bidi and random_access + { + constructorTest(buffer, buffer2); + } + + // contiguous + { + constructorTest(buffer, buffer2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp @@ -0,0 +1,396 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() requires(!(simple-view && ...)) +// constexpr auto end() const requires(range&&...) + +#include +#include + +#include "types.h" + +// ID | simple | common | bidi | random | sized | #views | v.end() | as_const(v) +// | | | | access | | | | .end() +// ---|--------|--------|------|--------|-------|--------|----------------|--------------- +// 1 | Y | Y | Y | Y | Y | 1 | iterator | iterator +// 2 | Y | Y | Y | Y | Y | >1 | iterator | iterator +// 3 | Y | N | Y | Y | N | 1 | sentinel | sentinel +// 4 | Y | N | Y | Y | N | >1 | sentinel | sentinel +// 5 | Y | Y | Y | N | Y | 1 | iterator | iterator +// 6 | Y | Y | Y | N | Y | >1 | sentinel | sentinel +// 7 | Y | Y | Y | N | N | 1 | iterator | iterator +// 8 | Y | Y | Y | N | N | >1 | sentinel | sentinel +// 9 | Y | Y | N | N | Y | 1 | iterator | iterator +// 10 | Y | Y | N | N | Y | >1 | iterator | iterator +// 11 | Y | Y | N | N | N | 1 | iterator | iterator +// 12 | Y | Y | N | N | N | >1 | iterator | iterator +// 13 | Y | N | Y | Y | Y | 1 | iterator | iterator +// 14 | Y | N | Y | Y | Y | >1 | iterator | iterator +// 15 | Y | N | Y | N | Y | 1 | sentinel | sentinel +// 16 | Y | N | Y | N | Y | >1 | sentinel | sentinel +// 17 | Y | N | Y | N | N | 1 | sentinel | sentinel +// 18 | Y | N | Y | N | N | >1 | sentinel | sentinel +// 19 | Y | N | N | N | Y | 1 | sentinel | sentinel +// 20 | Y | N | N | N | Y | >1 | sentinel | sentinel +// 21 | Y | N | N | N | N | 1 | sentinel | sentinel +// 22 | Y | N | N | N | N | >1 | sentinel | sentinel +// 23 | N | Y | Y | Y | Y | 1 | iterator| iterator +// 24 | N | Y | Y | Y | Y | >1 | iterator| iterator +// 25 | N | N | Y | Y | N | 1 | sentinel| sentinel +// 26 | N | N | Y | Y | N | >1 | sentinel| sentinel +// 27 | N | Y | Y | N | Y | 1 | iterator| iterator +// 28 | N | Y | Y | N | Y | >1 | sentinel| sentinel +// 29 | N | Y | Y | N | N | 1 | iterator| iterator +// 30 | N | Y | Y | N | N | >1 | sentinel| sentinel +// 31 | N | Y | N | N | Y | 1 | iterator| iterator +// 32 | N | Y | N | N | Y | >1 | iterator| iterator +// 33 | N | Y | N | N | N | 1 | iterator| iterator +// 34 | N | Y | N | N | N | >1 | iterator| iterator +// 35 | N | N | Y | Y | Y | 1 | iterator| iterator +// 36 | N | N | Y | Y | Y | >1 | iterator| iterator +// 37 | N | N | Y | N | Y | 1 | sentinel| sentinel +// 38 | N | N | Y | N | Y | >1 | sentinel| sentinel +// 39 | N | N | Y | N | N | 1 | sentinel| sentinel +// 40 | N | N | Y | N | N | >1 | sentinel| sentinel +// 41 | N | N | N | N | Y | 1 | sentinel| sentinel +// 42 | N | N | N | N | Y | >1 | sentinel| sentinel +// 43 | N | N | N | N | N | 1 | sentinel| sentinel +// 44 | N | N | N | N | N | >1 | sentinel| sentinel + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[1] = {1}; + int buffer3[3] = {1, 2, 3}; + { + // test ID 1 + std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 5 == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 2 + std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1), SimpleCommonRandomAccessSized(buffer2)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 1 == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 3 + std::ranges::zip_view v{NonSizedRandomAccessView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(v.begin() + 5 == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 4 + std::ranges::zip_view v{NonSizedRandomAccessView(buffer1), NonSizedRandomAccessView(buffer3)}; + static_assert(!std::ranges::common_range); + assert(v.begin() + 3 == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 5 + std::ranges::zip_view v{SizedBidiCommon(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 6 + std::ranges::zip_view v{SizedBidiCommon(buffer1), SizedBidiCommon(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 7 + std::ranges::zip_view v{BidiCommonView(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 8 + std::ranges::zip_view v{BidiCommonView(buffer1), BidiCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 9 + std::ranges::zip_view v{ForwardSizedView(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 10 + std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)}; + static_assert(std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 11 + std::ranges::zip_view v{InputCommonView(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 12 + std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)}; + static_assert(std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 13 + std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 5 == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 14 + std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1), SimpleNonCommonRandomAcessSized(buffer2)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 1 == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 15 + std::ranges::zip_view v{SizedBidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 16 + std::ranges::zip_view v{SizedBidiNonCommonView(buffer1), SizedBidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 17 + std::ranges::zip_view v{BidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 18 + std::ranges::zip_view v{BidiNonCommonView(buffer1), BidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 19 + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 20 + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1), ForwardSizedNonCommon(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 21 + std::ranges::zip_view v{InputNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 22 + std::ranges::zip_view v{InputNonCommonView(buffer1), InputNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::is_same_v); + } + { + // test ID 23 + std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 5 == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 24 + std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1), NonSimpleCommonRandomAccessSized(buffer2)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 1 == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 25 + std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(v.begin() + 5 == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 26 + std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1), NonSimpleNonSizedRandomAccessView(buffer3)}; + static_assert(!std::ranges::common_range); + assert(v.begin() + 3 == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 27 + std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 28 + std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1), NonSimpleSizedBidiCommon(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 29 + std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 30 + std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1), NonSimpleBidiCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 31 + std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 32 + std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1), NonSimpleForwardSizedView(buffer2)}; + static_assert(std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 33 + std::ranges::zip_view v{NonSimpleInputCommonView(buffer1)}; + static_assert(std::ranges::common_range); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 34 + std::ranges::zip_view v{NonSimpleInputCommonView(buffer1), NonSimpleInputCommonView(buffer2)}; + static_assert(std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 35 + std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 5 == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 36 + std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1), NonSimpleNonCommonRandomAcessSized(buffer2)}; + static_assert(std::ranges::common_range); + assert(v.begin() + 1 == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 37 + std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 38 + std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1), NonSimpleSizedBidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 39 + std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 40 + std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1), NonSimpleBidiNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 41 + std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 42 + std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1), NonSimpleForwardSizedNonCommon(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 43 + std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1)}; + static_assert(!std::ranges::common_range); + assert(std::ranges::next(v.begin(), 5) == v.end()); + static_assert(!std::is_same_v); + } + { + // test ID 44 + std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1), NonSimpleInputNonCommonView(buffer2)}; + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::is_same_v); + } + { + // end should go to the minimum length when zip is common and random_access sized + std::ranges::zip_view v(std::views::iota(0, 4), std::views::iota(0, 8)); + auto it = --(v.end()); + auto [x, y] = *it; + assert(x == 3); + assert(y == 3); // y should not go to the end "7" + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/general.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Some basic examples of how zip_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include + +#include +#include +#include +#include + +int main(int, char**) { + { + std::ranges::zip_view v{ + std::array{1, 2}, + std::vector{4, 5, 6}, + std::array{7}, + }; + assert(std::ranges::size(v) == 1); + assert(*v.begin() == std::make_tuple(1, 4, 7)); + } + { + using namespace std::string_literals; + std::vector v{1, 2, 3, 4}; + std::array a{"abc"s, "def"s, "gh"s}; + auto view = std::views::zip(v, a); + auto it = view.begin(); + assert(&(std::get<0>(*it)) == &(v[0])); + assert(&(std::get<1>(*it)) == &(a[0])); + + ++it; + assert(&(std::get<0>(*it)) == &(v[1])); + assert(&(std::get<1>(*it)) == &(a[1])); + + ++it; + assert(&(std::get<0>(*it)) == &(v[2])); + assert(&(std::get<1>(*it)) == &(a[2])); + + ++it; + assert(it == view.end()); + } + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/arithmetic.pass.cpp @@ -0,0 +1,143 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// x += n; +// x + n; +// n + x; +// x -= n; +// x - n; +// x - y; +// All the arithmetic operators have the constraint `requires all-random-access;`, +// except `operator-(x, y)` which instead has the constraint +// `requires (sized_­sentinel_­for>, +// iterator_t>> && ...);` + +#include + +#include +#include +#include + +#include "../types.h" + +template +concept canPlusEqual = requires(T& t, U& u) { t += u; }; + +template +concept canMinusEqual = requires(T& t, U& u) { t -= u; }; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + SizedRandomAccessView a{buffer1}; + static_assert(std::ranges::random_access_range); + std::array b{4.1, 3.2, 4.3, 0.1, 0.2}; + static_assert(std::ranges::contiguous_range); + { + // operator+(x, n) and operator+= + std::ranges::zip_view v(a, b); + auto it1 = v.begin(); + + auto it2 = it1 + 3; + auto [x2, y2] = *it2; + assert(&x2 == &(a[3])); + assert(&y2 == &(b[3])); + + auto it3 = 3 + it1; + auto [x3, y3] = *it3; + assert(&x3 == &(a[3])); + assert(&y3 == &(b[3])); + + it1 += 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[3])); + assert(&y1 == &(b[3])); + + using Iter = decltype(it1); + static_assert(canPlusEqual); + } + + { + // operator-(x, n) and operator-= + std::ranges::zip_view v(a, b); + auto it1 = v.end(); + + auto it2 = it1 - 3; + auto [x2, y2] = *it2; + assert(&x2 == &(a[2])); + assert(&y2 == &(b[2])); + + it1 -= 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[2])); + assert(&y1 == &(b[2])); + + using Iter = decltype(it1); + static_assert(canMinusEqual); + } + + { + // operator-(x, y) + std::ranges::zip_view v(a, b); + assert((v.end() - v.begin()) == 5); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + assert((it1 - it2) == -2); + } + + { + // in this case sentinel is computed by getting each of the underlying sentinels, so the distance + // between begin and end for each of the underlying iterators can be different + std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)}; + using View = decltype(v); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::random_access_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + // it1 : + // it2 : + assert((it1 - it2) == -5); + assert((it2 - it1) == 5); + } + + { + // One of the ranges is not random access + std::ranges::zip_view v(a, b, ForwardSizedView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!std::invocable, Iter, intptr_t>); + static_assert(!std::invocable, intptr_t, Iter>); + static_assert(!canPlusEqual); + static_assert(!std::invocable, Iter, intptr_t>); + static_assert(std::invocable, Iter, Iter>); + static_assert(!canMinusEqual); + } + + { + // One of the ranges does not have sized sentinel + std::ranges::zip_view v(a, b, InputCommonView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!std::invocable, Iter, Iter>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/compare.pass.cpp @@ -0,0 +1,250 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr bool operator==(const iterator& x, const iterator& y) +// requires (equality_­comparable>> && ...); +// friend constexpr bool operator<(const iterator& x, const iterator& y) +// requires all-random-access; +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// requires all-random-access; +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// requires all-random-access; +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// requires all-random-access; +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires all-random-access && +// (three_­way_­comparable>> && ...); + +#include +#include + +#include "test_iterators.h" +#include "../types.h" + +// This is for testing that zip iterator never calls underlying iterator's >, >=, <=, !=. +// The spec indicates that zip iterator's >= is negating zip iterator's < instead of calling underlying iterator's >=. +// Declare all the operations >, >=, <= etc to make it satisfy random_access_iterator concept, +// but not define them. If the zip iterator's >,>=, <=, etc isn't implemented in the way defined by the standard +// but instead calling underlying iterator's >,>=,<=, we will get a linker error for the runtime tests and +// non-constant expression for the compile time tests. +struct LessThanIterator { + int* it_ = nullptr; + LessThanIterator() = default; + constexpr LessThanIterator(int* it) : it_(it) {} + + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int& operator*() const { return *it_; } + constexpr int& operator[](difference_type n) const { return it_[n]; } + constexpr LessThanIterator& operator++() { + ++it_; + return *this; + } + constexpr LessThanIterator& operator--() { + --it_; + return *this; + } + constexpr LessThanIterator operator++(int) { return LessThanIterator(it_++); } + constexpr LessThanIterator operator--(int) { return LessThanIterator(it_--); } + + constexpr LessThanIterator& operator+=(difference_type n) { + it_ += n; + return *this; + } + constexpr LessThanIterator& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + constexpr friend LessThanIterator operator+(LessThanIterator x, difference_type n) { + x += n; + return x; + } + constexpr friend LessThanIterator operator+(difference_type n, LessThanIterator x) { + x += n; + return x; + } + constexpr friend LessThanIterator operator-(LessThanIterator x, difference_type n) { + x -= n; + return x; + } + constexpr friend difference_type operator-(LessThanIterator x, LessThanIterator y) { return x.it_ - y.it_; } + + constexpr friend bool operator==(LessThanIterator const&, LessThanIterator const&) = default; + friend bool operator!=(LessThanIterator const&, LessThanIterator const&); + + constexpr friend bool operator<(LessThanIterator const& x, LessThanIterator const& y) { return x.it_ < y.it_; } + friend bool operator<=(LessThanIterator const&, LessThanIterator const&); + friend bool operator>(LessThanIterator const&, LessThanIterator const&); + friend bool operator>=(LessThanIterator const&, LessThanIterator const&); +}; +static_assert(std::random_access_iterator); + +struct SmallerThanRange : IntBufferView { + using IntBufferView::IntBufferView; + constexpr LessThanIterator begin() const { return {buffer_}; } + constexpr LessThanIterator end() const { return {buffer_ + size_}; } +}; +static_assert(std::ranges::random_access_range); + +struct ForwardCommonView : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = forward_iterator; + + constexpr iterator begin() const { return iterator(buffer_); } + constexpr iterator end() const { return iterator(buffer_ + size_); } +}; + +constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) { + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); +} + +constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) { + using Iter1 = decltype(iter1); + using Iter2 = decltype(iter2); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); +} + +constexpr bool test() { + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + using SubRange = std::ranges::subrange; + static_assert(std::three_way_comparable); + using R = std::ranges::zip_view; + static_assert(std::three_way_comparable>); + + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5))); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + } + + { + // Test an old-school iterator with no operator<=>; the transform iterator shouldn't have + // operator<=> either. + using It = random_access_iterator; + using Subrange = std::ranges::subrange; + static_assert(!std::three_way_comparable); + using R = std::ranges::zip_view; + static_assert(!std::three_way_comparable>); + + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(Subrange(It(a), It(a + 4)), Subrange(It(b), It(b + 5))); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + } + + { + // non random_access_range + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + + std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)}; + using View = decltype(v); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + static_assert(std::ranges::common_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // in this case sentinel is computed by getting each of the underlying sentinel, so only one + // underlying iterator is comparing equal + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + std::ranges::zip_view v{ForwardCommonView(buffer1), ForwardCommonView(buffer2)}; + using View = decltype(v); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::bidirectional_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + // it1: + // it2: + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // only < and == are needed + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(SmallerThanRange(a), SmallerThanRange(b)); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + } + + { + // underlying iterator does not support == + using IterNoEqualView = BasicView, sentinel_wrapper>>; + int buffer[] = {1}; + std::ranges::zip_view r(IterNoEqualView{buffer}); + auto it = r.begin(); + using Iter = decltype(it); + static_assert(!std::invocable, Iter, Iter>); + inequalityOperatorsDoNotExistTest(it, it); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// iterator() = default; + +#include +#include + +#include "../types.h" + +struct PODIter { + int i; // deliberately uninitialised + + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int operator*() const { return i; } + + constexpr PODIter& operator++() { return *this; } + constexpr void operator++(int) {} + + friend constexpr bool operator==(const PODIter&, const PODIter&) = default; +}; + +struct IterDefaultCtrView : std::ranges::view_base { + PODIter begin() const; + PODIter end() const; +}; + +struct IterNoDefaultCtrView : std::ranges::view_base { + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +using zip_iter = std::ranges::iterator_t>; + +static_assert(!std::default_initializable>); +static_assert(!std::default_initializable>); +static_assert(!std::default_initializable>); +static_assert(std::default_initializable>); +static_assert(std::default_initializable>); + +constexpr bool test() { + using ZipIter = zip_iter; + { + ZipIter iter; + auto [x] = *iter; + assert(x == 0); // PODIter has to be initialised to have value 0 + } + + { + ZipIter iter = {}; + auto [x] = *iter; + assert(x == 0); // PODIter has to be initialised to have value 0 + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator(iterator i) +// requires Const && (convertible_­to, +// iterator_t>> && ...); + +#include + +#include +#include + +#include "../types.h" + +using ConstIterIncompatibleView = BasicView, forward_iterator, + random_access_iterator, random_access_iterator>; +static_assert(!std::convertible_to, + std::ranges::iterator_t>); + +constexpr bool test() { + int buffer[3] = {1, 2, 3}; + + { + std::ranges::zip_view v(NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + assert(iter1 == iter2); + + static_assert(!std::is_same_v); + + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + } + + { + // underlying non-const to const not convertible + std::ranges::zip_view v(ConstIterIncompatibleView{buffer}); + auto iter1 = v.begin(); + auto iter2 = std::as_const(v).begin(); + + static_assert(!std::is_same_v); + + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator--() requires all-bidirectional; +// constexpr iterator operator--(int) requires all-bidirectional; + +#include +#include +#include +#include + +#include "../types.h" + +template +concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; }; + +struct NonBidi : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = forward_iterator; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(iterator(buffer_ + size_)); } +}; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // all random access + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.end(); + using Iter = decltype(it); + + static_assert(std::is_same_v); + auto& it_ref = --it; + assert(&it_ref == &it); + + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(b[2])); + assert(std::get<2>(*it) == 2); + + static_assert(std::is_same_v); + it--; + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(b[1])); + assert(std::get<2>(*it) == 1); + } + + { + // all bidi+ + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(BidiCommonView{buffer}, std::views::iota(0, 5)); + auto it = v.begin(); + using Iter = decltype(it); + + ++it; + ++it; + + static_assert(std::is_same_v); + auto& it_ref = --it; + assert(&it_ref == &it); + + assert(it == ++v.begin()); + + static_assert(std::is_same_v); + auto tmp = it--; + assert(it == v.begin()); + assert(tmp == ++v.begin()); + } + + { + // non bidi + int buffer[3] = {4, 5, 6}; + std::ranges::zip_view v(a, NonBidi{buffer}); + using Iter = std::ranges::iterator_t; + static_assert(!canDecrement); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/deref.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto operator*() const; + +#include +#include +#include +#include + +#include "../types.h" + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // single range + std::ranges::zip_view v(a); + auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + static_assert(std::is_same_v>); + } + + { + // operator* is const + std::ranges::zip_view v(a); + const auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + } + + { + // two ranges with different types + std::ranges::zip_view v(a, b); + auto it = v.begin(); + auto [x, y] = *it; + assert(&x == &(a[0])); + assert(&y == &(b[0])); + static_assert(std::is_same_v>); + + x = 5; + y = 0.1; + assert(a[0] == 5); + assert(b[0] == 0.1); + } + + { + // underlying range with prvalue range_reference_t + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(b[0])); + assert(std::get<2>(*it) == 0); + static_assert(std::is_same_v>); + } + + { + // const-correctness + std::ranges::zip_view v(a, std::as_const(a)); + auto it = v.begin(); + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(a[0])); + static_assert(std::is_same_v>); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/increment.pass.cpp @@ -0,0 +1,135 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int) requires all_forward; + +#include +#include +#include +#include + +#include "../types.h" + +struct InputRange : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = cpp20_input_iterator; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(iterator(buffer_ + size_)); } +}; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // random/contiguous + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(b[0])); + assert(std::get<2>(*it) == 0); + + static_assert(std::is_same_v); + + auto& it_ref = ++it; + assert(&it_ref == &it); + + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(b[1])); + assert(std::get<2>(*it) == 1); + + static_assert(std::is_same_v); + auto original = it; + auto copy = it++; + assert(original == copy); + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(b[2])); + assert(std::get<2>(*it) == 2); + } + + { + // bidi + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(BidiCommonView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v); + auto& it_ref = ++it; + assert(&it_ref == &it); + assert(&(std::get<0>(*it)) == &(buffer[1])); + + static_assert(std::is_same_v); + auto original = it; + auto copy = it++; + assert(copy == original); + assert(&(std::get<0>(*it)) == &(buffer[2])); + } + + { + // forward + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(ForwardSizedView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v); + auto& it_ref = ++it; + assert(&it_ref == &it); + assert(&(std::get<0>(*it)) == &(buffer[1])); + + static_assert(std::is_same_v); + auto original = it; + auto copy = it++; + assert(copy == original); + assert(&(std::get<0>(*it)) == &(buffer[2])); + } + + { + // all input+ + int buffer[3] = {4, 5, 6}; + std::ranges::zip_view v(a, InputRange{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(a[0])); + assert(&(std::get<1>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v); + auto& it_ref = ++it; + assert(&it_ref == &it); + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(buffer[1])); + + static_assert(std::is_same_v); + it++; + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(buffer[2])); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr auto iter_move(const iterator& i) noexcept(see below); + +#include +#include +#include +#include +#include + +#include "../types.h" + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&){}; +}; + +constexpr bool test() { + { + // underlying iter_move noexcept + std::array a1{1, 2, 3, 4}; + const std::array a2{3.0, 4.0}; + + std::ranges::zip_view v(a1, a2, std::views::iota(3L)); + assert(std::ranges::iter_move(v.begin()) == std::make_tuple(1, 3.0, 3L)); + static_assert(std::is_same_v>); + + auto it = v.begin(); + static_assert(noexcept(std::ranges::iter_move(it))); + } + + { + // underlying iter_move may throw + auto throwingMoveRange = + std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; }); + std::ranges::zip_view v(throwingMoveRange); + auto it = v.begin(); + static_assert(!noexcept(std::ranges::iter_move(it))); + } + + { + // underlying iterators' iter_move are called through ranges::iter_move + adltest::IterMoveSwapRange r1{}, r2{}; + assert(r1.iter_move_called_times == 0); + assert(r2.iter_move_called_times == 0); + std::ranges::zip_view v(r1, r2); + auto it = v.begin(); + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(r1.iter_move_called_times == 1); + assert(r2.iter_move_called_times == 1); + } + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(r1.iter_move_called_times == 2); + assert(r2.iter_move_called_times == 2); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(see below) +// requires (indirectly_­swappable>> && ...); + +#include +#include +#include + +#include "../types.h" + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&){}; + ThrowingMove& operator=(ThrowingMove&&){return *this;} +}; + +constexpr bool test() { + { + std::array a1{1, 2, 3, 4}; + std::array a2{0.1, 0.2, 0.3}; + std::ranges::zip_view v(a1, a2); + auto iter1 = v.begin(); + auto iter2 = ++v.begin(); + + std::ranges::iter_swap(iter1, iter2); + + assert(a1[0] == 2); + assert(a1[1] == 1); + assert(a2[0] == 0.2); + assert(a2[1] == 0.1); + + auto [x1, y1] = *iter1; + assert(&x1 == &a1[0]); + assert(&y1 == &a2[0]); + + auto [x2, y2] = *iter2; + assert(&x2 == &a1[1]); + assert(&y2 == &a2[1]); + + static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); + } + + { + // underlying iter_swap may throw + std::array iterSwapMayThrow{}; + std::ranges::zip_view v(iterSwapMayThrow); + auto iter1 = v.begin(); + auto iter2 = ++v.begin(); + static_assert(!noexcept(std::ranges::iter_swap(iter1, iter2))); + } + + { + // underlying iterators' iter_move are called through ranges::iter_swap + adltest::IterMoveSwapRange r1, r2; + assert(r1.iter_swap_called_times == 0); + assert(r2.iter_swap_called_times == 0); + + std::ranges::zip_view v{r1, r2}; + auto it1 = v.begin(); + auto it2 = std::ranges::next(it1, 3); + + std::ranges::iter_swap(it1, it2); + assert(r1.iter_swap_called_times == 2); + assert(r2.iter_swap_called_times == 2); + + std::ranges::iter_swap(it1, it2); + assert(r1.iter_swap_called_times == 4); + assert(r2.iter_swap_called_times == 4); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp @@ -0,0 +1,185 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Iterator traits and member typedefs in zip_view::. + +#include +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template +struct ForwardView : std::ranges::view_base { + forward_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +struct InputView : std::ranges::view_base { + cpp17_input_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +concept HasIterCategory = requires { typename T::iterator_category; }; + +template +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 +struct DiffTypeRange { + DiffTypeIter begin() const; + DiffTypeIter end() const; +}; + +struct Foo {}; +struct Bar {}; + +struct ConstVeryDifferentRange { + int* begin(); + int* end(); + + forward_iterator begin() const; + forward_iterator end() const; +}; + +void test() { + int buffer[] = {1, 2, 3, 4}; + { + // 2 views should have pair value_type + // random_access_iterator_tag + std::ranges::zip_view v(buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + + { + // !=2 views should have tuple value_type + std::ranges::zip_view v(buffer, buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + + { + // bidirectional_iterator_tag + std::ranges::zip_view v(BidiCommonView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + } + + { + // forward_iterator_tag + using Iter = std::ranges::iterator_t>>; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + + { + // nested zip_view + std::ranges::zip_view v(buffer, buffer); + std::ranges::zip_view v2(buffer, v); + using Iter = decltype(v2.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>>); + static_assert(HasIterCategory); + } + + { + // input_iterator_tag + using Iter = std::ranges::iterator_t>>; + + static_assert(std::is_same_v); + static_assert(!HasIterCategory); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + } + + { + // difference_type of single view + std::ranges::zip_view v{DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + + { + // difference_type of multiple views should be the common type + std::ranges::zip_view v{DiffTypeRange{}, DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + const std::array foos{Foo{}}; + std::array bars{Bar{}, Bar{}}; + { + // value_type of single view + std::ranges::zip_view v{foos}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + { + // value_type of multiple views with different value_type + std::ranges::zip_view v{foos, bars}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + { + // const-iterator different from iterator + std::ranges::zip_view v{ConstVeryDifferentRange{}}; + using Iter = decltype(v.begin()); + using ConstIter = decltype(std::as_const(v).begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + } + +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges, no-exceptions + +// If the invocation of any non-const member function of `iterator` exits via an +// exception, the iterator acquires a singular value. + +#include + +#include + +#include "../types.h" + +struct ThrowOnIncrementIterator { + int* it_; + + using value_type = int; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + ThrowOnIncrementIterator() = default; + explicit ThrowOnIncrementIterator(int* it) : it_(it) {} + + ThrowOnIncrementIterator& operator++() { + ++it_; + throw 5; + return *this; + } + void operator++(int) { ++it_; } + + int& operator*() const { return *it_; } + + friend bool operator==(ThrowOnIncrementIterator const&, ThrowOnIncrementIterator const&) = default; +}; + +struct ThrowOnIncrementView : IntBufferView { + ThrowOnIncrementIterator begin() const { return ThrowOnIncrementIterator{buffer_}; } + ThrowOnIncrementIterator end() const { return ThrowOnIncrementIterator{buffer_ + size_}; } +}; + +// Cannot run the test at compile time because it is not allowed to throw exceptions +void test() { + int buffer[] = {1, 2, 3}; + { + // zip iterator should be able to be destroyed after member function throws + std::ranges::zip_view v{ThrowOnIncrementView{buffer}}; + auto it = v.begin(); + try { + ++it; + assert(false); // should not be reached as the above expression should throw. + } catch (int e) { + assert(e == 5); + } + } + + { + // zip iterator should be able to be assigned after member function throws + std::ranges::zip_view v{ThrowOnIncrementView{buffer}}; + auto it = v.begin(); + try { + ++it; + assert(false); // should not be reached as the above expression should throw. + } catch (int e) { + assert(e == 5); + } + it = v.begin(); + auto [x] = *it; + assert(x == 1); + } +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto operator[](difference_type n) const requires +// all_random_access + +#include +#include + +#include "../types.h" + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // random_access_range + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v>); + } + + { + // contiguous_range + std::ranges::zip_view v(ContiguousCommonView{buffer}, ContiguousCommonView{buffer}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v>); + } + + { + // non random_access_range + std::ranges::zip_view v(BidiCommonView{buffer}); + auto iter = v.begin(); + const auto canSubscript = [](auto&& it) { return requires { it[0]; }; }; + static_assert(!canSubscript(iter)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp @@ -0,0 +1,332 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// test if zip_view models input_range, forward_range, bidirectional_range, +// random_access_range, contiguous_range, common_range +// sized_range + +#include +#include +#include +#include +#include + +#include "types.h" + +void testConceptPair() { + int buffer1[2] = {1, 2}; + int buffer2[3] = {1, 2, 3}; + { + std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}}; + using View = decltype(v); + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } +} + +void testConceptTuple() { + int buffer1[2] = {1, 2}; + int buffer2[3] = {1, 2, 3}; + int buffer3[4] = {1, 2, 3, 4}; + + // TODO: uncomment all the static_asserts once [tuple.tuple] section in P2321R2 is implemented + // This is because convertible_to&, tuple> is false without + // the above implementation, thus the zip iterator does not model indirectly_readable + + { + std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}, + ContiguousCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2}, + ContiguousNonCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2}, + ContiguousNonCommonSized{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2}, + ContiguousCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2}, + SizedRandomAccessView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2}, + NonSizedRandomAccessView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}, SizedRandomAccessView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}, BidiCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}}; + using View = decltype(v); + // static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } +} + +using OutputIter = cpp17_output_iterator; +static_assert(std::output_iterator); + +struct OutputView : std::ranges::view_base { + OutputIter begin() const; + sentinel_wrapper end() const; +}; +static_assert(std::ranges::output_range); +static_assert(!std::ranges::input_range); + +template +concept zippable = requires { + typename std::ranges::zip_view; +}; + +// output_range is not supported +static_assert(!zippable); +static_assert(!zippable); +static_assert(zippable); diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.default.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// sentinel() = default; + +#include +#include +#include + +struct PODSentinel { + bool b; // deliberately uninitialised + + friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + PODSentinel end(); +}; + +constexpr bool test() { + { + using R = std::ranges::zip_view; + using Sentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v>); + + std::ranges::iterator_t it; + + Sentinel s1; + assert(it != s1); // PODSentinel.b is initialised to false + + Sentinel s2 = {}; + assert(it != s2); // PODSentinel.b is initialised to false + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/ctor.other.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr sentinel(sentinel s); + +#include +#include + +#include "../types.h" + +template +struct convertible_sentinel_wrapper { + explicit convertible_sentinel_wrapper() = default; + constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {} + + template + requires std::convertible_to + constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper& 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 end() { return convertible_sentinel_wrapper(buffer_ + size_); } + constexpr convertible_sentinel_wrapper end() const { + return convertible_sentinel_wrapper(buffer_ + size_); + } +}; + +static_assert(!std::ranges::common_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +static_assert(std::convertible_to, + std::ranges::sentinel_t>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + std::ranges::zip_view v{NonSimpleNonCommonConvertibleView(buffer1), NonSimpleNonCommonConvertibleView(buffer2)}; + static_assert(!std::ranges::common_range); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + + 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); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/eq.pass.cpp @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires sentinel_for, iterator_t>> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include +#include +#include +#include + +#include "../types.h" + +using Iterator = random_access_iterator; +using ConstIterator = random_access_iterator; + +template +struct ComparableSentinel { + + using Iter = std::conditional_t; + 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(Iterator(buffer_ + size_)); } + constexpr auto end() const { return ComparableSentinel(ConstIterator(buffer_ + size_)); } +}; + +struct ConstIncompatibleView : std::ranges::view_base { + cpp17_input_iterator begin(); + forward_iterator begin() const; + sentinel_wrapper> end(); + sentinel_wrapper> end() const; +}; + +// clang-format off +template +concept EqualComparable = std::invocable, const Iter&, const Sent&>; +// clang-format on + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + // simple-view: const and non-const have the same iterator/sentinel type + std::ranges::zip_view v{SimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)}; + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + + assert(v.begin() != v.end()); + assert(v.begin() + 1 != v.end()); + assert(v.begin() + 2 != v.end()); + assert(v.begin() + 3 != v.end()); + assert(v.begin() + 4 == v.end()); + } + + { + // !simple-view: const and non-const have different iterator/sentinel types + std::ranges::zip_view v{NonSimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)}; + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + + 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; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(EqualComparable); + static_assert(!EqualComparable); + static_assert(EqualComparable); + static_assert(EqualComparable); + } + + { + // underlying const/non-const sentinel can be compared with both const/non-const iterator + std::ranges::zip_view v{ComparableView(buffer1), ComparableView(buffer2)}; + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + + 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; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(EqualComparable); + static_assert(EqualComparable); + static_assert(EqualComparable); + static_assert(EqualComparable); + } + + { + // underlying const/non-const sentinel cannot be compared with non-const/const iterator + std::ranges::zip_view v{ComparableView(buffer1), ConstIncompatibleView{}}; + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(EqualComparable); + static_assert(!EqualComparable); + static_assert(!EqualComparable); + static_assert(EqualComparable); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp @@ -0,0 +1,235 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires(sized_sentinel_for>, +// iterator_t>>&&...) +// friend constexpr common_type_t>...> +// operator-(const iterator&, const sentinel&) +// +// template +// requires(sized_sentinel_for>, +// iterator_t>>&&...) +// friend constexpr common_type_t>...> +// operator-(const sentinel&, const iterator&) + +#include +#include +#include +#include +#include + +#include "../types.h" + +template +struct convertible_forward_sized_iterator { + Base it_ = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + convertible_forward_sized_iterator() = default; + constexpr convertible_forward_sized_iterator(Base it) : it_(it) {} + + template U> + constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator& 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>); + +template +struct convertible_sized_sentinel { + Base base_; + explicit convertible_sized_sentinel() = default; + constexpr convertible_sized_sentinel(const Base& it) : base_(it) {} + + template U> + constexpr convertible_sized_sentinel(const convertible_sized_sentinel& other) : base_(other.base_) {} + + template + requires(std::convertible_to || std::convertible_to) + friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) { + return s.base_ == base; + } + template + requires(std::convertible_to || std::convertible_to) + friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) { + return s.base_ - i; + } + + template + requires(std::convertible_to || std::convertible_to) + friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) { + return i - s.base_; + } +}; +static_assert(std::sized_sentinel_for>, + convertible_forward_sized_iterator<>>); +static_assert(std::sized_sentinel_for>, + convertible_forward_sized_iterator>); +static_assert(std::sized_sentinel_for>, + convertible_forward_sized_iterator>); + +struct ConstCompatibleForwardSized : IntBufferView { + using IntBufferView::IntBufferView; + + using iterator = convertible_forward_sized_iterator; + using const_iterator = convertible_forward_sized_iterator; + + constexpr iterator begin() { return {buffer_}; } + constexpr const_iterator begin() const { return {buffer_}; } + constexpr convertible_sized_sentinel end() { return iterator{buffer_ + size_}; } + constexpr convertible_sized_sentinel end() const { return const_iterator{buffer_ + size_}; } +}; + +// clang-format off +template +concept HasMinus = std::invocable,const T&, const U&>; + +template +concept SentinelHasMinus = HasMinus, std::ranges::iterator_t>; +// clang-format on + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + + { + // simple-view + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 5); + assert(st - std::ranges::next(it, 1) == 4); + + assert(it - st == -5); + assert(std::ranges::next(it, 1) - st == -4); + static_assert(SentinelHasMinus); + } + + { + // shortest range + std::ranges::zip_view v(std::views::iota(0, 3), ForwardSizedNonCommon(buffer1)); + static_assert(!std::ranges::common_range); + 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); + } + + { + // underlying sentinel does not model sized_sentinel_for + std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1)); + static_assert(!std::ranges::common_range); + static_assert(!SentinelHasMinus); + } + + { + // const imcompatible: + // underlying const sentinels cannot substract underlying iterators + // underlying sentinels cannot substract underlying const iterators + std::ranges::zip_view v(NonSimpleForwardSizedNonCommon{buffer1}); + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + 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); + static_assert(!HasMinus); + static_assert(!HasMinus); + static_assert(!HasMinus); + } + + { + // const compatible allow non-const to const conversion + std::ranges::zip_view v(ConstCompatibleForwardSized{buffer1}); + static_assert(!std::ranges::common_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + static_assert(HasMinus); + + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + + assert(it - st == -5); + assert(st - it == 5); + assert(const_it - const_st == -5); + assert(const_st - const_it == 5); + assert(it - const_st == -5); + assert(const_st - it == 5); + assert(const_it - st == -5); + assert(st - const_it == 5); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto size() requires(sized_range&&...) +// constexpr auto size() const requires(sized_range&&...) + +#include + +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; +struct View : std::ranges::view_base { + std::size_t size_ = 0; + constexpr View(std::size_t s) : size_(s) {} + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + size_; } +}; + +struct SizedNonConst : std::ranges::view_base { + using iterator = forward_iterator; + std::size_t size_ = 0; + constexpr SizedNonConst(std::size_t s) : size_(s) {} + constexpr auto begin() const { return iterator{buffer}; } + constexpr auto end() const { return iterator{buffer + size_}; } + constexpr std::size_t size() { return size_; } +}; + +struct StrangeSizeView : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } + + constexpr auto size() { return 5; } + constexpr auto size() const { return 6; } +}; + +constexpr bool test() { + { + // single range + std::ranges::zip_view v(View(8)); + assert(v.size() == 8); + assert(std::as_const(v).size() == 8); + } + + { + // multiple ranges same type + std::ranges::zip_view v(View(2), View(3)); + assert(v.size() == 2); + assert(std::as_const(v).size() == 2); + } + + { + // multiple ranges different types + std::ranges::zip_view v(std::views::iota(0, 500), View(3)); + assert(v.size() == 3); + assert(std::as_const(v).size() == 3); + } + + { + // const-view non-sized range + std::ranges::zip_view v(SizedNonConst(2), View(3)); + assert(v.size() == 2); + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + } + + { + // const/non-const has different sizes + std::ranges::zip_view v(StrangeSizeView{}); + assert(v.size() == 5); + assert(std::as_const(v).size() == 6); + } + + { + // underlying range not sized + std::ranges::zip_view v(InputCommonView{buffer}); + static_assert(!std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/types.h @@ -0,0 +1,459 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H + +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" + +#if TEST_STD_VER <= 20 +# error "range.zip/types.h" can only be included in builds supporting C++20 +#endif // TEST_STD_VER <= 20 + +template +struct BufferView : std::ranges::view_base { + T* buffer_; + std::size_t size_; + + template + constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} +}; + +using IntBufferView = BufferView; + +template +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; +using NonSimpleCommon = Common; + +using SimpleCommonRandomAccessSized = SimpleCommon; +using NonSimpleCommonRandomAccessSized = NonSimpleCommon; + +static_assert(std::ranges::common_range>); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct CommonNonRandom : IntBufferView { + using IntBufferView::IntBufferView; + using const_iterator = forward_iterator; + using iterator = forward_iterator; + 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; +using NonSimpleCommonNonRandom = CommonNonRandom; + +static_assert(std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct NonCommon : IntBufferView { + using IntBufferView::IntBufferView; + constexpr int* begin() + requires(!Simple) { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() + requires(!Simple) { + return sentinel_wrapper(buffer_ + size_); + } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } +}; + +using SimpleNonCommon = NonCommon; +using NonSimpleNonCommon = NonCommon; + +static_assert(!std::ranges::common_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct NonCommonSized : IntBufferView { + using IntBufferView::IntBufferView; + constexpr int* begin() + requires(!Simple) { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() + requires(!Simple) { + return sentinel_wrapper(buffer_ + size_); + } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } + constexpr std::size_t size() const { return size_; } +}; + +using SimpleNonCommonSized = NonCommonSized; +using SimpleNonCommonRandomAcessSized = SimpleNonCommonSized; +using NonSimpleNonCommonSized = NonCommonSized; +using NonSimpleNonCommonRandomAcessSized = NonSimpleNonCommonSized; + +static_assert(!std::ranges::common_range); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct NonCommonNonRandom : IntBufferView { + using IntBufferView::IntBufferView; + + using const_iterator = forward_iterator; + using iterator = forward_iterator; + + constexpr iterator begin() + requires(!Simple) { + return iterator(buffer_); + } + constexpr const_iterator begin() const { return const_iterator(buffer_); } + constexpr sentinel_wrapper end() + requires(!Simple) { + return sentinel_wrapper(iterator(buffer_ + size_)); + } + constexpr sentinel_wrapper end() const { + return sentinel_wrapper(const_iterator(buffer_ + size_)); + } +}; + +using SimpleNonCommonNonRandom = NonCommonNonRandom; +using NonSimpleNonCommonNonRandom = NonCommonNonRandom; + +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct BasicView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr NonConstIter begin() + requires(!std::is_same_v) { + return NonConstIter(buffer_); + } + constexpr Iter begin() const { return Iter(buffer_); } + + constexpr NonConstSent end() + requires(!std::is_same_v) { + if constexpr (std::is_same_v) { + return NonConstIter(buffer_ + size_); + } else { + return NonConstSent(NonConstIter(buffer_ + size_)); + } + } + + constexpr Sent end() const { + if constexpr (std::is_same_v) { + return Iter(buffer_ + size_); + } else { + return Sent(Iter(buffer_ + size_)); + } + } +}; + +template +struct forward_sized_iterator { + Base it_ = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + using pointer = Base; + using reference = decltype(*Base{}); + + forward_sized_iterator() = default; + constexpr forward_sized_iterator(Base it) : it_(it) {} + + constexpr reference operator*() const { return *it_; } + + constexpr forward_sized_iterator& operator++() { + ++it_; + return *this; + } + constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); } + + friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default; + + friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) { + return x.it_ - y.it_; + } +}; +static_assert(std::forward_iterator>); +static_assert(std::sized_sentinel_for, forward_sized_iterator<>>); + +using ForwardSizedView = BasicView>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleForwardSizedView = BasicView, forward_sized_iterator, + forward_sized_iterator, forward_sized_iterator>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using ForwardSizedNonCommon = BasicView, sized_sentinel>>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleForwardSizedNonCommon = + BasicView, sized_sentinel>, + forward_sized_iterator, sized_sentinel>>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +struct SizedRandomAccessView : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = random_access_iterator; + + constexpr auto begin() const { return iterator(buffer_); } + constexpr auto end() const { return sized_sentinel(iterator(buffer_ + size_)); } + + constexpr decltype(auto) operator[](std::size_t n) const { return *(begin() + n); } +}; +static_assert(std::ranges::view); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); + +using NonSizedRandomAccessView = + BasicView, sentinel_wrapper>>; +static_assert(!std::ranges::contiguous_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleNonSizedRandomAccessView = + BasicView, sentinel_wrapper>, + random_access_iterator, sentinel_wrapper> >; +static_assert(!std::ranges::contiguous_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using ContiguousCommonView = BasicView; +static_assert(std::ranges::contiguous_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::sized_range); + +using ContiguousNonCommonView = BasicView>; +static_assert(std::ranges::contiguous_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::sized_range); + +using ContiguousNonCommonSized = BasicView>; + +static_assert(std::ranges::contiguous_range); +static_assert(!std::ranges::common_range); +static_assert(std::ranges::sized_range); + +template +struct common_input_iterator { + Base it_; + + using value_type = int; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr common_input_iterator() = default; + constexpr explicit common_input_iterator(Base it) : it_(it) {} + + constexpr common_input_iterator& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } + + constexpr int& operator*() const { return *it_; } + + friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default; +}; + +using InputCommonView = BasicView>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleInputCommonView = BasicView, common_input_iterator, + common_input_iterator, common_input_iterator>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using InputNonCommonView = BasicView, sentinel_wrapper>>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleInputNonCommonView = + BasicView, sentinel_wrapper>, + common_input_iterator, sentinel_wrapper>>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using BidiCommonView = BasicView>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleBidiCommonView = BasicView, bidirectional_iterator, + bidirectional_iterator, bidirectional_iterator>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +struct SizedBidiCommon : BidiCommonView { + using BidiCommonView::BidiCommonView; + std::size_t size() const { return base(end()) - base(begin()); } +}; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +struct NonSimpleSizedBidiCommon : NonSimpleBidiCommonView { + using NonSimpleBidiCommonView::NonSimpleBidiCommonView; + std::size_t size() const { return base(end()) - base(begin()); } +}; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using BidiNonCommonView = BasicView, sentinel_wrapper>>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleBidiNonCommonView = + BasicView, sentinel_wrapper>, + bidirectional_iterator, sentinel_wrapper>>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using SizedBidiNonCommonView = BasicView, sized_sentinel>>; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleSizedBidiNonCommonView = + BasicView, sized_sentinel>, + bidirectional_iterator, sized_sentinel>>; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +namespace adltest{ +struct iter_move_swap_iterator { + + std::reference_wrapper iter_move_called_times; + std::reference_wrapper iter_swap_called_times; + int i = 0; + + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int operator*() const { return i; } + + constexpr iter_move_swap_iterator& operator++() { + ++i; + return *this; + } + constexpr void operator++(int) { ++i; } + + friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; } + + friend constexpr int iter_move(iter_move_swap_iterator const& it) { + ++it.iter_move_called_times; + return it.i; + } + friend constexpr void iter_swap(iter_move_swap_iterator const& x, iter_move_swap_iterator const& y) { + ++x.iter_swap_called_times; + ++y.iter_swap_called_times; + } +}; + +struct IterMoveSwapRange { + int iter_move_called_times = 0; + int iter_swap_called_times = 0; + constexpr auto begin() { return iter_move_swap_iterator{iter_move_called_times, iter_swap_called_times}; } + constexpr auto end() const { return std::default_sentinel; } +}; +} // namespace adltest + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H