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, |In Progress| +| `[range.zip.iterator] `_, "zip_view::iterator", None, Hui Xie, |In Progress| +| `[range.zip.sentinel] `_, "zip_view::sentinel", None, Hui Xie, |In Progress| | `[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 @@ -370,6 +370,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,526 @@ +// -*- 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/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/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 <__tuple> +#include <__utility/forward.h> +#include <__utility/integer_sequence.h> +#include <__utility/move.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_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...(_Ts) != 2) +auto __tuple_or_pair_test() -> tuple<_Ts...>; + +template +using __tuple_or_pair = decltype(__tuple_or_pair_test<_Ts...>()); + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) { + return apply( + [&](_Ts&&... __elements) { + return __tuple_or_pair...>( + std::invoke(__f, std::forward<_Ts>(__elements))...); + }, + std::forward<_Tuple>(__tuple)); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_for_each(_Fun&& __f, _Tuple&& __tuple) { + apply([&]( + _Ts&&... __elements) { (std::invoke(__f, std::forward<_Ts>(__elements)), ...); }, + std::forward<_Tuple>(__tuple)); +} + + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_transform(_Fun&& __f, + _Tuple1&& __tuple1, + _Tuple2&& __tuple2, + index_sequence) { + return __tuple_or_pair< + invoke_result_t<_Fun&, typename tuple_element>::type, + typename tuple_element>::type>...>{ + std::invoke(__f, std::get(std::forward<_Tuple1>(__tuple1)), + std::get(std::forward<_Tuple2>(__tuple2)))...}; +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_transform(_Fun&& __f, + _Tuple1&& __tuple1, + _Tuple2&& __tuple2) { + return __tuple_zip_transform(__f, std::forward<_Tuple1>(__tuple1), + std::forward<_Tuple2>(__tuple2), + make_index_sequence>::value>()); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_zip_for_each(_Fun&& __f, + _Tuple1&& __tuple1, + _Tuple2&& __tuple2, + index_sequence) { + (std::invoke(__f, std::get(std::forward<_Tuple1>(__tuple1)), + std::get(std::forward<_Tuple2>(__tuple2))), + ...); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_for_each(_Fun&& __f, + _Tuple1&& __tuple1, + _Tuple2&& __tuple2) { + return __tuple_zip_for_each(__f, std::forward<_Tuple1>(__tuple1), + std::forward<_Tuple2>(__tuple2), + make_index_sequence>::value>()); +} + +// abs in cstdlib is not constexpr +// TODO : remove __abs once P0533R9 is implemented. +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __abs(_Tp t) { + return t < 0 ? -t : t; +} + +template + requires(view<_Views>&&...) && (sizeof...(_Views) > 0) +class zip_view : public view_interface> { + + 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(__tuple_transform(ranges::begin, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const requires(range&&...) { + return __iterator(__tuple_transform(ranges::begin, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() requires(!(__simple_view<_Views> && ...)) { + if constexpr (!__zip_is_common<_Views...>) { + return __sentinel(__tuple_transform(ranges::end, __views_)); + } else if constexpr ((random_access_range<_Views> && ...)) { + return begin() + iter_difference_t<__iterator>(size()); + } else { + return __iterator(__tuple_transform(ranges::end, __views_)); + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const requires(range&&...) { + if constexpr (!__zip_is_common) { + return __sentinel(__tuple_transform(ranges::end, __views_)); + } else if constexpr ((random_access_range && ...)) { + return begin() + iter_difference_t<__iterator>(size()); + } else { + return __iterator(__tuple_transform(ranges::end, __views_)); + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto size() requires(sized_range<_Views>&&...) { + return apply( + [](auto... sizes) { + using CT = make_unsigned_t>; + return (std::min)({CT(sizes)...}); + }, + __tuple_transform(ranges::size, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto size() const requires(sized_range&&...) { + return apply( + [](auto... sizes) { + using CT = make_unsigned_t>; + return (std::min)({CT(sizes)...}); + }, + __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 __zip_view_iterator_concept_test() { + 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(__zip_view_iterator_concept_test<_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 __tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator++() { + __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...> { + __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...> { + __tuple_for_each([&](_Idx& __i) { __i += iter_difference_t<_Idx>(__x); }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator-=(difference_type __x) requires + __zip_all_random_access<_Const, _Views...> { + __tuple_for_each([&](_Idx& __i) { __i -= iter_difference_t<_Idx>(__x); }, __current_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto operator[](difference_type __n) const requires + __zip_all_random_access<_Const, _Views...> { + return __tuple_transform( + [&](_Idx& __i) -> decltype(auto) { return __i[iter_difference_t<_Idx>(__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 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>> && ...) { + // clang-format off + return __x.__current_ <=> __y.__current_; + // clang-format on + } + + _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...> { + auto __r = __i; + __r += __n; + return __r; + } + + _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(std::minus<>(), __x.__current_, __y.__current_); + return apply( + [](auto... ds) { + return (std::min)({difference_type(ds)...}, + [](auto x, auto y) { return __abs(x) < __abs(y); }); + }, + __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 __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; + 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 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(std::minus<>(), __iter_current(__x), __y.__end_); + return apply( + [](auto... ds) { + using Diff = common_type_t>...>; + return (std::min)({Diff(ds)...}, [](auto x, auto y) { return __abs(x) < __abs(y); }); + }, + __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 + +#endif // _LIBCPP___RANGES_ZIP_VIEW_H \ No newline at end of file diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -848,6 +848,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; + + template + inline constexpr bool enable_borrowed_range> = + (enable_borrowed_range && ...); + + namespace views { inline constexpr unspecified zip = unspecified; } } namespace std { @@ -256,6 +267,7 @@ #include <__ranges/transform_view.h> #include <__ranges/view_interface.h> #include <__ranges/views.h> +#include <__ranges/zip_view.h> #include <__tuple> // TODO: has to export std::tuple_size. Replace this, once is granularized. #include // Required by the standard. #include // Required by the standard. diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/zip_view.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/zip_view.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/zip_view.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}} +#include <__ranges/zip_view.h> 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,53 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" +#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), std::ranges::single_view(2.)); + auto it = v.begin(); + assert(*it == std::make_tuple(1, 0, 2.0)); + assert(&(std::get<0>(*it)) == &buffer[0]); + ASSERT_SAME_TYPE(decltype(*it), std::tuple); + } + { + std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + } + { + std::ranges::zip_view v(SimpleCommon{buffer}, SimpleCommon{buffer}); + ASSERT_SAME_TYPE(decltype(v.begin()), decltype(std::as_const(v).begin())); + } + { + std::ranges::zip_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer}); + static_assert(!std::same_as); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/borrowing.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// inline constexpr bool enable_borrowed_range> = +// (enable_borrowed_range && ...); + +#include + +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,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::zip + +#include + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "types.h" + +constexpr bool test() { + { + auto v = std::views::zip(); + assert(std::ranges::empty(v)); + // a bit weird. non 0-arity zipped range's reference is a prvalue tuple + ASSERT_SAME_TYPE(std::ranges::range_reference_t, std::tuple<>&); + ASSERT_SAME_TYPE(decltype(v), std::ranges::empty_view>); + } + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto v = std::views::zip(SizedRandomAccessView{buffer}); + assert(std::ranges::size(v) == 8); + ASSERT_SAME_TYPE(std::ranges::range_reference_t, std::tuple); + ASSERT_SAME_TYPE(decltype(v), std::ranges::zip_view); + } + { + std::array a{1, 2, 3}; + auto v = std::views::zip(a); + assert(&(std::get<0>(*v.begin())) == &(a[0])); + ASSERT_SAME_TYPE(std::ranges::range_reference_t, std::tuple); + ASSERT_SAME_TYPE(decltype(v), std::ranges::zip_view>>); + } + { + 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); + } + 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,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 +// zip_view(Rs&&...) -> zip_view...>; + +#include +#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::same_as>>); + + static_assert(std::same_as, View>>); + + Container c{}; + static_assert( + std::same_as, 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,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 + +// zip_view() = default; + +#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; +}; + +constexpr bool test() { + { + std::ranges::zip_view v; + 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])); + static_assert(std::is_default_constructible_v); + } + { + std::ranges::zip_view v{}; + assert(v.size() == 3); + } + { + using View = std::ranges::zip_view; + View v = View(); + assert(v.size() == 3); + } + static_assert(!std::is_default_constructible_v< std::ranges::zip_view>); + + static_assert(!std::is_default_constructible_v>); + + 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,44 @@ +//===----------------------------------------------------------------------===// +// +// 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 "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), std::ranges::single_view(2.)); + auto [i, j, k] = *v.begin(); + assert(i == 1); + assert(j == 0); + assert(k == 2.0); + } + + { + 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); + } + 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,97 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() requires(!(simple-view && ...)) +// constexpr auto end() const requires(range&&...) + +#include + +#include "test_macros.h" +#include "test_range.h" +#include "types.h" + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[1] = {1}; + int buffer3[3] = {1, 2, 3}; + { + // underlying simple non-common non-sized (zip non-common) + std::ranges::zip_view v(SimpleNonCommon{buffer3}, SimpleCommon(buffer1)); + static_assert(!std::ranges::common_range); + assert(v.begin() + 3 == v.end()); + static_assert(std::same_as); + } + { + // underlying simple non-common non-random_access (zip non-common) + std::ranges::zip_view v(SimpleNonCommonNonRandom{buffer2}, SimpleCommon(buffer1)); + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::same_as); + } + { + // underlying simple non-common random_access sized (zip common) + std::ranges::zip_view v(SimpleNonCommonSized{buffer3}, SimpleCommon(buffer1)); + static_assert(std::ranges::common_range); + assert(v.begin() + 3 == v.end()); + static_assert(std::same_as); + } + { + // underlying simple common non-random_access non-sized (zip common) + std::ranges::zip_view v(SimpleCommonNonRandom{buffer2}, SimpleCommon(buffer1)); + static_assert(std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(std::same_as); + } + { + // underlying non-simple non-common non-sized (zip non-common) + std::ranges::zip_view v(NonSimpleNonCommon{buffer3}, SimpleCommon(buffer1)); + static_assert(!std::ranges::common_range); + assert(v.begin() + 3 == v.end()); + static_assert(!std::same_as); + } + { + // underlying non-simple non-common non-random_access (zip non-common) + std::ranges::zip_view v(NonSimpleNonCommonNonRandom{buffer2}, SimpleCommon(buffer1)); + static_assert(!std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::same_as); + } + { + // underlying non-simple non-common random_access sized (zip common) + std::ranges::zip_view v(NonSimpleNonCommonSized{buffer3}, SimpleCommon(buffer1)); + static_assert(std::ranges::common_range); + assert(v.begin() + 3 == v.end()); + static_assert(!std::same_as); + } + { + // underlying non-simple ommon non-crandom_access non-sized (zip common) + std::ranges::zip_view v(NonSimpleCommonNonRandom{buffer2}, SimpleCommon(buffer1)); + static_assert(std::ranges::common_range); + assert(++v.begin() == v.end()); + static_assert(!std::same_as); + } + { + // end should go to the minimum length + 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); + } + 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 "test_macros.h" +#include "test_iterators.h" + +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()); + } +} 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,90 @@ +//===----------------------------------------------------------------------===// +// +// 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; +// x -= n; +// x - n; +// x - y; + +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + + std::array a{1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::array b{4.1, 3.2, 4.3, 0.1, 0.2}; + { + 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])); + + it1 += 3; + assert(it1 == it2); + auto [x1, y1] = *it2; + assert(&x1 == &(a[3])); + assert(&y1 == &(b[3])); + } + { + 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])); + } + { + 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 + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + 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); + } + 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,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 + +// zip_view::::operator{<,>,<=,>=,==,!=,<=>} + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "../types.h" + +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)); + + // clang-format off + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + // clang-format on + } + + { + // 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)); + } + { + // 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); + } + 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,59 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#include "../types.h" + +struct Default : std::ranges::view_base { + int* begin() const; + int* 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>; + +constexpr bool test() { + { + using It = zip_iter; + static_assert(!std::default_initializable); + } + { + using It = zip_iter; + static_assert(!std::default_initializable); + } + + { + using It = zip_iter; + [[maybe_unused]] It it; + static_assert(std::default_initializable); + } + { + using It = zip_iter; + [[maybe_unused]] It it; + static_assert(std::default_initializable); + } + 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 "test_macros.h" +#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::same_as); + + // 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,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 + +// constexpr iterator& operator--(); +// constexpr iterator operator--(int); + +#include +#include +#include + +#include "test_macros.h" +#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 forward + std::ranges::zip_view v(a, b, std::views::iota(0, 5)); + auto it = v.end(); + using Iter = decltype(it); + + ASSERT_SAME_TYPE(decltype(--it), Iter&); + auto& it_ref = --it; + assert(&it_ref == &it); + + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(b[2])); + assert(std::get<2>(*it) == 2); + + ASSERT_SAME_TYPE(decltype(it--), Iter); + it--; + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(b[1])); + assert(std::get<2>(*it) == 1); + } + { + 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,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#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])); + ASSERT_SAME_TYPE(decltype(*it), std::tuple); + } + { + std::ranges::zip_view v(a, b); + auto it = v.begin(); + auto [x, y] = *it; + assert(&x == &(a[0])); + assert(&y == &(b[0])); + ASSERT_SAME_TYPE(decltype(*it), std::pair); + + 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); + ASSERT_SAME_TYPE(decltype(*it), std::tuple); + } + { + 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])); + ASSERT_SAME_TYPE(decltype(*it), std::pair); + } + 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,85 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#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}; + { + // all forward + 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); + + ASSERT_SAME_TYPE(decltype(++it), Iter&); + + auto& it_ref = ++it; + assert(&it_ref == &it); + + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(b[1])); + assert(std::get<2>(*it) == 1); + + ASSERT_SAME_TYPE(decltype(it++), Iter); + it++; + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(b[2])); + assert(std::get<2>(*it) == 2); + } + { + 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])); + + ASSERT_SAME_TYPE(decltype(++it), Iter&); + ++it; + assert(&(std::get<0>(*it)) == &(a[1])); + assert(&(std::get<1>(*it)) == &(buffer[1])); + + ASSERT_SAME_TYPE(decltype(it++), void); + it++; + assert(&(std::get<0>(*it)) == &(a[2])); + assert(&(std::get<1>(*it)) == &(buffer[2])); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/iter_move.pass.cpp @@ -0,0 +1,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 + +// friend constexpr decltype(auto) iter_move(const iterator& i); + +#include +#include +#include + +#include "test_macros.h" +#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)); + ASSERT_SAME_TYPE(decltype(std::ranges::iter_move(v.begin())), std::tuple); + + 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()))); + } + 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,52 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#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 = std::next(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))); + + 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,83 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Iterator traits and member typedefs in zip_view::. + +#include + +#include "test_iterators.h" +#include "test_macros.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; }; + +void test() { + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer); + using Iter = std::ranges::iterator_t; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::random_access_iterator_tag); + ASSERT_SAME_TYPE(Iter::iterator_category, std::input_iterator_tag); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, std::pair); + } + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer, buffer); + using Iter = std::ranges::iterator_t; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::random_access_iterator_tag); + ASSERT_SAME_TYPE(Iter::iterator_category, std::input_iterator_tag); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, std::tuple); + } + { + using Iter = std::ranges::iterator_t>>; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::forward_iterator_tag); + ASSERT_SAME_TYPE(Iter::iterator_category, std::input_iterator_tag); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, std::tuple); + } + { + int buffer[4]; + std::ranges::zip_view v(buffer, buffer); + std::ranges::zip_view v2(buffer, v); + using Iter = std::ranges::iterator_t; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::random_access_iterator_tag); + ASSERT_SAME_TYPE(Iter::iterator_category, std::input_iterator_tag); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, std::pair>); + } + { + using Iter = std::ranges::iterator_t>>; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::input_iterator_tag); + static_assert(!HasIterCategory); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, std::tuple); + } +} 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,39 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#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)); + + ASSERT_SAME_TYPE(decltype(it[2]), std::pair); + + 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.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,36 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#include "../types.h" + +constexpr bool test() { + using R = std::ranges::zip_view; + using Sentinel = std::ranges::sentinel_t; + [[maybe_unused]] Sentinel st1; + [[maybe_unused]] Sentinel st2 = {}; + + static_assert(!std::same_as>); + + 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,82 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#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 { + int* buffer_; + std::size_t size_; + + template + constexpr NonSimpleNonCommonConveritbleView(int (&b)[N]) : buffer_(b), size_(N) {} + + 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::same_as); + + assert(v.begin() != sent2); + assert(std::as_const(v).begin() != sent2); + assert(v.begin() + 4 == sent2); + assert(std::as_const(v).begin() + 4 == sent2); + + // 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/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,125 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#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_)); } +}; + +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; + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + } + { + 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::same_as); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::same_as); + + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + LIBCPP_STATIC_ASSERT(!std::__weakly_equality_comparable_with); + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + } + { + + 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::same_as); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::same_as); + + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + LIBCPP_STATIC_ASSERT(std::__weakly_equality_comparable_with); + } + 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,75 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#include "test_iterators.h" +#include "../types.h" + +template +concept SentinelMinusable = requires(T t) { t.end() - t.begin(); }; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + + { + std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)}; + static_assert(!std::ranges::common_range); + 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); + } + + { + 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); + } + + { + std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1)); + static_assert(!std::ranges::common_range); + static_assert(!SentinelMinusable); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/sized.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/sized.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/sized.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_iterators.h" +#include "test_macros.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_; } +}; + +constexpr bool test() { + { + std::ranges::zip_view v(View(8)); + assert(v.size() == 8); + } + { + std::ranges::zip_view v(View(2), View(3)); + assert(v.size() == 2); + } + { + std::ranges::zip_view v(std::views::iota(0, 500), View(3)); + assert(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); + } + 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,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 +// +//===----------------------------------------------------------------------===// + +#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" + +struct IntBuffer { + int* buffer_; + std::size_t size_; + + template + constexpr IntBuffer(int (&b)[N]) : buffer_(b), size_(N) {} +}; + +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; + +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 NonSimpleNonCommonSized = NonCommonSized; + +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); + +struct forward_sized_iterator { + int* it_ = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + using pointer = int*; + using reference = int&; + + forward_sized_iterator() = default; + constexpr forward_sized_iterator(int* 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); + +struct ForwardSizedView : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr forward_sized_iterator begin() const { return buffer_; } + constexpr forward_sized_iterator end() const { return buffer_ + size_; } +}; + +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); + +struct ForwardSizedNonCommon : std::ranges::view_base, IntBuffer { + using IntBuffer::IntBuffer; + constexpr forward_sized_iterator begin() const { return buffer_; } + constexpr sized_sentinel end() const { + return sized_sentinel(buffer_ + size_); + } +}; + +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); + +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_)); } +}; +static_assert(std::ranges::view); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H