diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index cade192260f6d..15bf46d44b07f 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -38,6 +38,7 @@ What's New in Libc++ 22.0.0? Implemented Papers ------------------ +- P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv index e5b2dcf8c1a5b..189f8452e0678 100644 --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -170,7 +170,7 @@ "`LWG3687 `__","``expected`` move constructor should move","2022-07 (Virtual)","|Complete|","16","" "`LWG3692 `__","``zip_view::iterator``'s ``operator<=>`` is overconstrained","2022-07 (Virtual)","|Complete|","20","" "`LWG3701 `__","Make ``formatter, charT>`` requirement explicit","2022-07 (Virtual)","|Complete|","15","" -"`LWG3702 `__","Should ``zip_transform_view::iterator`` remove ``operator<``","2022-07 (Virtual)","","","" +"`LWG3702 `__","Should ``zip_transform_view::iterator`` remove ``operator<``","2022-07 (Virtual)","|Complete|","22","" "`LWG3703 `__","Missing requirements for ``expected`` requires ``is_void``","2022-07 (Virtual)","|Complete|","16","" "`LWG3704 `__","LWG 2059 added overloads that might be ill-formed for sets","2022-07 (Virtual)","","","" "`LWG3705 `__","Hashability shouldn't depend on basic_string's allocator","2022-07 (Virtual)","|Complete|","16","" @@ -222,7 +222,7 @@ "`LWG3765 `__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","" "`LWG3766 `__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","" "`LWG3770 `__","``const_sentinel_t`` is missing","2022-11 (Kona)","","","" -"`LWG3773 `__","``views::zip_transform`` still requires ``F`` to be ``copy_constructible`` when empty pack","2022-11 (Kona)","","","" +"`LWG3773 `__","``views::zip_transform`` still requires ``F`` to be ``copy_constructible`` when empty pack","2022-11 (Kona)","|Complete|","22","" "`LWG3774 `__","```` should include ````","2022-11 (Kona)","","","" "`LWG3775 `__","Broken dependencies in the ``Cpp17Allocator`` requirements","2022-11 (Kona)","","","" "`LWG3778 `__","``vector`` missing exception specifications","2022-11 (Kona)","|Complete|","3.7","" @@ -234,7 +234,7 @@ "`LWG3792 `__","``__cpp_lib_constexpr_algorithms`` should also be defined in ````","2022-11 (Kona)","|Complete|","16","" "`LWG3795 `__","Self-move-assignment of ``std::future`` and ``std::shared_future`` have unimplementable postconditions","2022-11 (Kona)","","","" "`LWG3796 `__","``movable-box`` as member should use ``default-initialization`` instead of ``copy-initialization``","2022-11 (Kona)","","","" -"`LWG3798 `__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Partial|","","``join_with_view``, ``zip_transform_view``, and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet" +"`LWG3798 `__","Rvalue reference and ``iterator_category``","2022-11 (Kona)","|Partial|","","``adjacent_transform_view`` hasn't been done yet since this type isn't implemented yet" "`LWG3801 `__","``cartesian_product_view::iterator::distance-from`` ignores the size of last underlying range","2022-11 (Kona)","","","" "`LWG3814 `__","Add freestanding items requested by NB comments","2022-11 (Kona)","","","" "`LWG3816 `__","``flat_map`` and ``flat_multimap`` should impose sequence container requirements","2022-11 (Kona)","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 25b567df2dd33..80d8ce422064a 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -737,6 +737,7 @@ set(files __ranges/transform_view.h __ranges/view_interface.h __ranges/views.h + __ranges/zip_transform_view.h __ranges/zip_view.h __split_buffer __std_mbstate_t.h diff --git a/libcxx/include/__ranges/zip_transform_view.h b/libcxx/include/__ranges/zip_transform_view.h new file mode 100644 index 0000000000000..07aa182f2858f --- /dev/null +++ b/libcxx/include/__ranges/zip_transform_view.h @@ -0,0 +1,357 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H +#define _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H + +#include <__config> + +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/derived_from.h> +#include <__concepts/equality_comparable.h> +#include <__concepts/invocable.h> +#include <__functional/invoke.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iterator_traits.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty_view.h> +#include <__ranges/movable_box.h> +#include <__ranges/view_interface.h> +#include <__ranges/zip_view.h> +#include <__type_traits/decay.h> +#include <__type_traits/invoke.h> +#include <__type_traits/is_object.h> +#include <__type_traits/is_reference.h> +#include <__type_traits/is_referenceable.h> +#include <__type_traits/maybe_const.h> +#include <__type_traits/remove_cvref.h> +#include <__utility/forward.h> +#include <__utility/in_place.h> +#include <__utility/move.h> +#include // for std::apply + +#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 >= 23 + +namespace ranges { + +template + requires(view<_Views> && ...) && + (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> && + __referenceable...>> +class zip_transform_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS zip_view<_Views...> __zip_; + _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Fn> __fun_; + + using _InnerView _LIBCPP_NODEBUG = zip_view<_Views...>; + template + using __ziperator _LIBCPP_NODEBUG = iterator_t<__maybe_const<_Const, _InnerView>>; + template + using __zentinel _LIBCPP_NODEBUG = sentinel_t<__maybe_const<_Const, _InnerView>>; + + template + class __iterator; + + template + class __sentinel; + +public: + _LIBCPP_HIDE_FROM_ABI zip_transform_view() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit zip_transform_view(_Fn __fun, _Views... __views) + : __zip_(std::move(__views)...), __fun_(in_place, std::move(__fun)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator(*this, __zip_.begin()); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range && regular_invocable...> + { + return __iterator(*this, __zip_.begin()); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() { + if constexpr (common_range<_InnerView>) { + return __iterator(*this, __zip_.end()); + } else { + return __sentinel(__zip_.end()); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range && regular_invocable...> + { + if constexpr (common_range) { + return __iterator(*this, __zip_.end()); + } else { + return __sentinel(__zip_.end()); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_InnerView> + { + return __zip_.size(); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return __zip_.size(); + } +}; + +template +zip_transform_view(_Fn, _Ranges&&...) -> zip_transform_view<_Fn, views::all_t<_Ranges>...>; + +template +struct __zip_transform_iterator_category_base {}; + +template + requires forward_range<__maybe_const<_Const, zip_view<_Views...>>> +struct __zip_transform_iterator_category_base<_Const, _Fn, _Views...> { +private: + template + using __tag _LIBCPP_NODEBUG = typename iterator_traits>>::iterator_category; + + static consteval auto __get_iterator_category() { + if constexpr (!is_reference_v&, + range_reference_t<__maybe_const<_Const, _Views>>...>>) { + return input_iterator_tag(); + } else if constexpr ((derived_from<__tag<_Views>, random_access_iterator_tag> && ...)) { + return random_access_iterator_tag(); + } else if constexpr ((derived_from<__tag<_Views>, bidirectional_iterator_tag> && ...)) { + return bidirectional_iterator_tag(); + } else if constexpr ((derived_from<__tag<_Views>, forward_iterator_tag> && ...)) { + return forward_iterator_tag(); + } else { + return input_iterator_tag(); + } + } + +public: + using iterator_category = decltype(__get_iterator_category()); +}; + +template + requires(view<_Views> && ...) && + (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> && + __referenceable...>> +template +class zip_transform_view<_Fn, _Views...>::__iterator + : public __zip_transform_iterator_category_base<_Const, _Fn, _Views...> { + using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, zip_transform_view>; + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _InnerView>; + + friend zip_transform_view<_Fn, _Views...>; + + _Parent* __parent_ = nullptr; + __ziperator<_Const> __inner_; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, __ziperator<_Const> __inner) + : __parent_(std::addressof(__parent)), __inner_(std::move(__inner)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr auto __get_deref_and_invoke() const noexcept { + return [&__fun = *__parent_->__fun_](const auto&... __iters) noexcept(noexcept(std::invoke( + *__parent_->__fun_, *__iters...))) -> decltype(auto) { return std::invoke(__fun, *__iters...); }; + } + +public: + using iterator_concept = typename __ziperator<_Const>::iterator_concept; + using value_type = + remove_cvref_t&, range_reference_t<__maybe_const<_Const, _Views>>...>>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI __iterator() = default; + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && convertible_to<__ziperator, __ziperator<_Const>> + : __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const + noexcept(noexcept(std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)))) { + return std::apply(__get_deref_and_invoke(), __zip_view_iterator_access::__get_underlying(__inner_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + ++__inner_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires forward_range<_Base> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + --__inner_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires bidirectional_range<_Base> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x) + requires random_access_range<_Base> + { + __inner_ += __x; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x) + requires random_access_range<_Base> + { + __inner_ -= __x; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const + requires random_access_range<_Base> + { + return std::apply( + [&](const _Is&... __iters) -> decltype(auto) { + return std::invoke(*__parent_->__fun_, __iters[iter_difference_t<_Is>(__n)]...); + }, + __zip_view_iterator_access::__get_underlying(__inner_)); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) + requires equality_comparable<__ziperator<_Const>> + { + return __x.__inner_ == __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__inner_ <=> __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + return __iterator(*__i.__parent_, __i.__inner_ + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires random_access_range<_Base> + { + return __iterator(*__i.__parent_, __i.__inner_ + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + return __iterator(*__i.__parent_, __i.__inner_ - __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires sized_sentinel_for<__ziperator<_Const>, __ziperator<_Const>> + { + return __x.__inner_ - __y.__inner_; + } +}; + +template + requires(view<_Views> && ...) && + (sizeof...(_Views) > 0) && is_object_v<_Fn> && regular_invocable<_Fn&, range_reference_t<_Views>...> && + __referenceable...>> +template +class zip_transform_view<_Fn, _Views...>::__sentinel { + __zentinel<_Const> __inner_; + + friend zip_transform_view<_Fn, _Views...>; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(__zentinel<_Const> __inner) : __inner_(__inner) {} + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __i) + requires _Const && convertible_to<__zentinel, __zentinel<_Const>> + : __inner_(__i.__inner_) {} + + template + requires sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__inner_ == __y.__inner_; + } + + template + requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__inner_ - __y.__inner_; + } + + template + requires sized_sentinel_for<__zentinel<_Const>, __ziperator<_OtherConst>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>> + operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) { + return __x.__inner_ - __y.__inner_; + } +}; + +namespace views { +namespace __zip_transform { + +struct __fn { + template + requires(move_constructible> && regular_invocable&> && + is_object_v&>>) + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&&) const + noexcept(noexcept(auto(views::empty&>>>))) { + return views::empty&>>>; + } + + template + requires(sizeof...(_Ranges) > 0) + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Fn&& __fun, _Ranges&&... __rs) const + noexcept(noexcept(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...))) + -> decltype(zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...)) { + return zip_transform_view(std::forward<_Fn>(__fun), std::forward<_Ranges>(__rs)...); + } +}; + +} // namespace __zip_transform +inline namespace __cpo { +inline constexpr auto zip_transform = __zip_transform::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ZIP_TRANSFORM_VIEW_H diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h index e2a194efcfb4c..ce00c98710c4e 100644 --- a/libcxx/include/__ranges/zip_view.h +++ b/libcxx/include/__ranges/zip_view.h @@ -235,6 +235,13 @@ struct __zip_view_iterator_category_base<_Const, _Views...> { using iterator_category = input_iterator_tag; }; +struct __zip_view_iterator_access { + template + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_underlying(_Iter& __iter) noexcept { + return (__iter.__current_); + } +}; + template requires(view<_Views> && ...) && (sizeof...(_Views) > 0) template @@ -255,6 +262,7 @@ class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base static constexpr bool __is_zip_view_iterator = true; friend struct __product_iterator_traits<__iterator>; + friend __zip_view_iterator_access; public: using iterator_concept = decltype(ranges::__get_zip_view_iterator_tag<_Const, _Views...>()); diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 78607f2c1301d..0a479e043e9c3 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1943,6 +1943,9 @@ module std [system] { header "__ranges/zip_view.h" export std.utility.pair } + module zip_transform_view { + header "__ranges/zip_transform_view.h" + } header "ranges" export * diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 2a6321bd2c5d8..96d7a6b897188 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -339,6 +339,16 @@ namespace std::ranges { namespace views { inline constexpr unspecified zip = unspecified; } // C++23 + // [range.zip.transform], zip transform view + template + requires (view && ...) && (sizeof...(Views) > 0) && is_object_v && + regular_invocable...> && + can-reference...>> + class zip_transform_view; // C++23 + + namespace views { inline constexpr unspecified zip_transform = unspecified; } // C++23 + + // [range.as.rvalue] template requires input_range @@ -439,6 +449,7 @@ namespace std { # include <__ranges/join_with_view.h> # include <__ranges/repeat_view.h> # include <__ranges/to.h> +# include <__ranges/zip_transform_view.h> # include <__ranges/zip_view.h> # endif diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index adabeeb22d551..7ede42e4f7b0a 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -289,16 +289,16 @@ export namespace std { namespace views { using std::ranges::views::zip; } // namespace views -#endif // _LIBCPP_STD_VER >= 23 -#if 0 // [range.zip.transform], zip transform view using std::ranges::zip_transform_view; namespace views { using std::ranges::views::zip_transform; } +#endif // _LIBCPP_STD_VER >= 23 +#if 0 using std::ranges::adjacent_view; namespace views { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp new file mode 100644 index 0000000000000..4b66862660072 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.zip.transform/no_unique_address.compile.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// XFAIL: msvc + +// This test ensures that we use `[[no_unique_address]]` in `zip_transform_view`. + +#include + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct Pred { + template + bool operator()(const Args&...) const; +}; + +template +struct Test { + [[no_unique_address]] View view; + char c; +}; + +static_assert(sizeof(std::ranges::zip_transform_view) == 1); diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp index 3f4317a724add..4e24dbe810165 100644 --- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp @@ -18,6 +18,8 @@ #include #include +#include "test_macros.h" + // Test for basic properties of C++20 16.3.3.3.6 [customization.point.object]. template constexpr bool test(CPO& o, Args&&...) { @@ -26,7 +28,7 @@ constexpr bool test(CPO& o, Args&&...) { static_assert(std::is_trivially_copyable_v); static_assert(std::is_trivially_default_constructible_v); - auto p = o; + auto p = o; using T = decltype(p); // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular. @@ -89,11 +91,15 @@ static_assert(test(std::views::counted, a, 10)); static_assert(test(std::views::drop, a, 10)); //static_assert(test(std::views::drop_while, a, [](int x){ return x < 10; })); //static_assert(test(std::views::elements<0>, pairs)); -static_assert(test(std::views::filter, a, [](int x){ return x < 10; })); +static_assert(test(std::views::filter, a, [](int x) { return x < 10; })); static_assert(test(std::views::join, arrays)); //static_assert(test(std::views::split, a, 4)); static_assert(test(std::views::lazy_split, a, 4)); static_assert(test(std::views::reverse, a)); static_assert(test(std::views::take, a, 10)); //static_assert(test(std::views::take_while, a, [](int x){ return x < 10; })); -static_assert(test(std::views::transform, a, [](int x){ return x + 1; })); +static_assert(test(std::views::transform, a, [](int x) { return x + 1; })); + +#if TEST_STD_VER >= 23 +static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a)); +#endif diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp new file mode 100644 index 0000000000000..9b326e1cd6981 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/begin.pass.cpp @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto begin(); +// constexpr auto begin() const +// requires range && +// regular_invocable...>; + +#include + +#include +#include + +#include "types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + // all underlying iterators should be at the begin position + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + auto it = v.begin(); + assert(*it == std::make_tuple(1, 0, 2.0)); + + auto const_it = std::as_const(v).begin(); + assert(*const_it == *it); + + static_assert(!std::same_as); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + assert(*it == std::make_tuple(1)); + auto cit = std::as_const(v).begin(); + assert(*cit == std::make_tuple(1)); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(&*it == &buffer[0]); + auto cit = std::as_const(v).begin(); + assert(&*cit == &buffer[0]); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + auto it = v.begin(); + assert(&std::get<0>(*it) == &buffer[0]); + assert(&std::get<1>(*it) == &buffer[0]); + assert(std::get<2>(*it) == 2.0); + auto cit = std::as_const(v).begin(); + assert(&std::get<0>(*cit) == &buffer[0]); + assert(&std::get<1>(*cit) == &buffer[0]); + assert(std::get<2>(*cit) == 2.0); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer}, SimpleCommon{buffer}); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), SimpleCommon{buffer}); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // underlying const R is not a range + using ZTV = std::ranges::zip_transform_view; + static_assert(HasBegin); + static_assert(!HasConstBegin); + } + + { + // Fn cannot be invoked on const range + using ZTV = std::ranges::zip_transform_view; + static_assert(HasBegin); + static_assert(!HasConstBegin); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp new file mode 100644 index 0000000000000..4a0bf7b8be1ac --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/cpo.pass.cpp @@ -0,0 +1,159 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::views::zip_transform + +#include + +#include +#include +#include +#include +#include +#include + +#include "types.h" + +struct NotMoveConstructible { + NotMoveConstructible() = default; + NotMoveConstructible(NotMoveConstructible&&) = delete; + int operator()() const { return 5; } +}; + +struct NotCopyConstructible { + NotCopyConstructible() = default; + NotCopyConstructible(NotCopyConstructible&&) = default; + NotCopyConstructible(const NotCopyConstructible&) = delete; + int operator()() const { return 5; } +}; + +struct NotInvocable {}; + +template +struct Invocable { + int operator()(Args...) const { return 5; } +}; + +struct ReturnNotObject { + void operator()() const {} +}; + +// LWG3773 views::zip_transform still requires F to be copy_constructible when empty pack +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>); +static_assert(!std::is_invocable_v); + +static_assert(std::is_invocable_v, // + std::ranges::iota_view>); +static_assert(!std::is_invocable_v, // + std::ranges::iota_view>); +static_assert(!std::is_invocable_v, + std::ranges::iota_view, + std::ranges::iota_view>); +static_assert(std::is_invocable_v, + std::ranges::iota_view, + std::ranges::iota_view>); + +constexpr bool test() { + { + // zip_transform function with no ranges + auto v = std::views::zip_transform(Invocable<>{}); + assert(std::ranges::empty(v)); + static_assert(std::is_same_v>); + } + + { + // zip_transform views + int buffer1[] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[] = {9, 10, 11, 12}; + auto view1 = std::views::all(buffer1); + auto view2 = std::views::all(buffer2); + std::same_as, decltype(view1), decltype(view2)>> decltype(auto) v = + std::views::zip_transform(std::plus{}, buffer1, buffer2); + assert(std::ranges::size(v) == 4); + auto expected = {10, 12, 14, 16}; + assert(std::ranges::equal(v, expected)); + static_assert(std::is_same_v, int>); + } + + { + // zip_transform a viewable range + std::array a{1, 2, 3}; + auto id = [](auto& x) -> decltype(auto) { return (x); }; + std::same_as< + std::ranges::zip_transform_view>>> decltype(auto) v = + std::views::zip_transform(id, a); + assert(&v[0] == &a[0]); + static_assert(std::is_same_v, int&>); + } + + int buffer[] = {1, 2, 3}; + { + // one range + auto v = std::views::zip_transform(MakeTuple{}, SimpleCommon{buffer}); + assert(std::ranges::equal(v, std::vector{std::tuple(1), std::tuple(2), std::tuple(3)})); + } + + { + // two ranges + auto v = std::views::zip_transform(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + assert(std::ranges::equal(v, std::vector{1, 2, 3})); + } + + { + // three ranges + auto v = std::views::zip_transform(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + assert(std::ranges::equal(v, std::vector{std::tuple(1, 1, 2.0)})); + } + + { + // single empty range + auto v = std::views::zip_transform(MakeTuple{}, std::ranges::empty_view()); + assert(std::ranges::empty(v)); + } + + { + // empty range at the beginning + auto v = std::views::zip_transform( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer}, SimpleCommon{buffer}); + assert(std::ranges::empty(v)); + } + + { + // empty range in the middle + auto v = std::views::zip_transform( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), SimpleCommon{buffer}); + assert(std::ranges::empty(v)); + } + + { + // empty range at the end + auto v = std::views::zip_transform( + MakeTuple{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::empty_view()); + assert(std::ranges::empty(v)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctad.compile.pass.cpp new file mode 100644 index 0000000000000..9254dd18da4be --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/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 + +// template +// zip_transform_view(F, Rs&&...) -> zip_transform_view...>; + +#include +#include + +#include "types.h" + +struct Container { + int* begin() const; + int* end() const; +}; + +struct Fn { + int operator()(auto&&...) const { return 5; } +}; + +void testCTAD() { + static_assert(std::is_same_v>>); + + static_assert(std::is_same_v, IntView>>); + + Container c{}; + static_assert( + std::is_same_v< + decltype(std::ranges::zip_transform_view(Fn{}, Container{}, IntView{}, c)), + std::ranges:: + zip_transform_view, IntView, std::ranges::ref_view>>); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp new file mode 100644 index 0000000000000..751210f2a4f1f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.default.pass.cpp @@ -0,0 +1,147 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// zip_transform_view() = default; + +#include + +#include +#include + +#include "types.h" + +constexpr int buff[] = {1, 2, 3}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NonDefaultConstructibleView : std::ranges::view_base { + NonDefaultConstructibleView() = delete; + int* begin() const; + int* end() const; +}; + +struct DefaultConstructibleFn { + constexpr int operator()(const auto&... x) const { return (x + ...); } +}; + +struct NonDefaultConstructibleFn { + NonDefaultConstructibleFn() = delete; + constexpr int operator()(const auto&... x) const; +}; + +// The default constructor requires all underlying views to be default constructible. +// It is implicitly required by the zip_view's constructor. +static_assert(std::is_default_constructible_v>); +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_transform_view; + View v = View(); // the default constructor is not explicit + assert(v.size() == 3); + auto it = v.begin(); + assert(*it++ == 2); + assert(*it++ == 4); + assert(*it == 6); + } + + { + // one range + using View = std::ranges::zip_transform_view; + View v = View(); // the default constructor is not explicit + auto it = v.begin(); + assert(*it == std::make_tuple(1)); + } + + { + // two ranges + using View = std::ranges::zip_transform_view>; + View v = View(); // the default constructor is not explicit + auto it = v.begin(); + assert(*it == std::tuple(1, 0)); + } + + { + // three ranges + using View = std::ranges:: + zip_transform_view>; + View v = View(); // the default constructor is not explicit + auto it = v.begin(); + assert(*it == std::tuple(1, 1, 0)); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the beginning + using View = std::ranges:: + zip_transform_view, DefaultConstructibleView, DefaultConstructibleView>; + View v = View(); // the default constructor is not explicit + assert(v.empty()); + } + + { + // empty range in the middle + using View = + std::ranges::zip_transform_view, + DefaultConstructibleView, + DefaultConstructibleView>; + View v = View(); // the default constructor is not explicit + assert(v.empty()); + } + + { + // empty range at the end + using View = std::ranges:: + zip_transform_view>; + View v = View(); // the default constructor is not explicit + assert(v.empty()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp new file mode 100644 index 0000000000000..5f3b5a3ff6f99 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/ctor.views.pass.cpp @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr explicit zip_transform_view(F, Views...) + +#include +#include +#include + +#include "types.h" + +struct Fn { + int operator()(auto&&...) const { return 5; } +}; + +template +concept IsImplicitlyConstructible = requires(T val, Args... args) { val = {std::forward(args)...}; }; + +// test constructor is explicit +static_assert(std::constructible_from, Fn, IntView>); +static_assert(!IsImplicitlyConstructible, Fn, IntView>); + +static_assert(std::constructible_from, Fn, IntView, IntView>); +static_assert(!IsImplicitlyConstructible, Fn, IntView, IntView>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +template +constexpr void constructorTest(auto&& buffer1, auto&& buffer2) { + std::ranges::zip_transform_view v{MakeTuple{}, View1{buffer1}, View2{buffer2}}; + auto [i, j] = *v.begin(); + assert(i == buffer1[0]); + assert(j == buffer2[0]); +}; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[4] = {9, 8, 7, 6}; + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer2}); + assert(std::ranges::equal(v, std::vector{std::tuple(9), std::tuple(8), std::tuple(7), std::tuple(6)})); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + assert(std::ranges::equal(v, std::vector{1, 2, 3, 4, 5, 6, 7, 8})); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer2}, std::ranges::single_view(2.)); + assert(std::ranges::equal(v, std::vector{std::tuple(1, 9, 2.0)})); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + assert(std::ranges::empty(v)); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer}, SimpleCommon{buffer}); + assert(std::ranges::empty(v)); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), SimpleCommon{buffer}); + assert(std::ranges::empty(v)); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::empty_view()); + assert(std::ranges::empty(v)); + } + { + // constructor from views + std::ranges::zip_transform_view v( + MakeTuple{}, SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + auto [i, j, k] = *v.begin(); + assert(i == 1); + assert(j == 0); + assert(k == 2.0); + } + + { + // arguments are moved once + MoveAwareView mv; + std::ranges::zip_transform_view v{MakeTuple{}, std::move(mv), MoveAwareView{}}; + auto [numMoves1, numMoves2] = *v.begin(); + assert(numMoves1 == 3); // one move from the local variable to parameter, one move from parameter to member + assert(numMoves2 == 2); + } + + // input and forward + { + constructorTest(buffer, buffer2); + } + + // bidi and random_access + { + constructorTest(buffer, buffer2); + } + + // contiguous + { + constructorTest(buffer, buffer2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp new file mode 100644 index 0000000000000..e6c7094e7d720 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/end.pass.cpp @@ -0,0 +1,147 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto end() +// constexpr auto end() const +// requires range && +// regular_invocable...>; + +#include + +#include "types.h" + +template +concept HasConstEnd = requires(const T& ct) { ct.end(); }; + +template +concept HasEnd = requires(T& t) { t.end(); }; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + // simple test + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::views::iota(0), std::ranges::single_view(2.)); + assert(v.begin() != v.end()); + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(v.begin() + 1 == v.end()); + assert(std::as_const(v).begin() + 1 == std::as_const(v).end()); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + assert(it + 8 == v.end()); + assert(it + 8 == std::as_const(v).end()); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(it + 8 == v.end()); + assert(it + 8 == std::as_const(v).end()); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + auto it = v.begin(); + assert(it + 1 == v.end()); + assert(it + 1 == std::as_const(v).end()); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer}, SimpleCommon{buffer}); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), SimpleCommon{buffer}); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // common_range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + + static_assert(!std::same_as); + static_assert(!std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + + assert(it + 8 == st); + assert(const_it + 8 == const_st); + } + { + // !common_range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleNonCommon{buffer}); + auto it = v.begin(); + auto const_it = std::as_const(v).begin(); + auto st = v.end(); + auto const_st = std::as_const(v).end(); + + static_assert(!std::same_as); + static_assert(!std::same_as); + static_assert(!std::same_as); + static_assert(!std::same_as); + + assert(it + 8 == st); + assert(const_it + 8 == const_st); + } + + { + // underlying const R is not a range + using ZTV = std::ranges::zip_transform_view; + static_assert(HasEnd); + static_assert(!HasConstEnd); + } + + { + // Fn cannot invoke on const range + using ZTV = std::ranges::zip_transform_view; + static_assert(HasEnd); + static_assert(!HasConstEnd); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp new file mode 100644 index 0000000000000..3c35de27deb69 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/general.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// Some basic examples of how zip_tranform_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include + +#include +#include +#include +#include + +int main(int, char**) { + std::vector v1 = {1, 2}; + std::vector v2 = {4, 5, 6}; + auto ztv = std::views::zip_transform(std::plus(), v1, v2); + auto expected = {5, 7}; + assert(std::ranges::equal(ztv, expected)); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp new file mode 100644 index 0000000000000..d697ae571cb7d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/arithmetic.pass.cpp @@ -0,0 +1,237 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator& operator+=(difference_type x) requires random_access_range; +// constexpr iterator& operator-=(difference_type x) requires random_access_range; +// friend constexpr iterator operator+(const iterator& i, difference_type n) +// requires random_access_range; +// friend constexpr iterator operator+(difference_type n, const iterator& i) +// requires random_access_range; +// friend constexpr iterator operator-(const iterator& i, difference_type n) +// requires random_access_range; +// friend constexpr difference_type operator-(const iterator& x, const iterator& y) +// requires sized_sentinel_for, ziperator>; + +#include + +#include +#include +#include + +#include "../types.h" + +template +concept canPlusEqual = requires(T& t, U& u) { t += u; }; + +template +concept canPlus = requires(T& t, U& u) { t + u; }; + +template +concept canMinusEqual = requires(T& t, U& u) { t -= u; }; + +template +concept canMinus = requires(T& t, U& u) { t - u; }; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + SizedRandomAccessView a{buffer1}; + static_assert(std::ranges::random_access_range); + + std::array b{4.1, 3.2, 4.3, 0.1, 0.2}; + static_assert(std::ranges::contiguous_range); + + { + // operator+(x, n) and operator+= + std::ranges::zip_transform_view v(MakeTuple{}, a, b); + auto it1 = v.begin(); + using Iter = decltype(it1); + + std::same_as decltype(auto) it2 = it1 + 3; + assert(*it2 == std::tuple(4, 0.1)); + + std::same_as decltype(auto) it3 = 3 + it1; + assert(*it3 == std::tuple(4, 0.1)); + + std::same_as decltype(auto) it1_ref = it1 += 3; + assert(&it1_ref == &it1); + assert(*it1_ref == std::tuple(4, 0.1)); + assert(*it1 == std::tuple(4, 0.1)); + + static_assert(canPlus); + static_assert(canPlusEqual); + } + + { + // operator-(x, n) and operator-= + std::ranges::zip_transform_view v(MakeTuple{}, a, b); + auto it1 = v.end(); + using Iter = decltype(it1); + + std::same_as decltype(auto) it2 = it1 - 3; + assert(*it2 == std::tuple(3, 4.3)); + + std::same_as decltype(auto) it1_ref = it1 -= 3; + assert(&it1_ref == &it1); + assert(*it1_ref == std::tuple(3, 4.3)); + assert(*it1 == std::tuple(3, 4.3)); + + static_assert(canMinusEqual); + static_assert(canMinus); + } + + { + // operator-(x, y) + std::ranges::zip_transform_view v(MakeTuple{}, a, b); + assert((v.end() - v.begin()) == 5); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + + using Iter = decltype(it1); + + std::same_as> decltype(auto) n = it1 - it2; + assert(n == -2); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer1}); + auto it = v.begin(); + assert(*it == std::make_tuple(1)); + + it += 4; + assert(*it == std::make_tuple(5)); + + it -= 1; + assert(*it == std::make_tuple(4)); + + auto it2 = it - 2; + assert(*it2 == std::make_tuple(2)); + + auto it3 = 3 + it2; + assert(*it3 == std::make_tuple(5)); + + assert(it3 - it2 == 3); + } + + { + // two ranges + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer1}, std::views::iota(0)); + auto it = v.begin(); + assert(*it == std::make_tuple(1, 0)); + + it += 4; + assert(*it == std::make_tuple(5, 4)); + + it -= 1; + assert(*it == std::make_tuple(4, 3)); + + auto it2 = it - 2; + assert(*it2 == std::make_tuple(2, 1)); + + auto it3 = 3 + it2; + assert(*it3 == std::make_tuple(5, 4)); + + assert(it3 - it2 == 3); + } + + { + // three ranges + std::ranges::zip_transform_view v( + Tie{}, SimpleCommon{buffer1}, SimpleCommon{buffer1}, std::ranges::single_view(2.)); + auto it = v.begin(); + assert(*it == std::make_tuple(1, 1, 2.0)); + + it += 1; + assert(it == v.end()); + + it -= 1; + assert(it == v.begin()); + + auto it2 = it + 1; + assert(it2 == v.end()); + + auto it3 = it2 - 1; + assert(it3 == v.begin()); + + assert(it3 - it2 == -1); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + auto it = v.begin(); + auto it2 = v.end(); + assert(it2 - it == 0); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer1}, SimpleCommon{buffer1}); + auto it = v.begin(); + auto it2 = v.end(); + assert(it2 - it == 0); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, std::ranges::empty_view(), SimpleCommon{buffer1}); + auto it = v.begin(); + auto it2 = v.end(); + assert(it2 - it == 0); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, SimpleCommon{buffer1}, std::ranges::empty_view()); + auto it = v.begin(); + auto it2 = v.end(); + assert(it2 - it == 0); + } + { + // One of the ranges is not random access + std::ranges::zip_transform_view v(MakeTuple{}, a, b, ForwardSizedView{buffer1}); + auto it1 = v.begin(); + using Iter = decltype(it1); + static_assert(!canPlus); + static_assert(!canPlus); + static_assert(!canPlusEqual); + static_assert(!canMinus); + static_assert(canMinus); + static_assert(!canMinusEqual); + + auto it2 = ++v.begin(); + assert((it2 - it1) == 1); + } + + { + // One of the ranges does not have sized sentinel + std::ranges::zip_transform_view v(MakeTuple{}, a, b, InputCommonView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!canPlus); + static_assert(!canPlus); + static_assert(!canPlusEqual); + static_assert(!canMinus); + static_assert(!canMinus); + static_assert(!canMinusEqual); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp new file mode 100644 index 0000000000000..2befb7e4cc58c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/compare.pass.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// friend constexpr bool operator==(const iterator& x, const iterator& y) +// requires equality_comparable>; + +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires random_access_range; + +#include + +#include + +#include "test_iterators.h" +#include "../types.h" + +constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) { + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); +} + +constexpr void spaceshipTest(auto&& iter1, auto&& iter2) { + using Iter = decltype(iter1); + static_assert(std::three_way_comparable); + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter2) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); +} + +constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) { + using Iter1 = decltype(iter1); + using Iter2 = decltype(iter2); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); +} + +constexpr bool test() { + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + using SubRange = std::ranges::subrange; + static_assert(std::three_way_comparable); + + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip_transform(MakeTuple{}, SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5))); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + using Iter = decltype(iter1); + static_assert(std::three_way_comparable); + compareOperatorTest(iter1, iter2); + spaceshipTest(iter1, iter2); + } + + { + // 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); + + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8, 9}; + auto r = std::views::zip_transform(MakeTuple{}, Subrange(It(a), It(a + 4)), Subrange(It(b), It(b + 5))); + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + spaceshipTest(iter1, iter2); + } + + int buffer[5] = {1, 2, 3, 4, 5}; + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + auto it2 = it + 3; + compareOperatorTest(it, it2); + spaceshipTest(it, it2); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin(); + auto it2 = it + 3; + compareOperatorTest(it, it2); + spaceshipTest(it, it2); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + auto it = v.begin(); + auto it2 = it + 1; + compareOperatorTest(it, it2); + spaceshipTest(it, it2); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + auto it = v.begin(); + auto it2 = v.end(); + assert(it == it2); + assert(it <=> it2 == std::strong_ordering::equal); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer}, SimpleCommon{buffer}); + auto it = v.begin(); + auto it2 = v.end(); + assert(it == it2); + assert(it <=> it2 == std::strong_ordering::equal); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), SimpleCommon{buffer}); + auto it = v.begin(); + auto it2 = v.end(); + assert(it == it2); + assert(it <=> it2 == std::strong_ordering::equal); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::empty_view()); + auto it = v.begin(); + auto it2 = v.end(); + assert(it == it2); + assert(it <=> it2 == std::strong_ordering::equal); + } + + { + // non random_access_range + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + + std::ranges::zip_transform_view v{MakeTuple{}, InputCommonView(buffer1), InputCommonView(buffer2)}; + using ZTV = decltype(v); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + static_assert(std::ranges::common_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // in this case sentinel is computed by getting each of the underlying sentinel, so only one + // underlying iterator is comparing equal + int buffer1[1] = {1}; + int buffer2[2] = {1, 2}; + std::ranges::zip_transform_view v{MakeTuple{}, ForwardSizedView(buffer1), ForwardSizedView(buffer2)}; + using ZTV = decltype(v); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::bidirectional_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + ++it1; + // it1: + // it2: + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // underlying iterator does not support == + using IterNoEqualView = BasicView, sentinel_wrapper>>; + int buffer2[] = {1}; + std::ranges::zip_transform_view r(MakeTuple{}, IterNoEqualView{buffer2}); + auto it = r.begin(); + using Iter = decltype(it); + static_assert(!std::invocable, Iter, Iter>); + inequalityOperatorsDoNotExistTest(it, it); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp new file mode 100644 index 0000000000000..8f8369d6af5f2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.default.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// iterator() = default; + +#include + +#include "../types.h" + +struct IterDefaultCtrView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct IterNoDefaultCtrView : std::ranges::view_base { + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +using 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>); + +template +constexpr void test() { + using ZipTransformIter = std::ranges::iterator_t>; + ZipTransformIter iter1 = {}; + ZipTransformIter iter2; + assert(iter1 == iter2); +} + +constexpr bool test() { + test(); + test>(); + test, std::ranges::single_view>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.pass.cpp new file mode 100644 index 0000000000000..c643d83a2c665 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/ctor.other.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 + +// constexpr iterator(iterator i) +// requires Const && convertible_to, ziperator>; + +#include +#include + +#include + +#include "../types.h" + +using ConstIterIncompatibleView = + BasicView, + forward_iterator, + random_access_iterator, + random_access_iterator>; +static_assert(!std::convertible_to, + std::ranges::iterator_t>); + +constexpr bool test() { + int buffer[3] = {1, 2, 3}; + + { + std::ranges::zip_transform_view v(MakeTuple{}, 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); + } + + { + // Check when we can't perform a non-const-to-const conversion of the ziperator + std::ranges::zip_transform_view v(MakeTuple{}, ConstIterIncompatibleView{buffer}); + auto iter1 = v.begin(); + auto iter2 = std::as_const(v).begin(); + + static_assert(!std::is_same_v); + + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(*iter2 == std::tuple(1)); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, NonSimpleCommon{buffer}, std::views::iota(0)); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(*iter2 == 1); + } + + { + // three ranges + std::ranges::zip_transform_view v( + Tie{}, NonSimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(*iter2 == std::tuple(1, 1, 2.0)); + } + + { + // single empty range + std::array buffer2{}; + std::ranges::zip_transform_view v(MakeTuple{}, buffer2); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(iter2 == v.end()); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), NonSimpleCommon{buffer}, SimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(iter2 == v.end()); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(iter2 == v.end()); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, NonSimpleCommon{buffer}, std::ranges::empty_view()); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + static_assert(!std::is_same_v); + assert(iter2 == v.end()); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp new file mode 100644 index 0000000000000..9f3647b82c820 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/decrement.pass.cpp @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator& operator--() requires bidirectional_range; +// constexpr iterator operator--(int) requires bidirectional_range; + +#include +#include +#include + +#include "../types.h" + +template +concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; }; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // all random access + std::ranges::zip_transform_view v(MakeTuple{}, a, b, std::views::iota(0, 5)); + auto it = v.end(); + using Iter = decltype(it); + static_assert(canDecrement); + + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + assert(*it == std::tuple(3, 4.3, 2)); + + auto original = it; + std::same_as decltype(auto) it2 = it--; + assert(original == it2); + assert(*it == std::tuple(2, 3.2, 1)); + } + + { + // all bidi+ + int buffer[2] = {1, 2}; + + std::ranges::zip_transform_view v(MakeTuple{}, BidiCommonView{buffer}, std::views::iota(0, 5)); + auto it = v.begin(); + using Iter = decltype(it); + static_assert(canDecrement); + + ++it; + ++it; + + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + assert(it == ++v.begin()); + assert(*it == std::tuple(2, 1)); + + auto original = it; + std::same_as decltype(auto) it2 = it--; + assert(original == it2); + assert(*it == std::tuple(1, 0)); + } + + { + // non bidi + int buffer[3] = {4, 5, 6}; + std::ranges::zip_transform_view v(MakeTuple{}, a, InputCommonView{buffer}); + using Iter = std::ranges::iterator_t; + static_assert(!canDecrement); + } + + int buffer[] = {1, 2, 3, 4, 5, 6}; + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.end(); + using Iter = decltype(it); + + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + assert(*it == std::tuple(6)); + + auto original = it; + std::same_as decltype(auto) it2 = it--; + assert(original == it2); + assert(*it == std::tuple(5)); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin() + 5; + using Iter = decltype(it); + + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + assert(*it == 5); + + auto original = it; + std::same_as decltype(auto) it2 = it--; + assert(original == it2); + assert(*it == 4); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + auto it = v.end(); + using Iter = decltype(it); + + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + assert(*it == std::tuple(1, 1, 2.0)); + + ++it; + + auto original = it; + std::same_as decltype(auto) it2 = it--; + assert(original == it2); + assert(*it == std::tuple(1, 1, 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.transform/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/deref.pass.cpp new file mode 100644 index 0000000000000..3c97e118a7700 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/deref.pass.cpp @@ -0,0 +1,143 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr decltype(auto) operator*() const noexcept(see below); + +#include +#include +#include + +#include "../types.h" + +// Test noexcept +// Remarks: Let Is be the pack 0, 1, ..., (sizeof...(Views)-1). The exception specification is equivalent to: +// noexcept(invoke(*parent_->fun_, *std::get(inner_.current_)...)). + +template +concept DerefNoexcept = requires(std::ranges::iterator_t iter) { requires noexcept(*iter); }; + +struct ThrowingDerefIter { + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = std::intptr_t; + + int operator*() const noexcept(false); + + ThrowingDerefIter& operator++(); + void operator++(int); + + friend constexpr bool operator==(const ThrowingDerefIter&, const ThrowingDerefIter&) = default; +}; + +using NoexceptDerefIter = int*; + +template +struct TestView : std::ranges::view_base { + using Iter = std::conditional_t; + Iter begin() const; + Iter end() const; +}; + +template +struct TestFn { + int operator()(auto&&...) const noexcept(NoExceptCall); +}; + +static_assert(DerefNoexcept, TestView>>); +static_assert(DerefNoexcept, TestView, TestView>>); +static_assert(!DerefNoexcept, TestView>>); +static_assert(!DerefNoexcept, TestView>>); +static_assert(!DerefNoexcept, TestView>>); +static_assert(!DerefNoexcept, TestView, TestView>>); +static_assert(!DerefNoexcept, TestView, TestView>>); +static_assert(!DerefNoexcept, TestView, TestView>>); + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{4.1, 3.2, 4.3}; + { + // Function returns reference + std::ranges::zip_transform_view v(GetFirst{}, a); + auto it = v.begin(); + std::same_as decltype(auto) val = *it; + assert(&val == &a[0]); + } + + { + // function returns PRValue + std::ranges::zip_transform_view v(MakeTuple{}, a, b); + auto it = v.begin(); + std::same_as> decltype(auto) val = *it; + assert(val == std::tuple(1, 4.1)); + } + + { + // operator* is const + std::ranges::zip_transform_view v(GetFirst{}, a); + const auto it = v.begin(); + std::same_as decltype(auto) val = *it; + assert(&val == &a[0]); + } + + { + // dereference twice + std::ranges::zip_transform_view v(MakeTuple{}, a, b); + auto it = v.begin(); + assert(*it == std::tuple(1, 4.1)); + assert(*it == std::tuple(1, 4.1)); + } + + { + // back and forth + std::ranges::zip_transform_view v(Tie{}, a, b); + auto it = v.begin(); + assert(&std::get<0>(*it) == &a[0]); + assert(&std::get<1>(*it) == &b[0]); + ++it; + assert(&std::get<0>(*it) == &a[1]); + assert(&std::get<1>(*it) == &b[1]); + --it; + assert(&std::get<0>(*it) == &a[0]); + assert(&std::get<1>(*it) == &b[0]); + } + + int buffer[] = {1, 2, 3, 4, 5, 6}; + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + assert(*it == std::make_tuple(1)); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(&*it == &buffer[0]); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::single_view(2.)); + auto it = v.begin(); + assert(&std::get<0>(*it) == &buffer[0]); + assert(&std::get<1>(*it) == &buffer[0]); + assert(std::get<2>(*it) == 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.transform/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.pass.cpp new file mode 100644 index 0000000000000..4a1ec74a66844 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/increment.pass.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int) requires forward_range;; + +#include +#include +#include + +#include "../types.h" + +struct InputRange : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = cpp20_input_iterator; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(iterator(buffer_ + size_)); } +}; + +template +constexpr void testForwardPlus() { + int buffer[] = {1, 2, 3, 4}; + + std::ranges::zip_transform_view v(GetFirst{}, View{buffer}, View{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(&(*it) == &(buffer[0])); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + assert(&(*it) == &(buffer[1])); + + static_assert(std::is_same_v); + auto original = it; + std::same_as decltype(auto) copy = it++; + assert(original == copy); + assert(&(*it) == &(buffer[2])); +} + +constexpr bool test() { + testForwardPlus(); + testForwardPlus(); + testForwardPlus(); + + { + // test input_range + int buffer[3] = {4, 5, 6}; + std::ranges::zip_transform_view v(MakeTuple{}, InputRange{buffer}, InputRange{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + assert(*it == std::tuple(4, 4)); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + assert(*it == std::tuple(5, 5)); + + static_assert(std::is_same_v); + it++; + assert(*it == std::tuple(6, 6)); + } + + int buffer[] = {1, 2, 3, 4, 5, 6}; + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + + assert(*it == std::tuple(2)); + + auto original = it; + std::same_as decltype(auto) it2 = it++; + assert(original == it2); + assert(*it == std::tuple(3)); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin(); + using Iter = decltype(it); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + + assert(*it == 2); + + auto original = it; + std::same_as decltype(auto) it2 = it++; + assert(original == it2); + assert(*it == 3); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::repeat_view(2.)); + auto it = v.begin(); + using Iter = decltype(it); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + + assert(*it == std::tuple(2, 2, 2.0)); + + auto original = it; + std::same_as decltype(auto) it2 = it++; + assert(original == it2); + assert(*it == std::tuple(3, 3, 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.transform/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp new file mode 100644 index 0000000000000..222f9e43974fb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/member_types.compile.pass.cpp @@ -0,0 +1,169 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// Iterator traits and member typedefs in zip_transform_view::iterator. + +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template +concept HasIterCategory = requires { typename T::iterator_category; }; + +template +struct DiffTypeIter { + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = T; + + int operator*() const; + DiffTypeIter& operator++(); + void operator++(int); + friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default; +}; + +template +struct DiffTypeRange { + DiffTypeIter begin() const; + DiffTypeIter end() const; +}; + +struct Foo {}; +struct Bar {}; + +struct RValueRefFn { + int&& operator()(auto&&...) const; +}; + +void test() { + int buffer[] = {1, 2, 3, 4}; + { + // C++20 random_access C++17 random_access + std::ranges::zip_transform_view v(GetFirst{}, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); + } + + { + // C++20 random_access C++17 input + std::ranges::zip_transform_view v(MakeTuple{}, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + + { + // C++20 bidirectional C++17 bidirectional + std::ranges::zip_transform_view v(GetFirst{}, BidiCommonView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); + } + + { + // C++20 bidirectional C++17 input + std::ranges::zip_transform_view v(MakeTuple{}, BidiCommonView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + + { + // C++20 forward C++17 bidirectional + std::ranges::zip_transform_view v(GetFirst{}, ForwardSizedView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); + } + + { + // C++20 forward C++17 input + std::ranges::zip_transform_view v(MakeTuple{}, ForwardSizedView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + static_assert(HasIterCategory); + } + + { + // C++20 input C++17 not a range + std::ranges::zip_transform_view v(GetFirst{}, InputCommonView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(!HasIterCategory); + } + + { + // difference_type of one view + std::ranges::zip_transform_view v{MakeTuple{}, DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + + { + // difference_type of multiple views should be the common type + std::ranges::zip_transform_view v{MakeTuple{}, DiffTypeRange{}, DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + const std::array foos{Foo{}}; + std::array bars{Bar{}, Bar{}}; + { + // value_type of one view + std::ranges::zip_transform_view v{MakeTuple{}, foos}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + { + // value_type of multiple views with different value_type + std::ranges::zip_transform_view v{MakeTuple{}, foos, bars}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + // LWG3798 Rvalue reference and iterator_category + { + std::ranges::zip_transform_view v(RValueRefFn{}, buffer); + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp new file mode 100644 index 0000000000000..70cf811e41f98 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/iterator/subscript.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr decltype(auto) operator[](difference_type n) const +// requires random_access_range; + +#include +#include + +#include "../types.h" + +template +concept CanSubscript = requires(T t) { t[0]; }; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // F returns PR value + std::ranges::zip_transform_view v(MakeTuple{}, SizedRandomAccessView{buffer}, std::views::iota(0)); + auto it = v.begin(); + using Iter = decltype(it); + static_assert(CanSubscript); + + std::same_as> decltype(auto) val = it[0]; + assert(val == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + } + + { + // F return by reference + std::ranges::zip_transform_view v(GetFirst{}, ContiguousCommonView{buffer}, ContiguousCommonView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + static_assert(CanSubscript); + + std::same_as decltype(auto) val = it[0]; + assert(&val == &buffer[0]); + assert(val == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + } + + { + // non random_access_range + std::ranges::zip_transform_view v(GetFirst{}, BidiCommonView{buffer}); + auto it = v.begin(); + using Iter = decltype(it); + static_assert(!CanSubscript); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + auto it = v.begin(); + assert(it[2] == std::make_tuple(3)); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, std::views::iota(0)); + auto it = v.begin(); + assert(it[0] == 1); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::repeat_view(2.)); + auto it = v.begin(); + assert(&std::get<0>(it[1]) == &buffer[1]); + assert(&std::get<1>(it[2]) == &buffer[2]); + assert(std::get<2>(it[3]) == 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.transform/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.default.pass.cpp new file mode 100644 index 0000000000000..ab00663f4a112 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.default.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 + +// sentinel() = default; + +#include +#include + +#include "../types.h" + +struct PODSentinel { + bool b; // deliberately uninitialised + + friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; } +}; + +struct Fn { + int operator()(auto&&...) const { return 5; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + PODSentinel end(); +}; + +constexpr bool test() { + { + using R = std::ranges::zip_transform_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.transform/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.other.pass.cpp new file mode 100644 index 0000000000000..be7c3d15a9109 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/ctor.other.pass.cpp @@ -0,0 +1,159 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr sentinel(sentinel i) +// requires Const && convertible_to, zentinel>; + +#include +#include + +#include "../types.h" + +template +struct convertible_sentinel_wrapper { + explicit convertible_sentinel_wrapper() = default; + constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {} + + template + requires std::convertible_to + constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper& other) : it_(other.it_) {} + + constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) { + return self.it_ == other; + } + T it_; +}; + +struct NonSimpleNonCommonConvertibleView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr convertible_sentinel_wrapper end() { return convertible_sentinel_wrapper(buffer_ + size_); } + constexpr convertible_sentinel_wrapper end() const { + return convertible_sentinel_wrapper(buffer_ + size_); + } +}; + +// convertible_to, zentinel> +static_assert(std::convertible_to< // + std::ranges::sentinel_t>, + std::ranges::sentinel_t const>>); + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + { + std::ranges::zip_transform_view v{ + MakeTuple{}, NonSimpleNonCommonConvertibleView(buffer1), NonSimpleNonCommonConvertibleView(buffer2)}; + using ZipTransformView = decltype(v); + static_assert(!std::ranges::common_range); + 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); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleNonCommonConvertibleView{buffer1}); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() != sent1); + assert(v.begin() != sent2); + assert(v.begin() + 4 == sent1); + assert(v.begin() + 4 == sent2); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, NonSimpleNonCommonConvertibleView{buffer1}, std::views::iota(0)); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() != sent1); + assert(v.begin() != sent2); + assert(v.begin() + 4 == sent1); + assert(v.begin() + 4 == sent2); + } + + { + // three ranges + std::ranges::zip_transform_view v( + Tie{}, NonSimpleNonCommonConvertibleView{buffer1}, SimpleCommon{buffer1}, std::ranges::single_view(2.)); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() != sent1); + assert(v.begin() != sent2); + assert(v.begin() + 1 == sent1); + assert(v.begin() + 1 == sent2); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleNonCommonConvertibleView(nullptr, 0)); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() == sent1); + assert(v.begin() == sent2); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), NonSimpleNonCommonConvertibleView{buffer1}, SimpleCommon{buffer1}); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() == sent1); + assert(v.begin() == sent2); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, std::ranges::empty_view(), NonSimpleNonCommonConvertibleView{buffer1}); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() == sent1); + assert(v.begin() == sent2); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, NonSimpleNonCommonConvertibleView{buffer1}, std::ranges::empty_view()); + auto sent1 = v.end(); + std::ranges::sentinel_t sent2 = sent1; + static_assert(!std::is_same_v); + assert(v.begin() == sent1); + assert(v.begin() == sent2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp new file mode 100644 index 0000000000000..9b20dacd1f696 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/eq.pass.cpp @@ -0,0 +1,199 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template +// requires sentinel_for, ziperator> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include +#include +#include + +#include "../types.h" + +using Iterator = random_access_iterator; +using ConstIterator = random_access_iterator; + +template +struct ComparableSentinel { + using Iter = std::conditional_t; + Iter iter_; + + explicit ComparableSentinel() = default; + constexpr explicit ComparableSentinel(const Iter& it) : iter_(it) {} + + constexpr friend bool operator==(const Iterator& i, const ComparableSentinel& s) { return base(i) == base(s.iter_); } + + constexpr friend bool operator==(const ConstIterator& i, const ComparableSentinel& s) { + return base(i) == base(s.iter_); + } +}; + +struct ComparableView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr auto begin() { return Iterator(buffer_); } + constexpr auto begin() const { return ConstIterator(buffer_); } + constexpr auto end() { return ComparableSentinel(Iterator(buffer_ + size_)); } + constexpr auto end() const { return ComparableSentinel(ConstIterator(buffer_ + size_)); } +}; + +struct ConstIncompatibleView : std::ranges::view_base { + cpp17_input_iterator begin(); + forward_iterator begin() const; + sentinel_wrapper> end(); + sentinel_wrapper> end() const; +}; + +template +concept EqualComparable = std::invocable, const Iter&, const Sent&>; + +constexpr bool test() { + int buffer1[4] = {1, 2, 3, 4}; + int buffer2[5] = {1, 2, 3, 4, 5}; + int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + // const and non-const have different iterator/sentinel types + std::ranges::zip_transform_view v{ + MakeTuple{}, NonSimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)}; + using ZipTransformView = decltype(v); + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + assert(v.begin() != v.end()); + assert(v.begin() + 4 == v.end()); + + // const_iterator (const int*) converted to iterator (int*) + assert(v.begin() + 4 == std::as_const(v).end()); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(EqualComparable); + static_assert(!EqualComparable); + static_assert(EqualComparable); + static_assert(EqualComparable); + } + + { + // underlying const/non-const sentinel can be compared with both const/non-const iterator + std::ranges::zip_transform_view v{MakeTuple{}, ComparableView(buffer1), ComparableView(buffer2)}; + using ZipTransformView = decltype(v); + static_assert(!std::ranges::common_range); + static_assert(!simple_view); + + assert(v.begin() != v.end()); + assert(v.begin() + 4 == v.end()); + assert(std::as_const(v).begin() + 4 == v.end()); + assert(std::as_const(v).begin() + 4 == std::as_const(v).end()); + assert(v.begin() + 4 == std::as_const(v).end()); + + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + static_assert(!std::is_same_v); + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + static_assert(EqualComparable); + static_assert(EqualComparable); + static_assert(EqualComparable); + static_assert(EqualComparable); + } + + { + // underlying const/non-const sentinel cannot be compared with non-const/const iterator + std::ranges::zip_transform_view v{MakeTuple{}, ComparableView(buffer1), ConstIncompatibleView{}}; + using ZipTransformView = decltype(v); + static_assert(!std::ranges::common_range); + static_assert(!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); + } + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, ComparableView{buffer1}); + assert(v.begin() != v.end()); + assert(v.begin() + 4 == v.end()); + assert(v.begin() + 4 == std::as_const(v).end()); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, ComparableView{buffer2}, std::views::iota(0)); + assert(v.begin() != v.end()); + assert(v.begin() + 5 == v.end()); + assert(v.begin() + 5 == std::as_const(v).end()); + } + + { + // three ranges + std::ranges::zip_transform_view v( + Tie{}, ComparableView{buffer1}, SimpleNonCommon{buffer2}, std::ranges::single_view(2.)); + assert(v.begin() != v.end()); + assert(v.begin() + 1 == v.end()); + assert(v.begin() + 1 == std::as_const(v).end()); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, ComparableView(nullptr, 0)); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), ComparableView{buffer1}, SimpleCommon{buffer2}); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, std::ranges::empty_view(), ComparableView{buffer2}); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, ComparableView{buffer2}, std::ranges::empty_view()); + assert(v.begin() == v.end()); + assert(std::as_const(v).begin() == std::as_const(v).end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp new file mode 100644 index 0000000000000..fc29a00014f67 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/sentinel/minus.pass.cpp @@ -0,0 +1,279 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template +// requires sized_sentinel_for, ziperator> +// friend constexpr range_difference_t> +// operator-(const sentinel& x, const iterator& y); + +#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 = std::intptr_t; + + convertible_forward_sized_iterator() = default; + constexpr convertible_forward_sized_iterator(Base it) : it_(it) {} + + template U> + constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator& it) : it_(it.it_) {} + + constexpr decltype(*Base{}) operator*() const { return *it_; } + + constexpr convertible_forward_sized_iterator& operator++() { + ++it_; + return *this; + } + constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); } + + friend constexpr bool + operator==(const convertible_forward_sized_iterator&, const convertible_forward_sized_iterator&) = default; + + friend constexpr difference_type + operator-(const convertible_forward_sized_iterator& x, const convertible_forward_sized_iterator& y) { + return x.it_ - y.it_; + } +}; +static_assert(std::forward_iterator>); + +template +struct convertible_sized_sentinel { + Base base_; + explicit convertible_sized_sentinel() = default; + constexpr convertible_sized_sentinel(const Base& it) : base_(it) {} + + template U> + constexpr convertible_sized_sentinel(const convertible_sized_sentinel& other) : base_(other.base_) {} + + template + requires(std::convertible_to || std::convertible_to) + friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) { + return s.base_ == base; + } + template + requires(std::convertible_to || std::convertible_to) + friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) { + return s.base_ - i; + } + + template + requires(std::convertible_to || std::convertible_to) + friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) { + return i - s.base_; + } +}; +static_assert(std::sized_sentinel_for>, + convertible_forward_sized_iterator<>>); +static_assert(std::sized_sentinel_for>, + convertible_forward_sized_iterator>); +static_assert(std::sized_sentinel_for>, + convertible_forward_sized_iterator>); + +struct ConstCompatibleForwardSized : IntBufferView { + using IntBufferView::IntBufferView; + + using iterator = convertible_forward_sized_iterator; + using const_iterator = convertible_forward_sized_iterator; + + constexpr iterator begin() { return {buffer_}; } + constexpr const_iterator begin() const { return {buffer_}; } + constexpr convertible_sized_sentinel end() { return iterator{buffer_ + size_}; } + constexpr convertible_sized_sentinel end() const { return const_iterator{buffer_ + size_}; } +}; + +template +concept HasMinus = std::invocable, const T&, const U&>; + +template +concept SentinelHasMinus = HasMinus, std::ranges::iterator_t>; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[3] = {1, 2, 3}; + + { + // shortest range + std::ranges::zip_transform_view v(MakeTuple{}, std::views::iota(0, 3), ForwardSizedNonCommon(buffer1)); + static_assert(!std::ranges::common_range); + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 3); + assert(st - std::ranges::next(it, 1) == 2); + + assert(it - st == -3); + assert(std::ranges::next(it, 1) - st == -2); + static_assert(SentinelHasMinus); + } + + { + // underlying sentinel does not model sized_sentinel_for + std::ranges::zip_transform_view v(MakeTuple{}, std::views::iota(0), SizedRandomAccessView(buffer1)); + static_assert(!std::ranges::common_range); + static_assert(!SentinelHasMinus); + } + + { + // const incompatible: + // underlying const sentinels cannot subtract underlying iterators + // underlying sentinels cannot subtract underlying const iterators + std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleForwardSizedNonCommon{buffer1}); + static_assert(!std::ranges::common_range); + static_assert(!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_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1}); + static_assert(!std::ranges::common_range); + static_assert(!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); + } + + auto testMinus = [](auto&& v, auto distance) { + 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 == -distance); + assert(st - it == distance); + assert(const_it - const_st == -distance); + assert(const_st - const_it == distance); + assert(it - const_st == -distance); + assert(const_st - it == distance); + assert(const_it - st == -distance); + assert(st - const_it == distance); + }; + + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1}); + testMinus(v, 5); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, ConstCompatibleForwardSized{buffer1}, std::views::iota(0, 100)); + testMinus(v, 5); + } + + { + // three ranges + std::ranges::zip_transform_view v( + Tie{}, + ConstCompatibleForwardSized{buffer1}, + ConstCompatibleForwardSized{buffer2}, + std::ranges::single_view(2.)); + testMinus(v, 1); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized(nullptr, 0)); + testMinus(v, 0); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), ConstCompatibleForwardSized{buffer1}, SimpleCommon{buffer2}); + testMinus(v, 0); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, + ConstCompatibleForwardSized{buffer1}, + std::ranges::empty_view(), + ConstCompatibleForwardSized{buffer2}); + testMinus(v, 0); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer1}, ConstCompatibleForwardSized{buffer2}, std::ranges::empty_view()); + testMinus(v, 0); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp new file mode 100644 index 0000000000000..140bdc311d527 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/size.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto size() requires sized_range +// constexpr auto size() const requires sized_range + +#include + +#include + +#include "test_iterators.h" +#include "types.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; +struct SizedView : std::ranges::view_base { + std::size_t size_ = 0; + constexpr SizedView(std::size_t s) : size_(s) {} + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + size_; } +}; + +struct SizedNonConst : std::ranges::view_base { + using iterator = forward_iterator; + std::size_t size_ = 0; + constexpr SizedNonConst(std::size_t s) : size_(s) {} + constexpr auto begin() const { return iterator{buffer}; } + constexpr auto end() const { return iterator{buffer + size_}; } + constexpr std::size_t size() { return size_; } +}; + +struct ConstNonConstDifferentSize : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } + + constexpr auto size() { return 5; } + constexpr auto size() const { return 6; } +}; + +constexpr bool test() { + { + // one range + std::ranges::zip_transform_view v(MakeTuple{}, SimpleCommon{buffer}); + assert(v.size() == 9); + assert(std::as_const(v).size() == 9); + } + + { + // two ranges + std::ranges::zip_transform_view v(GetFirst{}, SimpleCommon{buffer}, SizedView(3)); + assert(v.size() == 3); + assert(std::as_const(v).size() == 3); + } + + { + // three ranges + std::ranges::zip_transform_view v(Tie{}, SimpleCommon{buffer}, SizedView{6}, std::ranges::single_view(2.)); + assert(v.size() == 1); + assert(std::as_const(v).size() == 1); + } + + { + // single empty range + std::ranges::zip_transform_view v(MakeTuple{}, std::ranges::empty_view()); + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); + } + + { + // empty range at the beginning + std::ranges::zip_transform_view v( + MakeTuple{}, std::ranges::empty_view(), SimpleCommon{buffer}, SimpleCommon{buffer}); + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); + } + + { + // empty range in the middle + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, std::ranges::empty_view(), SimpleCommon{buffer}); + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); + } + + { + // empty range at the end + std::ranges::zip_transform_view v( + MakeTuple{}, SimpleCommon{buffer}, SimpleCommon{buffer}, std::ranges::empty_view()); + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); + } + + { + // const-view non-sized range + std::ranges::zip_transform_view v(MakeTuple{}, SizedNonConst(2), SizedView(3)); + assert(v.size() == 2); + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + } + + { + // const/non-const has different sizes + std::ranges::zip_transform_view v(MakeTuple{}, ConstNonConstDifferentSize{}); + assert(v.size() == 5); + assert(std::as_const(v).size() == 6); + } + + { + // underlying range not sized + std::ranges::zip_transform_view v(MakeTuple{}, InputCommonView{buffer}); + static_assert(!std::ranges::sized_range); + 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.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h new file mode 100644 index 0000000000000..dc6a40a84d61f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.zip.transform/types.h @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TRANSFORM_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TRANSFORM_TYPES_H + +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" +#include "../range_adaptor_types.h" + +#if TEST_STD_VER <= 20 +# error "range.zip.transform/types.h" can only be included in builds supporting C++20 +#endif + +struct IntView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct MakeTuple { + constexpr auto operator()(auto&&... args) const { return std::tuple(std::forward(args)...); } +}; + +struct Tie { + constexpr auto operator()(auto&&... args) const { return std::tie(std::forward(args)...); } +}; + +struct GetFirst { + constexpr decltype(auto) operator()(auto&& first, auto&&...) const { return std::forward(first); } +}; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +struct ConstNonConstDifferentView : std::ranges::view_base { + int* begin(); + const int* begin() const; + int* end(); + const int* end() const; +}; + +struct NonConstOnlyFn { + int operator()(int&) const; + int operator()(const int&) const = delete; +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TRANSFORM_TYPES_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 index 637d776ad08c4..837ed9efee19d 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/begin.pass.cpp @@ -18,7 +18,7 @@ #include #include -#include "types.h" +#include "../range_adaptor_types.h" template concept HasConstBegin = requires(const T& ct) { ct.begin(); }; 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 index bdfd58ff8bbe7..34c81f05820fe 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/cpo.pass.cpp @@ -18,7 +18,7 @@ #include #include -#include "types.h" +#include "../range_adaptor_types.h" static_assert(std::is_invocable_v); static_assert(!std::is_invocable_v); 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 index b4a16debedc8f..513d26817e915 100644 --- 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 @@ -13,7 +13,7 @@ #include #include -#include "types.h" +#include "../range_adaptor_types.h" template void conversion_test(T); 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 index b7f64477a12d0..7cf9f5f462725 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/end.pass.cpp @@ -14,7 +14,7 @@ #include #include -#include "types.h" +#include "../range_adaptor_types.h" // ID | simple | common | bidi | random | sized | #views | v.end() | as_const(v) // | | | | access | | | | .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 index efe64b31f79fb..444f3ed95b322 100644 --- 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 @@ -25,7 +25,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" template concept canPlusEqual = requires(T& t, U& u) { t += u; }; 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 index 8ab7346800093..5ad054c0c3b1d 100644 --- 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 @@ -19,7 +19,7 @@ #include "test_iterators.h" #include "test_range.h" -#include "../types.h" +#include "../../range_adaptor_types.h" // This is for testing that zip iterator never calls underlying iterator's >, >=, <=, !=. // The spec indicates that zip iterator's >= is negating zip iterator's < instead of calling underlying iterator's >=. 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 index 98078b2ce3095..abced1629a4a7 100644 --- 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 @@ -13,7 +13,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" struct PODIter { int i; // deliberately uninitialised 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 index 7f9784e6f7ae2..6b8b55fbe68e8 100644 --- 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 @@ -17,7 +17,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" using ConstIterIncompatibleView = BasicView, forward_iterator, random_access_iterator, random_access_iterator>; 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 index a8422ec73330f..db5a651dae528 100644 --- 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 @@ -16,7 +16,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" template concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; }; 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 index fb58aa28fbdf8..61495fae0467f 100644 --- 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 @@ -15,7 +15,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" constexpr bool test() { std::array a{1, 2, 3, 4}; 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 index 94d2bd47e9806..d094779589e09 100644 --- 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 @@ -17,7 +17,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" struct InputRange : IntBufferView { using IntBufferView::IntBufferView; 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 index 2926b22cffa82..f258809a13304 100644 --- 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 @@ -16,7 +16,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" struct ThrowingMove { ThrowingMove() = default; 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 index bb0ec1c813238..b12877c33caf0 100644 --- 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 @@ -15,7 +15,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" struct ThrowingMove { ThrowingMove() = default; 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 index 2f2f0fc4f4e33..852ea9ba069b0 100644 --- 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 @@ -16,7 +16,7 @@ #include "test_iterators.h" -#include "../types.h" +#include "../../range_adaptor_types.h" template struct ForwardView : std::ranges::view_base { diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp index 0f7e4c4b86489..5cd76e03bab48 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/iterator/singular.pass.cpp @@ -16,7 +16,7 @@ #include -#include "../types.h" +#include "../../range_adaptor_types.h" struct ThrowOnIncrementIterator { int* it_; 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 index ba3abfa2a4369..deeeebf8ca8cf 100644 --- 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 @@ -14,7 +14,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" constexpr bool test() { int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; 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 index c74c15662d83a..4b8587fc22c4b 100644 --- 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 @@ -18,7 +18,7 @@ #include #include -#include "types.h" +#include "../range_adaptor_types.h" void testConceptPair() { int buffer1[2] = {1, 2}; 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 index 11ad73c313c59..c3f50b0aeabd8 100644 --- 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 @@ -13,7 +13,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" template struct convertible_sentinel_wrapper { 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 index 04542c3ae4d14..fa26d987b8dd3 100644 --- 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 @@ -17,7 +17,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" #include "test_range.h" using Iterator = random_access_iterator; 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 index be0a7ba5b907e..bcfa3407e6cb1 100644 --- 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 @@ -26,7 +26,7 @@ #include #include -#include "../types.h" +#include "../../range_adaptor_types.h" template struct convertible_forward_sized_iterator { 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 index 194b3bd1c8719..0c0014c5d1864 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.zip/size.pass.cpp @@ -18,7 +18,7 @@ #include #include "test_iterators.h" -#include "types.h" +#include "../range_adaptor_types.h" int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; struct View : std::ranges::view_base { diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h similarity index 83% rename from libcxx/test/std/ranges/range.adaptors/range.zip/types.h rename to libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h index e084dcfc41b0d..288a78ac722e6 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.zip/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// -#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H -#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H #include #include @@ -17,7 +17,7 @@ #include "test_range.h" #if TEST_STD_VER <= 20 -# error "range.zip/types.h" can only be included in builds supporting C++20 +# error "range.adaptor/types.h" can only be included in builds supporting C++20 #endif // TEST_STD_VER <= 20 template @@ -27,12 +27,14 @@ struct BufferView : std::ranges::view_base { template constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} + + constexpr BufferView(T* b, std::size_t s) : buffer_(b), size_(s) {} }; using IntBufferView = BufferView; template -struct Common : IntBufferView { +struct Common : IntBufferView { using IntBufferView::IntBufferView; constexpr int* begin() @@ -48,10 +50,10 @@ struct Common : IntBufferView { } constexpr const int* end() const { return buffer_ + size_; } }; -using SimpleCommon = Common; +using SimpleCommon = Common; using NonSimpleCommon = Common; -using SimpleCommonRandomAccessSized = SimpleCommon; +using SimpleCommonRandomAccessSized = SimpleCommon; using NonSimpleCommonRandomAccessSized = NonSimpleCommon; static_assert(std::ranges::common_range>); @@ -64,20 +66,22 @@ template struct CommonNonRandom : IntBufferView { using IntBufferView::IntBufferView; using const_iterator = forward_iterator; - using iterator = forward_iterator; + using iterator = forward_iterator; constexpr iterator begin() - requires(!Simple) { + requires(!Simple) + { return iterator(buffer_); } constexpr const_iterator begin() const { return const_iterator(buffer_); } constexpr iterator end() - requires(!Simple) { + requires(!Simple) + { return iterator(buffer_ + size_); } constexpr const_iterator end() const { return const_iterator(buffer_ + size_); } }; -using SimpleCommonNonRandom = CommonNonRandom; +using SimpleCommonNonRandom = CommonNonRandom; using NonSimpleCommonNonRandom = CommonNonRandom; static_assert(std::ranges::common_range); @@ -90,18 +94,20 @@ template struct NonCommon : IntBufferView { using IntBufferView::IntBufferView; constexpr int* begin() - requires(!Simple) { + requires(!Simple) + { return buffer_; } constexpr const int* begin() const { return buffer_; } constexpr sentinel_wrapper end() - requires(!Simple) { + requires(!Simple) + { return sentinel_wrapper(buffer_ + size_); } constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } }; -using SimpleNonCommon = NonCommon; +using SimpleNonCommon = NonCommon; using NonSimpleNonCommon = NonCommon; static_assert(!std::ranges::common_range); @@ -114,21 +120,23 @@ template struct NonCommonSized : IntBufferView { using IntBufferView::IntBufferView; constexpr int* begin() - requires(!Simple) { + requires(!Simple) + { return buffer_; } constexpr const int* begin() const { return buffer_; } constexpr sentinel_wrapper end() - requires(!Simple) { + 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 SimpleNonCommonSized = NonCommonSized; using SimpleNonCommonRandomAccessSized = SimpleNonCommonSized; -using NonSimpleNonCommonSized = NonCommonSized; +using NonSimpleNonCommonSized = NonCommonSized; using NonSimpleNonCommonRandomAccessSized = NonSimpleNonCommonSized; static_assert(!std::ranges::common_range); @@ -142,15 +150,17 @@ struct NonCommonNonRandom : IntBufferView { using IntBufferView::IntBufferView; using const_iterator = forward_iterator; - using iterator = forward_iterator; + using iterator = forward_iterator; constexpr iterator begin() - requires(!Simple) { + requires(!Simple) + { return iterator(buffer_); } constexpr const_iterator begin() const { return const_iterator(buffer_); } constexpr sentinel_wrapper end() - requires(!Simple) { + requires(!Simple) + { return sentinel_wrapper(iterator(buffer_ + size_)); } constexpr sentinel_wrapper end() const { @@ -158,7 +168,7 @@ struct NonCommonNonRandom : IntBufferView { } }; -using SimpleNonCommonNonRandom = NonCommonNonRandom; +using SimpleNonCommonNonRandom = NonCommonNonRandom; using NonSimpleNonCommonNonRandom = NonCommonNonRandom; static_assert(!std::ranges::common_range); @@ -172,13 +182,15 @@ struct BasicView : IntBufferView { using IntBufferView::IntBufferView; constexpr NonConstIter begin() - requires(!std::is_same_v) { + requires(!std::is_same_v) + { return NonConstIter(buffer_); } constexpr Iter begin() const { return Iter(buffer_); } constexpr NonConstSent end() - requires(!std::is_same_v) { + requires(!std::is_same_v) + { if constexpr (std::is_same_v) { return NonConstIter(buffer_ + size_); } else { @@ -200,10 +212,10 @@ struct forward_sized_iterator { Base it_ = nullptr; using iterator_category = std::forward_iterator_tag; - using value_type = int; - using difference_type = std::intptr_t; - using pointer = Base; - using reference = decltype(*Base{}); + using value_type = int; + using difference_type = std::intptr_t; + using pointer = Base; + using reference = decltype(*Base{}); forward_sized_iterator() = default; constexpr forward_sized_iterator(Base it) : it_(it) {} @@ -232,8 +244,11 @@ static_assert(std::ranges::common_range); static_assert(!std::ranges::random_access_range); static_assert(simple_view); -using NonSimpleForwardSizedView = BasicView, forward_sized_iterator, - forward_sized_iterator, forward_sized_iterator>; +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); @@ -248,8 +263,10 @@ static_assert(!std::ranges::random_access_range); static_assert(simple_view); using NonSimpleForwardSizedNonCommon = - BasicView, sized_sentinel>, - forward_sized_iterator, sized_sentinel>>; + 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); @@ -278,8 +295,10 @@ static_assert(!std::ranges::sized_range); static_assert(simple_view); using NonSimpleNonSizedRandomAccessView = - BasicView, sentinel_wrapper>, - random_access_iterator, sentinel_wrapper> >; + 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); @@ -308,8 +327,11 @@ static_assert(!std::ranges::forward_range); static_assert(std::ranges::common_range); static_assert(simple_view); -using NonSimpleInputCommonView = BasicView, common_input_iterator, - common_input_iterator, common_input_iterator>; +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); @@ -322,8 +344,10 @@ static_assert(!std::ranges::common_range); static_assert(simple_view); using NonSimpleInputNonCommonView = - BasicView, sentinel_wrapper>, - common_input_iterator, sentinel_wrapper>>; + 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); @@ -336,8 +360,11 @@ static_assert(!std::ranges::random_access_range); static_assert(std::ranges::common_range); static_assert(simple_view); -using NonSimpleBidiCommonView = BasicView, bidirectional_iterator, - bidirectional_iterator, bidirectional_iterator>; +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); @@ -372,8 +399,10 @@ static_assert(!std::ranges::common_range); static_assert(simple_view); using NonSimpleBidiNonCommonView = - BasicView, sentinel_wrapper>, - bidirectional_iterator, sentinel_wrapper>>; + 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); @@ -388,24 +417,25 @@ static_assert(!std::ranges::common_range); static_assert(simple_view); using NonSimpleSizedBidiNonCommonView = - BasicView, sized_sentinel>, - bidirectional_iterator, sized_sentinel>>; + 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); static_assert(!simple_view); -namespace adltest{ +namespace adltest { struct iter_move_swap_iterator { - std::reference_wrapper iter_move_called_times; std::reference_wrapper iter_swap_called_times; int i = 0; using iterator_category = std::input_iterator_tag; - using value_type = int; - using difference_type = std::intptr_t; + using value_type = int; + using difference_type = std::intptr_t; constexpr int operator*() const { return i; } @@ -435,4 +465,4 @@ struct IterMoveSwapRange { }; } // namespace adltest -#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADAPTOR_TYPES_H diff --git a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp index 3b35271d649c3..06186656f0122 100644 --- a/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp +++ b/libcxx/test/std/ranges/ranges_robust_against_no_unique_address.pass.cpp @@ -58,6 +58,7 @@ constexpr bool test() { #if TEST_STD_VER >= 23 testOne>(); testOne>(); + testOne>(); #endif return true; }