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 @@ -8,9 +8,9 @@ | `[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| +| `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 @@ -371,6 +371,7 @@ __ranges/transform_view.h __ranges/view_interface.h __ranges/views.h + __ranges/zip_view.h __split_buffer __std_stream __string diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/zip_view.h @@ -0,0 +1,530 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef _LIBCPP___RANGES_ZIP_VIEW_H +#define _LIBCPP___RANGES_ZIP_VIEW_H + +#include <__config> + +#include <__algorithm/ranges_min.h> +#include <__compare/three_way_comparable.h> +#include <__concepts/convertible_to.h> +#include <__concepts/equality_comparable.h> +#include <__functional/invoke.h> +#include <__functional/operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty_view.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__utility/forward.h> +#include <__utility/integer_sequence.h> +#include <__utility/move.h> +#include +#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>()); +} + +// 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 { + const auto __it_equals = __tuple_zip_transform(std::equal_to<>(), __x.__current_, __y.__current_); + return std::apply([](auto... __bs) { return (__bs || ...); }, __it_equals); + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires __zip_all_random_access<_Const, _Views...> && + (three_way_comparable>> && ...) + { + 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 = __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>> && ...) + { + __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) { + const auto __it_equals = __tuple_zip_transform(std::equal_to<>(), __iter_current(__x), __y.__end_); + return std::apply([](auto... bs) { return (bs || ...); }, __it_equals); + } + + 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 = __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 @@ -853,6 +853,7 @@ } module view_interface { private header "__ranges/view_interface.h" } module views { private header "__ranges/views.h" } + module zip_view { private header "__ranges/zip_view.h" } } } module ratio { diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -196,6 +196,17 @@ template requires view && input_range> class join_view; + + // [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 { @@ -257,6 +268,7 @@ #include <__ranges/transform_view.h> #include <__ranges/view_interface.h> #include <__ranges/views.h> +#include <__ranges/zip_view.h> #include <__tuple> // TODO: 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/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,93 @@ +//===----------------------------------------------------------------------===// +// +// 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 HasNonConstBegin = requires(T& t) { t.begin(); }; + +template +concept BeginHasNoOverload = requires { &T::begin; }; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + std::same_as> decltype(auto) val = *v.begin(); + assert(val == std::make_tuple(1, 0, 2.0)); + assert(&(std::get<0>(val)) == &buffer[0]); + } + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view()); + 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]); + } + { + // 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 = std::ranges::zip_view; + static_assert(HasConstBegin); +#ifdef __clang__ // gcc bug + static_assert(BeginHasNoOverload); +#endif + } + { + using View = std::ranges::zip_view; + static_assert(HasConstBegin); + static_assert(!BeginHasNoOverload); + } + { + using View = std::ranges::zip_view; + static_assert(!HasConstBegin); + static_assert(HasNonConstBegin); + } + 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,41 @@ +//===----------------------------------------------------------------------===// +// +// 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); + +void testBorrowedRange() { + 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,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::zip + +#include + +#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() { + { + auto v = std::views::zip(); + assert(std::ranges::empty(v)); + static_assert(std::is_same_v>>); + } + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + std::same_as> auto v = std::views::zip(SizedRandomAccessView{buffer}); + assert(std::ranges::size(v) == 8); + static_assert(std::is_same_v, std::tuple>); + } + { + std::array a{1, 2, 3}; + std::same_as>>> auto v = std::views::zip(a); + assert(&(std::get<0>(*v.begin())) == &(a[0])); + 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,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// zip_view() = default; + +#include + +#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 NoDefaultView : std::ranges::view_base { + NoDefaultView() = delete; + int* begin() const; + int* end() const; +}; + +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,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 + +// 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>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +constexpr bool test() { + + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[4] = {9, 8, 7, 6}; + { + // constructor from views + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + auto [i, j, k] = *v.begin(); + assert(i == 1); + assert(j == 0); + assert(k == 2.0); + } + { + // arguments are moved once + MoveAwareView mv; + std::ranges::zip_view v{std::move(mv), MoveAwareView{}}; + auto [numMoves1, numMoves2] = *v.begin(); + assert(numMoves1 == 2); // one move from stack to parameter, one move from parameter to member + assert(numMoves2 == 1); + } + { + std::ranges::zip_view v{InputCommonView{buffer}, ForwardSizedView{buffer2}}; + auto [i, j] = *v.begin(); + assert(i == 1); + assert(j == 9); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp @@ -0,0 +1,396 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() requires(!(simple-view && ...)) +// 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,135 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// x += n; +// x + n; +// n + x; +// x -= n; +// x - n; +// x - y; + +#include + +#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); + { + std::ranges::zip_view v(a, b); + auto it1 = v.begin(); + + auto it2 = it1 + 3; + auto [x2, y2] = *it2; + assert(&x2 == &(a[3])); + assert(&y2 == &(b[3])); + + auto it3 = 3 + it1; + auto [x3, y3] = *it3; + assert(&x3 == &(a[3])); + assert(&y3 == &(b[3])); + + it1 += 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[3])); + assert(&y1 == &(b[3])); + + using Iter = decltype(it1); + static_assert(std::invocable, Iter, intptr_t>); + static_assert(std::invocable, intptr_t, Iter>); + static_assert(canPlusEqual); + } + { + std::ranges::zip_view v(a, b); + auto it1 = v.end(); + + auto it2 = it1 - 3; + auto [x2, y2] = *it2; + assert(&x2 == &(a[2])); + assert(&y2 == &(b[2])); + + it1 -= 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[2])); + assert(&y1 == &(b[2])); + + using Iter = decltype(it1); + static_assert(std::invocable, Iter, intptr_t>); + static_assert(std::invocable, Iter, Iter>); + static_assert(canMinusEqual); + } + { + std::ranges::zip_view v(a, b); + assert((v.end() - v.begin()) == 5); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + assert((it1 - it2) == -2); + } + { + // in this case sentinel is computed by getting each underlying sentinel, so the distance + // between begin and end for each underlying iterators can be different + std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)}; + using View = decltype(v); + static_assert(std::ranges::common_range); + 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 range 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 range 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,242 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// zip_view::::operator{<,>,<=,>=,==,!=,<=>} + +#include +#include + +#include "test_iterators.h" +#include "../types.h" + +template +concept canSmallerThan = requires(T&& t1, T&& t2) { t1 < t2; }; + +struct smaller_than_iterator { + int* it_ = nullptr; + smaller_than_iterator() = default; + constexpr smaller_than_iterator(int* it) : it_(it) {} + + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int& operator*() const { return *it_; } + constexpr int& operator[](difference_type n) const { return it_[n]; } + constexpr smaller_than_iterator& operator++() { + ++it_; + return *this; + } + constexpr smaller_than_iterator& operator--() { + --it_; + return *this; + } + constexpr smaller_than_iterator operator++(int) { return smaller_than_iterator(it_++); } + constexpr smaller_than_iterator operator--(int) { return smaller_than_iterator(it_--); } + + constexpr smaller_than_iterator& operator+=(difference_type n) { + it_ += n; + return *this; + } + constexpr smaller_than_iterator& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + constexpr friend smaller_than_iterator operator+(smaller_than_iterator x, difference_type n) { + x += n; + return x; + } + constexpr friend smaller_than_iterator operator+(difference_type n, smaller_than_iterator x) { + x += n; + return x; + } + constexpr friend smaller_than_iterator operator-(smaller_than_iterator x, difference_type n) { + x -= n; + return x; + } + constexpr friend difference_type operator-(smaller_than_iterator x, smaller_than_iterator y) { return x.it_ - y.it_; } + + constexpr friend bool operator==(smaller_than_iterator const&, smaller_than_iterator const&) = default; + friend bool operator!=(smaller_than_iterator const&, smaller_than_iterator const&); + + constexpr friend bool operator<(smaller_than_iterator const& x, smaller_than_iterator const& y) { + return x.it_ < y.it_; + } + friend bool operator<=(smaller_than_iterator const&, smaller_than_iterator const&); + friend bool operator>(smaller_than_iterator const&, smaller_than_iterator const&); + friend bool operator>=(smaller_than_iterator const&, smaller_than_iterator const&); +}; +static_assert(std::random_access_iterator); + +struct SmallerThanRange : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr smaller_than_iterator begin() const { return {buffer_}; } + constexpr smaller_than_iterator end() const { return {buffer_ + size_}; } +}; +static_assert(std::ranges::random_access_range); + +struct ForwardCommonView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + using iterator = forward_iterator; + + constexpr iterator begin() const { return iterator(buffer_); } + constexpr iterator end() const { return iterator(buffer_ + size_); } +}; + +constexpr bool test() { + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + 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; + + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + } + + { + // Test an old-school iterator with no operator<=>; the transform iterator shouldn't have + // operator<=> either. + using It = random_access_iterator; + 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; + + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); + } + { + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + + std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)}; + using View = decltype(v); + static_assert(!std::ranges::forward_range); + 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); + + using Iter = decltype(it1); + static_assert(!canSmallerThan); + } + { + // in this case sentinel is computed by getting each underlying sentinel, so only one + // underlying iterator is comparing equal + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + std::ranges::zip_view v{ForwardCommonView(buffer1), ForwardCommonView(buffer2)}; + using View = decltype(v); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::bidirectional_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + // it1: + // it2: + assert(it1 == it2); + } + { + // only < and == are needed + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip(SmallerThanRange(a), SmallerThanRange(b)); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.default.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// iterator() = default; + +#include +#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 Default : std::ranges::view_base { + PODIter begin() const; + PODIter end() const; +}; + +struct NonDefault : 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 = std::ranges::iterator_t>; + { + ZipIter iter; + auto [x] = *iter; + assert(x == 0); // PODIter has to be initialised to have value 0 + } + { + ZipIter iter = {}; + auto [x] = *iter; + assert(x == 0); // PODIter has to be initialised to have value 0 + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/ctor.other.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator(iterator i); + +#include + +#include +#include + +#include "../types.h" + +constexpr bool test() { + int buffer[3] = {1, 2, 3}; + + std::ranges::zip_view v(NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t 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); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/decrement.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator--(); +// constexpr iterator operator--(int); + +#include +#include +#include +#include + +#include "../types.h" + +template +concept canDecrement = requires(Iter it) { --it; }; + +struct NonBidi : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + 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,72 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto operator*() const; + +#include +#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}; + { + 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])); + } + { + 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); + } + { + 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>); + } + { + 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,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int) requires all_forward; + +#include +#include +#include +#include + +#include "../types.h" + +struct InputRange : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + 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])); + } + { + // forword + int buffer[2] = {1, 2}; + + std::ranges::zip_view v(ForwardSizedView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(std::get<0>(*it)) == &(buffer[0])); + + static_assert(std::is_same_v); + 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,69 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr decltype(auto) iter_move(const iterator& i); + +#include +#include +#include +#include +#include + +#include "../types.h" + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&){}; +}; + +constexpr bool test() { + { + std::array a1{1, 2, 3, 4}; + const std::array a2{3.0, 4.0}; + + std::ranges::zip_view v(a1, a2, std::views::iota(3L)); + assert(std::ranges::iter_move(v.begin()) == std::make_tuple(1, 3.0, 3L)); + static_assert(std::is_same_v>); + + static_assert(noexcept(std::ranges::iter_move(std::declval()))); + } + { + auto throwingMoveRange = + std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; }); + std::ranges::zip_view v(throwingMoveRange); + static_assert(!noexcept(std::ranges::iter_move(std::declval()))); + } + { + IterMoveSwapRange r1{}, r2{}; + assert(r1.iter_move_called_times == 0); + assert(r2.iter_move_called_times == 0); + std::ranges::zip_view v(r1, r2); + auto it = v.begin(); + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(r1.iter_move_called_times == 1); + assert(r2.iter_move_called_times == 1); + } + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(r1.iter_move_called_times == 2); + assert(r2.iter_move_called_times == 2); + } + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_swap.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr void iter_swap(const iterator& x, const iterator& y); + +#include +#include +#include + +#include "../types.h" + +constexpr bool test() { + { + std::array a1{1, 2, 3, 4}; + std::array a2{0.1, 0.2, 0.3}; + std::ranges::zip_view v(a1, a2); + auto iter1 = v.begin(); + auto iter2 = ++v.begin(); + + std::ranges::iter_swap(iter1, iter2); + + assert(a1[0] == 2); + assert(a1[1] == 1); + assert(a2[0] == 0.2); + assert(a2[1] == 0.1); + + auto [x1, y1] = *iter1; + assert(&x1 == &a1[0]); + assert(&y1 == &a2[0]); + + auto [x2, y2] = *iter2; + assert(&x2 == &a1[1]); + assert(&y2 == &a2[1]); + + static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); + } + { + IterMoveSwapRange r1, r2; + assert(r1.iter_swap_called_times == 0); + assert(r2.iter_swap_called_times == 0); + + std::ranges::zip_view v{r1, r2}; + auto it1 = v.begin(); + auto it2 = std::ranges::next(it1, 3); + + std::ranges::iter_swap(it1, it2); + assert(r1.iter_swap_called_times == 2); + assert(r2.iter_swap_called_times == 2); + + std::ranges::iter_swap(it1, it2); + assert(r1.iter_swap_called_times == 4); + assert(r2.iter_swap_called_times == 4); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/member_types.compile.pass.cpp @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Iterator traits and member typedefs in zip_view::. + +#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 diff_type_iter { + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = T; + + int operator*() const; + diff_type_iter& operator++(); + void operator++(int); + friend constexpr bool operator==(diff_type_iter, diff_type_iter) = default; +}; + +template +struct DiffTypeRange { + diff_type_iter begin() const; + diff_type_iter end() const; +}; + +struct Foo {}; +struct Bar {}; + +struct ConstVeryDifferentRange { + int* begin(); + int* end(); + + forward_iterator begin() const; + forward_iterator end() const; +}; + +void test() { + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + { + int buffer[4]; + 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); + } + { + 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); + } + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer); + std::ranges::zip_view v2(buffer, v); + using Iter = decltype(v2.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>>); + static_assert(HasIterCategory); + } + { + 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>); + } + { + std::ranges::zip_view v{DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + { + 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{}}; + { + std::ranges::zip_view v{foos}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + { + std::ranges::zip_view v{foos, bars}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + { + 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/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/subscript.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto operator[](difference_type __n) const requires +// all_random_access<_Const, _Views...> + +#include +#include + +#include "../types.h" + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v>); + } + { + 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>); + } + 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,319 @@ +//===----------------------------------------------------------------------===// +// +// 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_assert 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); + } +} + +struct OutputIter { + using It = int*; + It it; + struct proxy { + It it; + void operator=(int i) const; + }; + proxy operator*(); + + OutputIter& operator++(); + + OutputIter operator++(int); + + bool operator==(OutputIter other) const; + using difference_type = std::ptrdiff_t; +}; +static_assert(std::output_iterator); + +struct OutputView : std::ranges::view_base { + OutputIter begin() const; + OutputIter end() const; +}; +static_assert(std::ranges::output_range); + +template +concept zippable = requires(Ts&&... ts) { std::views::zip(std::move(ts)...); }; + +// output_range is not supported +static_assert(!zippable); +static_assert(!zippable); \ No newline at end of file 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 NonSimpleNonCommonConveritbleView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr convertible_sentinel_wrapper 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{NonSimpleNonCommonConveritbleView(buffer1), NonSimpleNonCommonConveritbleView(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,152 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr auto begin() { return Iterator(buffer_); } + constexpr auto begin() const { return ConstIterator(buffer_); } + constexpr auto end() { return ComparableSentinel(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; +}; + +template +concept EqualComparable = std::invocable < std::equal_to<>, +const Iter&, const Sent& > ; + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + std::ranges::zip_view v{SimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)}; + static_assert(!std::ranges::common_range); + 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()); + + using Iter = std::ranges::iterator_t; + using Sentinel = std::ranges::sentinel_t; + static_assert(EqualComparable); + } + { + 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); + } + { + 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); + } + { + 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,229 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + 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_}; } +}; + +template +concept HasMinus = std::invocable < std::minus<>, +const T&, const U& > ; + +template +concept SentinelHasMinus = HasMinus < std::ranges::sentinel_t, +std::ranges::iterator_t < T >> ; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + + { + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range); + 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); + } + + { + 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); + } + + { + std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1)); + static_assert(!std::ranges::common_range); + static_assert(!SentinelHasMinus); + } + { + // const incompatible + 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,89 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto size() requires(sized_range&&...) +// 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() { + { + std::ranges::zip_view v(View(8)); + assert(v.size() == 8); + assert(std::as_const(v).size() == 8); + } + { + std::ranges::zip_view v(View(2), View(3)); + assert(v.size() == 2); + assert(std::as_const(v).size() == 2); + } + { + std::ranges::zip_view v(std::views::iota(0, 500), View(3)); + assert(v.size() == 3); + assert(std::as_const(v).size() == 3); + } + { + std::ranges::zip_view v(SizedNonConst(2), View(3)); + assert(v.size() == 2); + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + } + { + std::ranges::zip_view v(StrangeSizeView{}); + assert(v.size() == 5); + assert(std::as_const(v).size() == 6); + } + { + std::ranges::zip_view v(InputCommonView{buffer}); + static_assert(!std::ranges::sized_range); + 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,467 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H + +#include + +#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 Buffer { + T* buffer_; + std::size_t size_; + + template + constexpr Buffer(T (&b)[N]) : buffer_(b), size_(N) {} +}; + +using IntBuffer = Buffer; + +template +struct Common : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr int* end() + requires(!Simple) + { + return buffer_ + size_; + } + constexpr const int* end() const { return buffer_ + size_; } +}; +using SimpleCommon = Common; +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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + + 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 : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + 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); + +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; } +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H