diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index ec23ba9d1e3a1..b3186f7e486d7 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -39,7 +39,7 @@ Implemented Papers ------------------ - P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` - is implemented in this release) + and `adjacent_view` are implemented in this release) - P3044R2: sub-``string_view`` from ``string`` (`Github `__) - P3223R2: Making ``std::istream::ignore`` less surprising (`Github `__) - P3060R3: Add ``std::views::indices(n)`` (`Github `__) diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index ddace8bf8c728..9d725f49a18f1 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -696,6 +696,7 @@ set(files __random/uniform_real_distribution.h __random/weibull_distribution.h __ranges/access.h + __ranges/adjacent_view.h __ranges/all.h __ranges/as_rvalue_view.h __ranges/chunk_by_view.h @@ -739,6 +740,7 @@ set(files __ranges/view_interface.h __ranges/views.h __ranges/zip_transform_view.h + __ranges/zip_utils.h __ranges/zip_view.h __split_buffer __std_mbstate_t.h diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h new file mode 100644 index 0000000000000..873b58d3b4365 --- /dev/null +++ b/libcxx/include/__ranges/adjacent_view.h @@ -0,0 +1,408 @@ +// -*- 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_ADJACENT_VIEW_H +#define _LIBCPP___RANGES_ADJACENT_VIEW_H + +#include <__config> + +#include <__algorithm/min.h> +#include <__compare/three_way_comparable.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/equality_comparable.h> +#include <__cstddef/size_t.h> +#include <__functional/invoke.h> +#include <__functional/operations.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/next.h> +#include <__iterator/prev.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/empty_view.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__ranges/zip_utils.h> +#include <__type_traits/common_type.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/make_unsigned.h> +#include <__type_traits/maybe_const.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/integer_sequence.h> +#include <__utility/move.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { + +template + requires view<_View> && (_Np > 0) +class adjacent_view : public view_interface> { +private: + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + + template + class __iterator; + + template + class __sentinel; + + struct __as_sentinel {}; + +public: + _LIBCPP_HIDE_FROM_ABI adjacent_view() + requires default_initializable<_View> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_view(_View __base) : __base_(std::move(__base)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator(ranges::begin(__base_), ranges::end(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range // todo: this seems under-constrained. lwg issue? + { + return __iterator(ranges::begin(__base_), ranges::end(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (common_range<_View>) { + return __iterator(__as_sentinel{}, ranges::begin(__base_), ranges::end(__base_)); + } else { + return __sentinel(ranges::end(__base_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range + { + if constexpr (common_range) { + return __iterator(__as_sentinel{}, ranges::begin(__base_), ranges::end(__base_)); + } else { + return __sentinel(ranges::end(__base_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + using _ST = decltype(ranges::size(__base_)); + using _CT = common_type_t<_ST, size_t>; + auto __sz = static_cast<_CT>(ranges::size(__base_)); + __sz -= std::min<_CT>(__sz, _Np - 1); + return static_cast<_ST>(__sz); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + using _ST = decltype(ranges::size(__base_)); + using _CT = common_type_t<_ST, size_t>; + auto __sz = static_cast<_CT>(ranges::size(__base_)); + __sz -= std::min<_CT>(__sz, _Np - 1); + return static_cast<_ST>(__sz); + } +}; + +template + requires view<_View> && (_Np > 0) +template +class adjacent_view<_View, _Np>::__iterator { + friend adjacent_view; + using _Base = __maybe_const<_Const, _View>; + array, _Np> __current_ = array, _Np>(); + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(iterator_t<_Base> __first, sentinel_t<_Base> __last) { + __current_[0] = __first; + for (int __i = 1; __i < static_cast(_Np); ++__i) { + __current_[__i] = ranges::next(__current_[__i - 1], 1, __last); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__as_sentinel, iterator_t<_Base> __first, iterator_t<_Base> __last) { + if constexpr (!bidirectional_range<_Base>) { + __current_.fill(__last); + } else { + __current_[_Np - 1] = __last; + for (int __i = static_cast(_Np) - 2; __i >= 0; --__i) { + __current_[__i] = ranges::prev(__current_[__i + 1], 1, __first); + } + } + } + + template + _LIBCPP_HIDE_FROM_ABI explicit constexpr __iterator(_Iter&& __i, index_sequence<_Is...>) + : __current_{std::move(__i.__current_[_Is])...} {} + + static consteval auto __get_iterator_concept() { + if constexpr (random_access_range<_Base>) + return random_access_iterator_tag{}; + else if constexpr (bidirectional_range<_Base>) + return bidirectional_iterator_tag{}; + else + return forward_iterator_tag{}; + } + + template + static auto __repeat_tuple_helper(index_sequence<_Is...>) -> tuple()()))...>; + +public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(__get_iterator_concept()); + using value_type = decltype(__repeat_tuple_helper>(make_index_sequence<_Np>{})); + 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, iterator_t<_Base>> + : __iterator(std::move(__i), make_index_sequence<_Np>{}) {} + + _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const { + return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + for (auto& __i : __current_) { + ++__i; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + for (auto& __i : __current_) { + --__i; + } + 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> + { + for (auto& __i : __current_) { + __i += __x; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x) + requires random_access_range<_Base> + { + for (auto& __i : __current_) { + __i -= __x; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type n) const + requires random_access_range<_Base> + { + return ranges::__tuple_transform([&](auto& __i) -> decltype(auto) { return __i[n]; }, __current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) { + return __x.__current_.back() == __y.__current_.back(); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__current_.back() < __y.__current_.back(); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> && three_way_comparable> + { + return __x.__current_.back() <=> __y.__current_.back(); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __n; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires random_access_range<_Base> + { + return __i + __n; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + auto __r = __i; + __r -= __n; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + return __x.__current_.back() - __y.__current_.back(); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept( + noexcept(ranges::iter_move(std::declval&>())) && + is_nothrow_move_constructible_v>) { + return ranges::__tuple_transform(ranges::iter_move, __i.__current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept( + noexcept(ranges::iter_swap(std::declval>(), std::declval>()))) + requires indirectly_swappable> + { + for (size_t __i = 0; __i < _Np; ++__i) { + ranges::iter_swap(__l.__current_[__i], __r.__current_[__i]); + } + } +}; + +template + requires view<_View> && (_Np > 0) +template +class adjacent_view<_View, _Np>::__sentinel { + friend adjacent_view; + using _Base = __maybe_const<_Const, _View>; + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) { __end_ = std::move(__end); } + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __i) + requires _Const && convertible_to, sentinel_t<_Base>> + : __end_(std::move(__i.__end_)) {} + + template + requires sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__current_.back() == __y.__end_; + } + + template + requires sized_sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__current_.back() - __y.__end_; + } + + template + requires sized_sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) { + return __y.__end_ - __x.__current_.back(); + } +}; + +template +constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; + +namespace views { +namespace __adjacent { + +template +struct __fn : __range_adaptor_closure<__fn<_Np>> { + template + requires(_Np == 0 && forward_range<_Range &&>) + _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&&) noexcept { + return empty_view>{}; + } + + template + _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ranges&& __range) noexcept( + noexcept(adjacent_view, _Np>(std::forward<_Ranges>(__range)))) + -> decltype(adjacent_view, _Np>(std::forward<_Ranges>(__range))) { + return adjacent_view, _Np>(std::forward<_Ranges>(__range)); + } +}; + +} // namespace __adjacent +inline namespace __cpo { +template +inline constexpr auto adjacent = __adjacent::__fn<_Np>{}; +inline constexpr auto pairwise = adjacent<2>; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ADJACENT_VIEW_H diff --git a/libcxx/include/__ranges/zip_utils.h b/libcxx/include/__ranges/zip_utils.h new file mode 100644 index 0000000000000..d7b40d12048cf --- /dev/null +++ b/libcxx/include/__ranges/zip_utils.h @@ -0,0 +1,49 @@ + +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___RANGES_ZIP_VIEW_H +#define _LIBCPP___RANGES_ZIP_VIEW_H + +#include <__config> + +#include <__functional/invoke.h> +#include <__utility/forward.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) { + return std::apply( + [&](_Types&&... __elements) { + return tuple...>(std::invoke(__f, std::forward<_Types>(__elements))...); + }, + std::forward<_Tuple>(__tuple)); +} + +} // namespace ranges +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ZIP_VIEW_H diff --git a/libcxx/include/__ranges/zip_view.h b/libcxx/include/__ranges/zip_view.h index ce00c98710c4e..bd54f37028e45 100644 --- a/libcxx/include/__ranges/zip_view.h +++ b/libcxx/include/__ranges/zip_view.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef _LIBCPP___RANGES_ZIP_VIEW_H -#define _LIBCPP___RANGES_ZIP_VIEW_H +#ifndef _LIBCPP___RANGES_ZIP_UTILS_H +#define _LIBCPP___RANGES_ZIP_UTILS_H #include <__config> @@ -31,6 +31,7 @@ #include <__ranges/enable_borrowed_range.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> +#include <__ranges/zip_utils.h> #include <__type_traits/is_nothrow_constructible.h> #include <__type_traits/make_unsigned.h> #include <__utility/declval.h> @@ -58,15 +59,6 @@ concept __zip_is_common = (!(bidirectional_range<_Ranges> && ...) && (common_range<_Ranges> && ...)) || ((random_access_range<_Ranges> && ...) && (sized_range<_Ranges> && ...)); -template -_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) { - return std::apply( - [&](_Types&&... __elements) { - return tuple...>(std::invoke(__f, std::forward<_Types>(__elements))...); - }, - std::forward<_Tuple>(__tuple)); -} - template _LIBCPP_HIDE_FROM_ABI constexpr void __tuple_for_each(_Fun&& __f, _Tuple&& __tuple) { std::apply( @@ -504,4 +496,4 @@ _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS -#endif // _LIBCPP___RANGES_ZIP_VIEW_H +#endif // _LIBCPP___RANGES_ZIP_UTILS_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 894093b409e11..e2b60cf8607db 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1850,6 +1850,7 @@ module std [system] { module ranges { module access { header "__ranges/access.h" } + module adjacent_view { header "__ranges/adjacent_view.h" } module all { header "__ranges/all.h" export std.ranges.ref_view @@ -1947,6 +1948,9 @@ module std [system] { header "__ranges/zip_view.h" export std.utility.pair } + module zip_utils { + header "__ranges/zip_utils.h" + } module zip_transform_view { header "__ranges/zip_transform_view.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges index cfaa66a0831b3..fe272579ca898 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -353,6 +353,20 @@ namespace std::ranges { namespace views { inline constexpr unspecified zip_transform = unspecified; } // C++23 + // [range.adjacent], adjacent view + template + requires view && (N > 0) + class adjacent_view; + + template + constexpr bool enable_borrowed_range> = + enable_borrowed_range; + + namespace views { + template + constexpr unspecified adjacent = unspecified; + inline constexpr auto pairwise = adjacent<2>; + } // [range.as.rvalue] template @@ -411,6 +425,7 @@ namespace std { # if _LIBCPP_STD_VER >= 20 # include <__ranges/access.h> +# include <__ranges/adjacent_view.h> # include <__ranges/all.h> # include <__ranges/common_view.h> # include <__ranges/concepts.h> diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index cc7daa3cd1aec..38222812fdc0b 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -299,9 +299,6 @@ export namespace std { namespace views { using std::ranges::views::zip_transform; } -#endif // _LIBCPP_STD_VER >= 23 - -#if 0 using std::ranges::adjacent_view; namespace views { @@ -309,6 +306,9 @@ export namespace std { using std::ranges::views::pairwise; } // namespace views +#endif // _LIBCPP_STD_VER >= 23 + +#if 0 using std::ranges::adjacent_transform_view; namespace views { 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 7e2510f5b5d13..9996ea42ab26d 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 @@ -121,7 +121,7 @@ static_assert(test(std::views::values, pairs)); #if TEST_STD_VER >= 23 // static_assert(test(std::views::adjacent_transform<2>, [](int x, int y) { return x + y; }, a)); -// static_assert(test(std::views::adjacent<2>, a)); +static_assert(test(std::views::adjacent<2>, a)); // static_assert(test(std::views::as_const, a)); static_assert(test(std::views::as_rvalue, a)); // static_assert(test(std::views::cartesian_product, a, a, a)); @@ -129,6 +129,7 @@ static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; })) // static_assert(test(std::views::chunk, a, 1)); // static_assert(test(std::views::enumerate, a)); static_assert(test(std::views::join_with, 1)); +static_assert(test(std::views::pairwise, a)); // static_assert(test(std::views::stride, a, 1)); static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a)); static_assert(test(std::views::zip, a, a)); diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp new file mode 100644 index 0000000000000..ffd0f52765734 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/adaptor.pass.cpp @@ -0,0 +1,242 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// Test std::views::adjacent + +#include +#include +#include +#include +#include + +#include "../range_adaptor_types.h" + +template +constexpr void test_constraints() { + // needs to be a range + static_assert(std::is_invocable_v)), std::ranges::empty_view>); + static_assert(!std::is_invocable_v)), int>); + + // underlying needs to be forward_range + static_assert(!std::is_invocable_v)), InputCommonView>); + static_assert(std::is_invocable_v)), ForwardSizedView>); +} + +constexpr void test_pairwise_constraints() { + // needs to be a range + static_assert(std::is_invocable_v>); + static_assert(!std::is_invocable_v); + + // underlying needs to be forward_range + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); +} + +constexpr void test_all_constraints() { + test_pairwise_constraints(); + test_constraints<0>(); + test_constraints<1>(); + test_constraints<2>(); + test_constraints<3>(); + test_constraints<5>(); +} + +constexpr void test_zero_case() { + // N == 0 is a special case that always results in an empty range + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + std::same_as>> decltype(auto) v = + std::views::adjacent<0>(ContiguousCommonView{buffer}); + assert(std::ranges::size(v) == 0); +} + +struct MoveOnlyView : ForwardSizedView { + using ForwardSizedView::ForwardSizedView; + + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView(const MoveOnlyView&) = delete; + + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(const MoveOnlyView&) = delete; +}; + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + const auto validateBegin = [&](auto&& view) { + auto it = view.begin(); + auto tuple = *it; + + assert(std::get<0>(tuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<3>(tuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<4>(tuple) == buffer[4]); + }; + + // Test `views::adjacent(r)` + { + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = std::views::adjacent(ContiguousCommonView{buffer}); + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + validateBegin(v); + } + + // Test `views::adjacent(move only view)` + { + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = std::views::adjacent(MoveOnlyView{buffer}); + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + validateBegin(v); + } + + // Test `r | views::adjacent` + { + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | std::views::adjacent; + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + validateBegin(v); + } + + // Test `move only view | views::adjacent` + { + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = MoveOnlyView{buffer} | std::views::adjacent; + assert(std::ranges::size(v) == (N <= 8 ? 9 - N : 0)); + validateBegin(v); + } + + // Test adjacent | adjacent + { + using View = std::ranges::adjacent_view, N>; + + auto twice = std::views::adjacent | std::views::adjacent; + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | twice; + assert(std::ranges::size(v) == (N <= 5 ? 10 - 2 * N : 0)); + + if (std::ranges::size(v) == 0) + return; + + auto it = v.begin(); + auto nestedTuple = *it; + + auto innerFirstTuple = std::get<0>(nestedTuple); + + assert(std::get<0>(innerFirstTuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(innerFirstTuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(innerFirstTuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<3>(innerFirstTuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<4>(innerFirstTuple) == buffer[4]); + + if constexpr (N >= 2) { + auto innerSecondTuple = std::get<1>(nestedTuple); + assert(std::get<0>(innerSecondTuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<1>(innerSecondTuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<2>(innerSecondTuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<3>(innerSecondTuple) == buffer[4]); + } + } +} + +constexpr void test_pairwise() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // Test `views::pairwise(r)` + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = std::views::pairwise(ContiguousCommonView{buffer}); + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + + { + // Test `views::pairwise(move only view)` + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = std::views::pairwise(MoveOnlyView{buffer}); + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + { + // Test `r | views::pairwise` + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | std::views::pairwise; + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + { + // Test `move only view | views::pairwise` + using View = std::ranges::adjacent_view; + std::same_as decltype(auto) v = MoveOnlyView{buffer} | std::views::pairwise; + assert(std::ranges::size(v) == 7); + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + assert(std::get<1>(tuple) == buffer[1]); + } + { + // Test pairwise | pairwise + using View = std::ranges::adjacent_view, 2>; + + auto twice = std::views::pairwise | std::views::pairwise; + std::same_as decltype(auto) v = ContiguousCommonView{buffer} | twice; + assert(std::ranges::size(v) == 6); + + auto it = v.begin(); + auto nestedTuple = *it; + + auto innerFirstTuple = std::get<0>(nestedTuple); + + assert(std::get<0>(innerFirstTuple) == buffer[0]); + assert(std::get<1>(innerFirstTuple) == buffer[1]); + + auto innerSecondTuple = std::get<1>(nestedTuple); + assert(std::get<0>(innerSecondTuple) == buffer[1]); + assert(std::get<1>(innerSecondTuple) == buffer[2]); + } +} + +constexpr bool test() { + test_all_constraints(); + test_zero_case(); + test_pairwise(); + test<1>(); + test<2>(); + test<3>(); + test<5>(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp new file mode 100644 index 0000000000000..c09231d672288 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/base.pass.cpp @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// 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 View base() const& requires copy_constructible; +// constexpr View base() &&; + +#include + +#include +#include +#include + +struct Range : std::ranges::view_base { + template + constexpr explicit Range(int (&buffer)[N]) : begin_(&buffer[0]), end_(&buffer[0] + N) {} + constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {} + constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {} + Range& operator=(Range const&) = default; + Range& operator=(Range&&) = default; + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + + int* begin_; + int* end_; + bool wasCopyInitialized = false; + bool wasMoveInitialized = false; +}; + +static_assert(std::ranges::view); +static_assert(std::ranges::forward_range); + +struct NonCopyableRange : std::ranges::view_base { + explicit NonCopyableRange(int*, int*); + NonCopyableRange(NonCopyableRange const&) = delete; + NonCopyableRange(NonCopyableRange&&) = default; + NonCopyableRange& operator=(NonCopyableRange const&) = default; + NonCopyableRange& operator=(NonCopyableRange&&) = default; + int* begin() const; + int* end() const; +}; + +static_assert(!std::copy_constructible); + +template +concept CanCallBaseOn = requires(T t) { std::forward(t).base(); }; + +template +constexpr void test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + // Check the const& overload + { + Range range(buff); + auto view = range | std::views::adjacent; + + std::same_as decltype(auto) result = view.base(); + assert(result.wasCopyInitialized); + assert(result.begin() == buff); + assert(result.end() == buff + 9); + } + + // Check the && overload + { + Range range(buff); + auto view = range | std::views::adjacent; + std::same_as decltype(auto) result = std::move(view).base(); + assert(result.wasMoveInitialized); + assert(result.begin() == buff); + assert(result.end() == buff + 9); + } + + // Ensure the const& overload is not considered when the base is not copy-constructible + { + static_assert(!CanCallBaseOn const&>); + static_assert(!CanCallBaseOn&>); + static_assert(!CanCallBaseOn const&&>); + static_assert(CanCallBaseOn&&>); + static_assert(CanCallBaseOn>); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp new file mode 100644 index 0000000000000..c17138bd6a5a3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/begin.pass.cpp @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// +// 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() requires (!(simple-view && ...)); +// constexpr auto begin() const requires (range && ...); + +#include + +#include +#include +#include +#include + +#include "../range_adaptor_types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = HasConstBegin && requires(T& t, const T& ct) { + requires !std::same_as; +}; + +template +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +struct NoConstBeginView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +template +constexpr void test_one() { + using View = std::ranges::adjacent_view; + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + View v(Range{buffer}); + + auto it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buffer[2]); + + auto cit = std::as_const(v).begin(); + auto ctuple = *cit; + assert(std::get<0>(ctuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(ctuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(ctuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<3>(ctuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<4>(ctuple) == buffer[4]); + } + + { + // empty range + std::array buffer = {}; + View v(Range{buffer.data(), 0}); + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + assert(it == v.end()); + assert(cit == std::as_const(v).end()); + } + + if constexpr (N > 2) { + // N greater than range size + int buffer[2] = {1, 2}; + View v(Range{buffer}); + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + assert(it == v.end()); + assert(cit == std::as_const(v).end()); + } +} + +template +constexpr void test_simple() { + test_one(); + + using View = std::ranges::adjacent_view; + static_assert(std::is_same_v, std::ranges::iterator_t>); +} + +template +constexpr void test_non_simple() { + test_one(); + + using View = std::ranges::adjacent_view; + static_assert(!std::is_same_v, std::ranges::iterator_t>); +} + +template +constexpr void test() { + test_simple(); + test_non_simple(); + + // Test with view that doesn't support const begin() + using ViewWithNoConstBegin = std::ranges::adjacent_view; + static_assert(!HasOnlyConstBegin); + static_assert(HasOnlyNonConstBegin); + static_assert(!HasConstAndNonConstBegin); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp new file mode 100644 index 0000000000000..75e70c2bb95dc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/borrowing.compile.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template +// inline constexpr bool enable_borrowed_range> = +// enable_borrowed_range; + +#include + +struct Borrowed : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(std::ranges::borrowed_range); + +struct NonBorrowed : std::ranges::view_base { + int* begin() const; + int* end() const; +}; +static_assert(!std::ranges::borrowed_range); + +// test borrowed_range +static_assert(std::ranges::borrowed_range>); +static_assert(std::ranges::borrowed_range>); +static_assert(std::ranges::borrowed_range>); +static_assert(!std::ranges::borrowed_range>); +static_assert(!std::ranges::borrowed_range>); +static_assert(!std::ranges::borrowed_range>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp new file mode 100644 index 0000000000000..c8a8a9c740281 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.default.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// adjacent_view() default_initializable = default; + +#include + +#include +#include +#include + +constexpr int buff[] = {1, 2, 3, 4, 5}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + std::ranges::size(buff)) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NoDefaultCtrView : std::ranges::view_base { + NoDefaultCtrView() = delete; + int* begin() const; + int* end() const; +}; + +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>); +static_assert(!std::is_default_constructible_v>); + +template +constexpr void test() { + { + using View = std::ranges::adjacent_view; + View v = View(); // the default constructor is not explicit + assert(v.size() == std::ranges::size(buff) - (N - 1)); + auto tuple = *v.begin(); + assert(std::get<0>(tuple) == buff[0]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buff[1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buff[2]); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp new file mode 100644 index 0000000000000..647ca147fdc09 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/ctor.views.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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 adjacent_view(View) + +#include +#include + +#include "../range_adaptor_types.h" + +template +void conversion_test(T); + +template +concept implicitly_constructible_from = requires(Args&&... args) { conversion_test({std::move(args)...}); }; + +// test constructor is explicit +static_assert(std::constructible_from, SimpleCommon>); +static_assert(!implicitly_constructible_from, SimpleCommon>); + +static_assert(std::constructible_from, SimpleCommon>); +static_assert(!implicitly_constructible_from, SimpleCommon>); + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +template +constexpr void constructorTest(auto&& buffer) { + std::ranges::adjacent_view v{View{buffer}}; + auto tuple = *v.begin(); + assert(std::get<0>(tuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buffer[2]); +}; + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // arguments are moved once + MoveAwareView mv; + std::ranges::adjacent_view v{std::move(mv)}; + auto tuple = *v.begin(); + assert(std::get<0>(tuple) == 2); // one move from the local variable to parameter, one move from parameter to member + } + + // forward + { + constructorTest(buffer); + } + + // bidi + { + constructorTest(buffer); + } + + // random_access + { + constructorTest(buffer); + } + + // contiguous + { + constructorTest(buffer); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.pass.cpp new file mode 100644 index 0000000000000..3ce45f49920fe --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/end.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 auto end() requires(!simple-view<_View>) +// constexpr auto end() const requires range + +#include +#include +#include +#include +#include + +#include "../range_adaptor_types.h" + +template +constexpr void test_one() { + { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::adjacent_view v{Underlying{buffer}}; + + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + auto endIt = v.end(); + auto cendIt = std::as_const(v).end(); + assert(it != endIt); + assert(cit != cendIt); + assert(it + (8 - (N - 1)) == endIt); + assert(cit + (8 - (N - 1)) == cendIt); + } + { + // empty range + std::array buffer = {}; + std::ranges::adjacent_view v{Underlying{buffer.data(), 0}}; + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + auto endIt = v.end(); + auto cendIt = std::as_const(v).end(); + assert(it == endIt); + assert(cit == cendIt); + } + if constexpr (N > 2) { + // N greater than range size + int buffer[2] = {1, 2}; + std::ranges::adjacent_view v{Underlying{buffer}}; + auto it = v.begin(); + auto cit = std::as_const(v).begin(); + auto endIt = v.end(); + auto cendIt = std::as_const(v).end(); + assert(it == endIt); + assert(cit == cendIt); + } +} + +template +constexpr void test_simple_common_types() { + using NonConstView = std::ranges::adjacent_view; + using ConstView = const NonConstView; + + static_assert(std::ranges::common_range); + static_assert(std::ranges::common_range); + static_assert(std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_simple_non_common_types() { + using NonConstView = std::ranges::adjacent_view; + using ConstView = const NonConstView; + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + static_assert(std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_non_simple_common_types() { + using NonConstView = std::ranges::adjacent_view; + using ConstView = const NonConstView; + + static_assert(std::ranges::common_range); + static_assert(std::ranges::common_range); + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_non_simple_non_common_types() { + using NonConstView = std::ranges::adjacent_view; + using ConstView = const NonConstView; + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test_forward_only() { + using NonConstView = std::ranges::adjacent_view; + using ConstView = const NonConstView; + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::common_range); + static_assert(!std::is_same_v, std::ranges::sentinel_t>); + + test_one(); +} + +template +constexpr void test() { + test_simple_common_types(); + test_simple_non_common_types(); + test_non_simple_common_types(); + test_non_simple_non_common_types(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/general.pass.cpp new file mode 100644 index 0000000000000..3c3716190a495 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/general.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 + +// Some basic examples of how adjacent_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 + +constexpr void test_adjacent_pairs() { + std::vector v = {1, 2, 3, 4}; + + std::pair expected_index{0, 1}; + for (auto [x, y] : v | std::views::adjacent<2>) { + assert(x == v[expected_index.first]); + assert(y == v[expected_index.second]); + assert(&x == &v[expected_index.first]); + assert(&y == &v[expected_index.second]); + ++expected_index.first; + ++expected_index.second; + } +} + +constexpr void test_string_view() { + std::string_view sv = "123456789"; + auto v = sv | std::views::adjacent<3>; + auto [a, b, c] = *v.begin(); + assert(a == '1'); + assert(b == '2'); + assert(c == '3'); +} + +constexpr bool test() { + test_adjacent_pairs(); + test_string_view(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h b/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h new file mode 100644 index 0000000000000..cef29689ea301 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/helpers.h @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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_ADJACENT_HELPERS_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H + +#include + +// intentionally not using meta programming for the expected tuple types + +template +struct ExpectedTupleType; + +template +struct ExpectedTupleType<1, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<2, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<3, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<4, T> { + using type = std::tuple; +}; +template +struct ExpectedTupleType<5, T> { + using type = std::tuple; +}; + +template +using expectedTupleType = typename ExpectedTupleType::type; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ADJACENT_HELPERS_H \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp new file mode 100644 index 0000000000000..671a5c3fec4d0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/arithmetic.pass.cpp @@ -0,0 +1,165 @@ +//===----------------------------------------------------------------------===// +// +// 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, iterator_t>; + +#include +#include + +#include +#include +#include + +#include "../../range_adaptor_types.h" + +template +concept canPlusEqual = requires(T& t, U& u) { t += u; }; + +template +concept canMinusEqual = requires(T& t, U& u) { t -= u; }; + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + const auto validateRefFromIndex = [&](auto&& tuple, std::size_t idx) { + assert(&std::get<0>(tuple) == &buffer[idx]); + if constexpr (N >= 2) + assert(&std::get<1>(tuple) == &buffer[idx + 1]); + if constexpr (N >= 3) + assert(&std::get<2>(tuple) == &buffer[idx + 2]); + if constexpr (N >= 4) + assert(&std::get<3>(tuple) == &buffer[idx + 3]); + if constexpr (N >= 5) + assert(&std::get<4>(tuple) == &buffer[idx + 4]); + }; + + R rng{buffer}; + { + // operator+(x, n) and operator+= + std::ranges::adjacent_view v(rng); + auto it1 = v.begin(); + + validateRefFromIndex(*it1, 0); + + auto it2 = it1 + 3; + validateRefFromIndex(*it2, 3); + + auto it3 = 3 + it1; + validateRefFromIndex(*it3, 3); + + it1 += 3; + assert(it1 == it2); + validateRefFromIndex(*it1, 3); + } + + { + // operator-(x, n) and operator-= + std::ranges::adjacent_view v(rng); + auto it1 = v.end(); + + auto it2 = it1 - 3; + validateRefFromIndex(*it2, 7 - N); + + it1 -= 3; + assert(it1 == it2); + validateRefFromIndex(*it1, 7 - N); + } + + { + // operator-(x, y) + std::ranges::adjacent_view v(rng); + assert((v.end() - v.begin()) == (10 - N)); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + assert((it1 - it2) == static_cast(N) - 7); + } + + { + // empty range + std::ranges::adjacent_view v(R{buffer, 0}); + assert((v.end() - v.begin()) == 0); + } + + { + // N > size of range + std::ranges::adjacent_view v(R{buffer, 2}); + assert((v.end() - v.begin()) == 0); + } +} + +template +constexpr void test() { + test(); + test(); + + { + // Non random access but sized sentinel + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + using View = std::ranges::adjacent_view; + using Iter = std::ranges::iterator_t; + using Diff = std::iter_difference_t; + + static_assert(!std::invocable, Iter, Diff>); + static_assert(!std::invocable, Diff, Iter>); + static_assert(!canPlusEqual); + static_assert(!std::invocable, Iter, Diff>); + static_assert(!canMinusEqual); + static_assert(std::invocable, Iter, Iter>); + + View v(ForwardSizedView{buffer}); + auto it1 = v.begin(); + auto it2 = v.end(); + assert((it2 - it1) == (10 - N)); + } + + { + // Non random access and non-sized sentinel + using View = std::ranges::adjacent_view; + using Iter = std::ranges::iterator_t; + using Diff = std::iter_difference_t; + + static_assert(!std::invocable, Iter, Diff>); + static_assert(!std::invocable, Diff, Iter>); + static_assert(!canPlusEqual); + static_assert(!std::invocable, Iter, Diff>); + static_assert(!canMinusEqual); + static_assert(!std::invocable, Iter, Iter>); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp new file mode 100644 index 0000000000000..8461d7fa4a53c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/compare.pass.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// 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); +// friend constexpr bool operator<(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires random_access_range && +// three_way_comparable>; + +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +#include "../../range_adaptor_types.h" + +constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) { + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); +} + +constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) { + using Iter1 = decltype(iter1); + using Iter2 = decltype(iter2); + static_assert(!std::is_invocable_v, 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>); +} + +template +constexpr void test() { + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator; + using SubRange = std::ranges::subrange; + static_assert(std::three_way_comparable); + using R = std::ranges::adjacent_view; + static_assert(std::three_way_comparable>); + + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto r = R{SubRange(It(buffer), It(buffer + 8))}; + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + } + + { + // Test an old-school iterator with no operator<=>; the adjacent iterator shouldn't have + // operator<=> either. + using It = random_access_iterator; + using SubRange = std::ranges::subrange; + static_assert(!std::three_way_comparable); + using R = std::ranges::adjacent_view; + static_assert(!std::three_way_comparable>); + + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto r = R{SubRange(It(buffer), It(buffer + 8))}; + auto iter1 = r.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + } + + { + // non random_access_range + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + std::ranges::adjacent_view v(BidiCommonView{buffer}); + using View = decltype(v); + static_assert(!std::ranges::random_access_range); + + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 != it2); + + // advance it1 to the end + std::ranges::advance(it1, 9 - N); + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // empty range + auto v = std::views::empty | std::views::adjacent; + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 == it2); + } + + { + // N > size of range + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + auto v = std::ranges::adjacent_view(ContiguousCommonView{buffer}); + auto it1 = v.begin(); + auto it2 = v.end(); + assert(it1 == it2); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp new file mode 100644 index 0000000000000..5c1fb15b443e0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.default.pass.cpp @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include "../../range_adaptor_types.h" + +struct PODIter { + int i; // deliberately uninitialised + + using iterator_category = std::random_access_iterator_tag; + using value_type = int; + using difference_type = std::intptr_t; + + constexpr int operator*() const { return i; } + + constexpr PODIter& operator++() { return *this; } + constexpr PODIter operator++(int) { return *this; } + + friend constexpr bool operator==(const PODIter&, const PODIter&) = default; +}; + +struct IterDefaultCtrView : std::ranges::view_base { + PODIter begin() const; + PODIter end() const; +}; + +struct IterNoDefaultCtrView : std::ranges::view_base { + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +using adjacent_iter = std::ranges::iterator_t>; + +template +constexpr void test() { + using View = std::ranges::adjacent_view; + using Iter = std::ranges::iterator_t; + { + Iter iter; + auto tuple = *iter; + assert((std::get<0>(tuple) == 0)); + if constexpr (N >= 2) + assert((std::get<1>(tuple) == 0)); + if constexpr (N >= 3) + assert((std::get<2>(tuple) == 0)); + if constexpr (N >= 4) + assert((std::get<3>(tuple) == 0)); + if constexpr (N >= 5) + assert((std::get<4>(tuple) == 0)); + } + + { + Iter iter = {}; + auto tuple = *iter; + assert((std::get<0>(tuple) == 0)); + if constexpr (N >= 2) + assert((std::get<1>(tuple) == 0)); + if constexpr (N >= 3) + assert((std::get<2>(tuple) == 0)); + if constexpr (N >= 4) + assert((std::get<3>(tuple) == 0)); + if constexpr (N >= 5) + assert((std::get<4>(tuple) == 0)); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp new file mode 100644 index 0000000000000..7d47ca7761ae1 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/ctor.other.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator(iterator i) +// requires Const && convertible_to, iterator_t>; + +#include + +#include +#include + +#include "../../range_adaptor_types.h" + +using ConstIterIncompatibleView = + BasicView, + forward_iterator, + random_access_iterator, + random_access_iterator>; +static_assert(!std::convertible_to, + std::ranges::iterator_t>); + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + std::ranges::adjacent_view v(NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + + static_assert(!std::is_same_v); + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + + assert(iter1 == iter2); + + auto tuple = *iter2; + assert(std::get<0>(tuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<3>(tuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<4>(tuple) == buffer[4]); + } + + { + // underlying non-const to const not convertible + std::ranges::adjacent_view v(ConstIterIncompatibleView{buffer}); + auto iter1 = v.begin(); + auto iter2 = std::as_const(v).begin(); + + static_assert(!std::is_same_v); + + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp new file mode 100644 index 0000000000000..155b3d40a7378 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/decrement.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include + +#include "../../range_adaptor_types.h" + +template +concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; }; + +template +constexpr void test_one() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + const auto validateRefFromIndex = [&](auto&& tuple, std::size_t idx) { + assert(&std::get<0>(tuple) == &buffer[idx]); + if constexpr (N >= 2) + assert(&std::get<1>(tuple) == &buffer[idx + 1]); + if constexpr (N >= 3) + assert(&std::get<2>(tuple) == &buffer[idx + 2]); + if constexpr (N >= 4) + assert(&std::get<3>(tuple) == &buffer[idx + 3]); + if constexpr (N >= 5) + assert(&std::get<4>(tuple) == &buffer[idx + 4]); + }; + + { + auto v = R(buffer) | std::views::adjacent; + + auto it = v.begin(); + using Iter = decltype(it); + + std::ranges::advance(it, v.end()); + + --it; + validateRefFromIndex(*it, 9 - N); + + static_assert(std::is_same_v); + std::same_as decltype(auto) it_ref = --it; + assert(&it_ref == &it); + + validateRefFromIndex(*it, 8 - N); + + std::same_as decltype(auto) tmp = it--; + + validateRefFromIndex(*tmp, 8 - N); + validateRefFromIndex(*it, 7 - N); + + // Decrement to the beginning + for (int i = 6 - N; i >= 0; --i) { + --it; + validateRefFromIndex(*it, i); + } + assert(it == v.begin()); + } +} + +template +constexpr void test() { + test_one(); + test_one(); + test_one(); + + // Non-bidirectional base range + { + using View = std::ranges::adjacent_view; + using Iter = std::ranges::iterator_t; + + static_assert(!canDecrement); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp new file mode 100644 index 0000000000000..f1e516c11701b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/deref.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// 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 operator*() const; + +#include +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // simple case + auto v = buffer | std::views::adjacent; + std::same_as> decltype(auto) res = *v.begin(); + + assert(&std::get<0>(res) == &buffer[0]); + if constexpr (N >= 2) + assert(&std::get<1>(res) == &buffer[1]); + if constexpr (N >= 3) + assert(&std::get<2>(res) == &buffer[2]); + if constexpr (N >= 4) + assert(&std::get<3>(res) == &buffer[3]); + if constexpr (N >= 5) + assert(&std::get<4>(res) == &buffer[4]); + } + + { + // operator* is const + auto v = buffer | std::views::adjacent; + const auto it = v.begin(); + std::same_as> decltype(auto) res = *it; + assert(&std::get<0>(res) == &buffer[0]); + if constexpr (N >= 2) + assert(&std::get<1>(res) == &buffer[1]); + if constexpr (N >= 3) + assert(&std::get<2>(res) == &buffer[2]); + if constexpr (N >= 4) + assert(&std::get<3>(res) == &buffer[3]); + if constexpr (N >= 5) + assert(&std::get<4>(res) == &buffer[4]); + } + + { + // underlying range with prvalue range_reference_t + auto v = std::views::iota(0, 8) | std::views::adjacent; + std::same_as> decltype(auto) res = *v.begin(); + assert(std::get<0>(res) == 0); + if constexpr (N >= 2) + assert(std::get<1>(res) == 1); + if constexpr (N >= 3) + assert(std::get<2>(res) == 2); + if constexpr (N >= 4) + assert(std::get<3>(res) == 3); + if constexpr (N >= 5) + assert(std::get<4>(res) == 4); + } + + { + // const-correctness + const std::array bufferConst = {1, 2, 3, 4, 5, 6, 7, 8}; + auto v = bufferConst | std::views::adjacent; + std::same_as> decltype(auto) res = *v.begin(); + assert(&std::get<0>(res) == &bufferConst[0]); + if constexpr (N >= 2) + assert(&std::get<1>(res) == &bufferConst[1]); + if constexpr (N >= 3) + assert(&std::get<2>(res) == &bufferConst[2]); + if constexpr (N >= 4) + assert(&std::get<3>(res) == &bufferConst[3]); + if constexpr (N >= 5) + assert(&std::get<4>(res) == &bufferConst[4]); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp new file mode 100644 index 0000000000000..d1dab943d2785 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/increment.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr iterator& operator++(); +// constexpr iterator operator++(int); + +#include +#include +#include +#include +#include + +#include "../../range_adaptor_types.h" + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + const auto validateRefFromIndex = [&](auto&& tuple, std::size_t idx) { + assert(&std::get<0>(tuple) == &buffer[idx]); + if constexpr (N >= 2) + assert(&std::get<1>(tuple) == &buffer[idx + 1]); + if constexpr (N >= 3) + assert(&std::get<2>(tuple) == &buffer[idx + 2]); + if constexpr (N >= 4) + assert(&std::get<3>(tuple) == &buffer[idx + 3]); + if constexpr (N >= 5) + assert(&std::get<4>(tuple) == &buffer[idx + 4]); + }; + + { + auto v = R(buffer) | std::views::adjacent; + auto it = v.begin(); + using Iter = decltype(it); + + validateRefFromIndex(*it, 0); + + std::same_as decltype(auto) it_ref = ++it; + assert(&it_ref == &it); + + validateRefFromIndex(*it, 1); + + static_assert(std::is_same_v); + auto original = it; + std::same_as decltype(auto) copy = it++; + assert(original == copy); + + validateRefFromIndex(*copy, 1); + validateRefFromIndex(*it, 2); + + // Increment to the end + for (std::size_t i = 3; i != 9 - N; ++i) { + ++it; + validateRefFromIndex(*it, i); + } + + ++it; + assert(it == v.end()); + } +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp new file mode 100644 index 0000000000000..0d228c79e48ea --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_move.pass.cpp @@ -0,0 +1,154 @@ +//===----------------------------------------------------------------------===// +// +// 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 auto iter_move(const iterator& i) noexcept(see below); + +#include +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&) {} +}; + +class IterMoveMayThrowIter { + int* it_; + +public: + using value_type = int; + using difference_type = typename std::iterator_traits::difference_type; + + constexpr IterMoveMayThrowIter() = default; + explicit constexpr IterMoveMayThrowIter(int* it) : it_(it) {} + + friend constexpr decltype(auto) iter_move(const IterMoveMayThrowIter& it) noexcept(false) { + return std::ranges::iter_move(it.it_); + } + + friend constexpr bool operator==(const IterMoveMayThrowIter& x, const IterMoveMayThrowIter& y) { + return x.it_ == y.it_; + } + + constexpr decltype(auto) operator*() const { return *it_; } + constexpr IterMoveMayThrowIter& operator++() { + ++it_; + return *this; + } + constexpr IterMoveMayThrowIter operator++(int) { + auto tmp(*this); + ++(*this); + return tmp; + } +}; + +class IterMoveMayThrowRange { + int* buffer_; + std::size_t size_; + +public: + constexpr IterMoveMayThrowRange(int* buffer, std::size_t size) : buffer_(buffer), size_(size) {} + constexpr IterMoveMayThrowIter begin() const { return IterMoveMayThrowIter{buffer_}; } + constexpr IterMoveMayThrowIter end() const { return IterMoveMayThrowIter{buffer_ + size_}; } +}; + +template +constexpr void test() { + { + // underlying iter_move noexcept + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + auto v = buffer | std::views::adjacent; + + auto it = v.begin(); + static_assert(noexcept(std::ranges::iter_move(it))); + + std::same_as> decltype(auto) res = std::ranges::iter_move(it); + + assert(&std::get<0>(res) == &buffer[0]); + if constexpr (N >= 2) + assert(&std::get<1>(res) == &buffer[1]); + if constexpr (N >= 3) + assert(&std::get<2>(res) == &buffer[2]); + if constexpr (N >= 4) + assert(&std::get<3>(res) == &buffer[3]); + if constexpr (N >= 5) + assert(&std::get<4>(res) == &buffer[4]); + } + + { + // underlying iter_move may throw + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + auto v = IterMoveMayThrowRange{buffer, 9} | std::views::adjacent; + + auto it = v.begin(); + static_assert(!noexcept(std::ranges::iter_move(it))); + + std::same_as> decltype(auto) res = std::ranges::iter_move(it); + assert(&std::get<0>(res) == &buffer[0]); + if constexpr (N >= 2) + assert(&std::get<1>(res) == &buffer[1]); + if constexpr (N >= 3) + assert(&std::get<2>(res) == &buffer[2]); + if constexpr (N >= 4) + assert(&std::get<3>(res) == &buffer[3]); + if constexpr (N >= 5) + assert(&std::get<4>(res) == &buffer[4]); + } + + { + // !is_nothrow_move_constructible_v> + // underlying iter_move may throw + auto throwingMoveRange = + std::views::iota(0, 9) | std::views::transform([](auto) noexcept { return ThrowingMove{}; }); + auto v = throwingMoveRange | std::views::adjacent; + auto it = v.begin(); + static_assert(!noexcept(std::ranges::iter_move(it))); + } + + { + // underlying iterators' iter_move are called through ranges::iter_move + auto rng = adltest::IterMoveSwapRange{}; + auto v = rng | std::views::adjacent; + assert(rng.iter_move_called_times == 0); + auto it = v.begin(); + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(rng.iter_move_called_times == N); + } + { + [[maybe_unused]] auto&& i = std::ranges::iter_move(it); + assert(rng.iter_move_called_times == 2 * N); + } + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp new file mode 100644 index 0000000000000..36be63dc6594b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/iter_swap.pass.cpp @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// +// 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 void iter_swap(const iterator& l, const iterator& r) noexcept(see below) +// requires indirectly_swappable>; + +#include +#include +#include +#include + +#include "../helpers.h" +#include "../../range_adaptor_types.h" + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&) {} + ThrowingMove& operator=(ThrowingMove&&) { return *this; } +}; + +template +constexpr void test() { + { + // underlying iter_swap noexcept + int buffer[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + }; + + auto v = buffer | std::views::adjacent; + + auto iter1 = v.begin(); + auto iter2 = v.begin() + 10; + static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); + + std::ranges::iter_swap(iter1, iter2); + + assert(buffer[0] == 11); + assert(buffer[10] == 1); + if constexpr (N >= 2) { + assert(buffer[1] == 12); + assert(buffer[11] == 2); + } + if constexpr (N >= 3) { + assert(buffer[2] == 13); + assert(buffer[12] == 3); + } + if constexpr (N >= 4) { + assert(buffer[3] == 14); + assert(buffer[13] == 4); + } + if constexpr (N >= 5) { + assert(buffer[4] == 15); + assert(buffer[14] == 5); + } + + auto tuple1 = *iter1; + auto tuple2 = *iter2; + assert(&std::get<0>(tuple1) == &buffer[0]); + assert(&std::get<0>(tuple2) == &buffer[10]); + + if constexpr (N >= 2) { + assert(&std::get<1>(tuple1) == &buffer[1]); + assert(&std::get<1>(tuple2) == &buffer[11]); + } + if constexpr (N >= 3) { + assert(&std::get<2>(tuple1) == &buffer[2]); + assert(&std::get<2>(tuple2) == &buffer[12]); + } + if constexpr (N >= 4) { + assert(&std::get<3>(tuple1) == &buffer[3]); + assert(&std::get<3>(tuple2) == &buffer[13]); + } + if constexpr (N >= 5) { + assert(&std::get<4>(tuple1) == &buffer[4]); + assert(&std::get<4>(tuple2) == &buffer[14]); + } + } + + { + // underlying iter_swap may throw + std::array iterSwapMayThrow{}; + auto v = iterSwapMayThrow | std::views::adjacent; + auto iter1 = v.begin(); + auto iter2 = ++v.begin(); + static_assert(!noexcept(std::ranges::iter_swap(iter1, iter2))); + } + + { + // underlying iterators' iter_swap are called through ranges::iter_swap + auto rng = adltest::IterMoveSwapRange{}; + auto v = rng | std::views::adjacent; + assert(rng.iter_move_called_times == 0); + auto it1 = v.begin(); + auto it2 = std::ranges::next(it1, 3); + + std::ranges::iter_swap(it1, it2); + assert(rng.iter_swap_called_times == 2 * N); + + std::ranges::iter_swap(it1, it2); + assert(rng.iter_swap_called_times == 4 * N); + } + + { + // !indirectly_swappable>; + + const int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + auto v = buffer | std::views::adjacent; + auto it1 = v.begin(); + auto it2 = v.begin() + 1; + static_assert(!std::invocable); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp new file mode 100644 index 0000000000000..fc70fc4eed162 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/member_types.compile.pass.cpp @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// 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 adjacent_view::iterator. + +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../../range_adaptor_types.h" + +template +struct DiffTypeIter : random_access_iterator { + using value_type = int; + using difference_type = T; + + int operator*() const; + DiffTypeIter& operator++(); + DiffTypeIter operator++(int); + friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default; +}; + +template +struct DiffTypeView : std::ranges::view_base { + DiffTypeIter begin() const; + DiffTypeIter end() const; +}; + +struct Foo {}; + +struct ConstVeryDifferentRange { + int* begin(); + int* end(); + + forward_iterator begin() const; + forward_iterator end() const; +}; + +template +void test() { + int buffer[] = {1, 2, 3, 4}; + + const auto expectedTupleType = [] { + if constexpr (N == 1) + return std::tuple{}; + else if constexpr (N == 2) + return std::tuple{}; + else if constexpr (N == 3) + return std::tuple{}; + else if constexpr (N == 4) + return std::tuple{}; + else if constexpr (N == 5) + return std::tuple{}; + }; + + using expected_value_type = decltype(expectedTupleType()); + + { + // Base contiguous range + std::ranges::adjacent_view, N> v(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); + } + + { + // Base random access + std::ranges::adjacent_view v(SizedRandomAccessView{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); + } + + { + // Base bidirectional + std::ranges::adjacent_view v(BidiCommonView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // Base forward + std::ranges::adjacent_view v(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); + } + + { + // difference_type + std::ranges::adjacent_view, N> v{DiffTypeView{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + + { + // value_type + const std::array foos{Foo{}}; + auto v = std::views::adjacent<2>(foos); + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + { + // const-iterator different from iterator + auto v = ConstVeryDifferentRange{} | std::views::adjacent<2>; + using Iter = decltype(v.begin()); + using ConstIter = decltype(std::as_const(v).begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v>); + } +} + +void test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp new file mode 100644 index 0000000000000..de912ca50bac7 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/singular.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: no-exceptions + +// If the invocation of any non-const member function of `iterator` exits via an +// exception, the iterator acquires a singular value. + +#include + +#include + +#include "../../range_adaptor_types.h" + +struct ThrowOnDecrementIterator { + int* it_; + + using value_type = int; + using difference_type = std::intptr_t; + + ThrowOnDecrementIterator() = default; + explicit ThrowOnDecrementIterator(int* it) : it_(it) {} + + ThrowOnDecrementIterator& operator++() { + ++it_; + return *this; + } + ThrowOnDecrementIterator operator++(int) { + auto tmp = *this; + ++it_; + return tmp; + } + + ThrowOnDecrementIterator& operator--() { throw 5; } + ThrowOnDecrementIterator operator--(int) { throw 5; } + + int& operator*() const { return *it_; } + + friend bool operator==(ThrowOnDecrementIterator const&, ThrowOnDecrementIterator const&) = default; +}; + +struct ThrowOnIncrementView : IntBufferView { + ThrowOnDecrementIterator begin() const { return ThrowOnDecrementIterator{buffer_}; } + ThrowOnDecrementIterator end() const { return ThrowOnDecrementIterator{buffer_ + size_}; } +}; + +template +void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8}; + { + // adjacent_view iterator should be able to be destroyed after member function throws + auto v = ThrowOnIncrementView{buffer} | std::views::adjacent; + auto it = v.begin(); + ++it; + try { + --it; + assert(false); // should not be reached as the above expression should throw. + } catch (int e) { + assert(e == 5); + } + } + + { + // adjacent_view iterator should be able to be assigned after member function throws + auto v = ThrowOnIncrementView{buffer} | std::views::adjacent; + auto it = v.begin(); + ++it; + try { + --it; + assert(false); // should not be reached as the above expression should throw. + } catch (int e) { + assert(e == 5); + } + it = v.begin(); + auto tuple = *it; + assert(std::get<0>(tuple) == buffer[0]); + if constexpr (N >= 2) + assert(std::get<1>(tuple) == buffer[1]); + if constexpr (N >= 3) + assert(std::get<2>(tuple) == buffer[2]); + if constexpr (N >= 4) + assert(std::get<3>(tuple) == buffer[3]); + if constexpr (N >= 5) + assert(std::get<4>(tuple) == buffer[4]); + } +} + +void test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp new file mode 100644 index 0000000000000..389fbe9dc98d4 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/iterator/subscript.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto operator[](difference_type n) const requires +// all_random_access + +#include +#include + +#include "../../range_adaptor_types.h" + +template +constexpr void test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // random_access_range + std::ranges::adjacent_view v(SizedRandomAccessView{buffer}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + + static_assert(std::is_same_v); + } + + { + // contiguous_range + std::ranges::adjacent_view v(ContiguousCommonView{buffer}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + + static_assert(std::is_same_v); + } + + { + // non random_access_range + std::ranges::adjacent_view v(BidiCommonView{buffer}); + auto iter = v.begin(); + const auto canSubscript = [](auto&& it) { return requires { it[0]; }; }; + static_assert(!canSubscript(iter)); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp new file mode 100644 index 0000000000000..674182592a281 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/range.concept.compile.pass.cpp @@ -0,0 +1,135 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// test if adjacent_view models input_range, forward_range, bidirectional_range, +// random_access_range, contiguous_range, common_range +// sized_range + +#include +#include +#include +#include +#include + +#include "../range_adaptor_types.h" + +template +constexpr bool testConcept() { + int buffer[3] = {1, 2, 3}; + { + std::ranges::adjacent_view v(ContiguousCommonView{buffer}); + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{ContiguousNonCommonView{buffer}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{ContiguousNonCommonSized{buffer}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{SizedRandomAccessView{buffer}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{NonSizedRandomAccessView{buffer}}; + using View = decltype(v); + static_assert(std::ranges::random_access_range); + static_assert(!std::ranges::contiguous_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{BidiCommonView{buffer}}; + using View = decltype(v); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::random_access_range); + static_assert(std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{BidiNonCommonView{buffer}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{ForwardSizedView{buffer}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + { + std::ranges::adjacent_view v{ForwardSizedNonCommon{buffer}}; + using View = decltype(v); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::bidirectional_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + } + + return true; +} + +static_assert(testConcept<1>()); +static_assert(testConcept<2>()); +static_assert(testConcept<3>()); +static_assert(testConcept<5>()); + +using OutputIter = cpp17_output_iterator; +static_assert(std::output_iterator); + +struct OutputView : std::ranges::view_base { + OutputIter begin() const; + sentinel_wrapper end() const; +}; +static_assert(std::ranges::output_range); +static_assert(!std::ranges::input_range); + +template +concept adjacent_viewable = requires { typename std::ranges::adjacent_view; }; + +static_assert(adjacent_viewable); + +// output_range is not supported +static_assert(!adjacent_viewable); + +// input only range is not supported +static_assert(!adjacent_viewable); +static_assert(!adjacent_viewable); diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp new file mode 100644 index 0000000000000..00a6cee577741 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.default.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +struct PODSentinel { + bool b; // deliberately uninitialised + + friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + PODSentinel end(); +}; + +template +constexpr void test() { + { + using R = std::ranges::adjacent_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 + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp new file mode 100644 index 0000000000000..7a3ebbd94ca9d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/ctor.other.pass.cpp @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// +// 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 s); +// requires Const && convertible_to, sentinel_t>;; + +#include +#include + +#include "../../range_adaptor_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 SentinelConvertibleView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr convertible_sentinel_wrapper end() { return convertible_sentinel_wrapper(buffer_ + size_); } + constexpr convertible_sentinel_wrapper end() const { + return convertible_sentinel_wrapper(buffer_ + size_); + } +}; + +static_assert(!std::ranges::common_range); +static_assert(std::convertible_to, + std::ranges::sentinel_t>); +static_assert(!simple_view); + +struct SentinelNonConvertibleView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr int* begin() { return buffer_; } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() { return sentinel_wrapper(buffer_ + size_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } +}; + +static_assert(!std::ranges::common_range); +static_assert(!std::convertible_to, + std::ranges::sentinel_t>); +static_assert(!simple_view); + +template +constexpr void test() { + using View = std::ranges::adjacent_view; + static_assert(!std::ranges::common_range); + + using Sent = std::ranges::sentinel_t; + using ConstSent = std::ranges::sentinel_t; + static_assert(!std::is_same_v); + + { + // implicitly convertible + static_assert(std::convertible_to); + } + { + // !Const + static_assert(!std::convertible_to); + } + { + // !convertible_to, iterator_t> + using V2 = std::ranges::adjacent_view; + static_assert(!std::convertible_to, std::ranges::sentinel_t>); + } + + { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + View v{SentinelConvertibleView{buffer}}; + Sent sent1 = v.end(); + ConstSent sent2 = sent1; + + assert(v.begin() != sent2); + assert(std::as_const(v).begin() != sent2); + assert(v.begin() + (10 - N) == sent2); + assert(std::as_const(v).begin() + (10 - N) == sent2); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp new file mode 100644 index 0000000000000..a79107c69fb4b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/eq.pass.cpp @@ -0,0 +1,195 @@ +//===----------------------------------------------------------------------===// +// +// 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, iterator_t>> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include +#include +#include +#include + +#include "../../range_adaptor_types.h" +#include "test_iterators.h" +#include "test_range.h" + +using Iterator = random_access_iterator; +using ConstIterator = contiguous_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 : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr random_access_iterator begin() { return random_access_iterator(buffer_); } + constexpr contiguous_iterator begin() const { return contiguous_iterator(buffer_); } + constexpr sentinel_wrapper> end() { + return sentinel_wrapper>(random_access_iterator(buffer_ + size_)); + } + constexpr sentinel_wrapper> end() const { + return sentinel_wrapper>(contiguous_iterator(buffer_ + size_)); + } +}; + +template +constexpr bool test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + { + // simple-view: const and non-const have the same iterator/sentinel type + using View = std::ranges::adjacent_view; + static_assert(!std::ranges::common_range); + static_assert(simple_view); + + View v{SimpleNonCommon(buffer)}; + + assert(v.begin() != v.end()); + assert(v.begin() + 1 != v.end()); + assert(v.begin() + 2 != v.end()); + assert(v.begin() + 3 != v.end()); + assert(v.begin() + (10 - N) == v.end()); + } + + { + // !simple-view: const and non-const have different iterator/sentinel types + using View = std::ranges::adjacent_view; + 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(weakly_equality_comparable_with); + static_assert(!weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + + View v{NonSimpleNonCommon(buffer)}; + + assert(v.begin() != v.end()); + assert(v.begin() + (10 - N) == v.end()); + + assert(v.begin() != std::as_const(v).end()); + assert(v.begin() + (10 - N) == std::as_const(v).end()); + // the above works because + static_assert(std::convertible_to); + + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end()); + } + + { + // underlying const/non-const sentinel can be compared with both const/non-const iterator + using View = std::ranges::adjacent_view; + 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(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + + View v{ComparableView(buffer)}; + + assert(v.begin() != v.end()); + assert(v.begin() + (10 - N) == v.end()); + + static_assert(!std::convertible_to); + + assert(v.begin() != std::as_const(v).end()); + assert(v.begin() + (10 - N) == std::as_const(v).end()); + + assert(std::as_const(v).begin() != v.end()); + assert(std::as_const(v).begin() + (10 - N) == v.end()); + + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end()); + } + + { + // underlying const/non-const sentinel cannot be compared with non-const/const iterator + + using View = std::ranges::adjacent_view; + 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(weakly_equality_comparable_with); + static_assert(!weakly_equality_comparable_with); + static_assert(!weakly_equality_comparable_with); + static_assert(weakly_equality_comparable_with); + + View v{ConstIncompatibleView{buffer}}; + + assert(v.begin() != v.end()); + assert(v.begin() + (10 - N) == v.end()); + + assert(std::as_const(v).begin() != std::as_const(v).end()); + assert(std::as_const(v).begin() + (10 - N) == std::as_const(v).end()); + } + + return true; +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp new file mode 100644 index 0000000000000..9f3fada6013cb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/sentinel/minus.pass.cpp @@ -0,0 +1,185 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template +// requires sized_sentinel_for, iterator_t>> +// friend constexpr range_difference_t> +// operator-(const iterator& x, const sentinel& y); + +// template +// requires sized_sentinel_for, iterator_t>> +// friend constexpr range_difference_t> +// operator-(const sentinel& y, const iterator& x); + +#include +#include +#include +#include +#include + +#include "../../range_adaptor_types.h" + +// clang-format off +template +concept HasMinus = std::invocable,const T&, const U&>; + +template +concept SentinelHasMinus = HasMinus, std::ranges::iterator_t>; +// clang-format on + +template +constexpr void test() { + int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + { + // simple-view + using View = std::ranges::adjacent_view; + static_assert(!std::ranges::common_range); + static_assert(simple_view); + + View v{ForwardSizedNonCommon(buffer)}; + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == (10 - N)); + assert(st - std::ranges::next(it, 1) == (9 - N)); + + assert(it - st == (static_cast(N) - 10)); + assert(std::ranges::next(it, 1) - st == (static_cast(N) - 9)); + static_assert(SentinelHasMinus); + } + + { + // empty range + using View = std::ranges::adjacent_view; + View v{ForwardSizedNonCommon(buffer, 0)}; + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 0); + assert(it - st == 0); + } + + { + // N > size of underlying range + using View = std::ranges::adjacent_view; + View v{ForwardSizedNonCommon(buffer, 3)}; + + auto it = v.begin(); + auto st = v.end(); + assert(st - it == 0); + assert(it - st == 0); + } + + { + // underlying sentinel does not model sized_sentinel_for + using View = std::ranges::adjacent_view; + 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 + using View = std::ranges::adjacent_view; + 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); + + View v{NonSimpleForwardSizedNonCommon{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(); + + int n = N; + + assert(it - st == (n - 10)); + assert(st - it == (10 - n)); + assert(const_it - const_st == (n - 10)); + assert(const_st - const_it == (10 - n)); + + static_assert(!HasMinus); + static_assert(!HasMinus); + static_assert(!HasMinus); + static_assert(!HasMinus); + } + + { + // const compatible allow non-const to const conversion + using View = std::ranges::adjacent_view; + 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); + + View v{ConstCompatibleForwardSized{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(); + + int n = N; + + assert(it - st == (n - 10)); + assert(st - it == (10 - n)); + assert(const_it - const_st == (n - 10)); + assert(const_st - const_it == (10 - n)); + assert(it - const_st == (n - 10)); + assert(const_st - it == (10 - n)); + assert(const_it - st == (n - 10)); + assert(st - const_it == (10 - n)); + } +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp new file mode 100644 index 0000000000000..be88d27b54654 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/size.pass.cpp @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#include "../range_adaptor_types.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; +struct View : std::ranges::view_base { + std::size_t size_ = 0; + constexpr View(std::size_t s) : size_(s) {} + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + size_; } +}; + +struct SizedNonConst : std::ranges::view_base { + using iterator = forward_iterator; + std::size_t size_ = 0; + constexpr SizedNonConst(std::size_t s) : size_(s) {} + constexpr auto begin() const { return iterator{buffer}; } + constexpr auto end() const { return iterator{buffer + size_}; } + constexpr std::size_t size() { return size_; } +}; + +struct StrangeSizeView : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } + + constexpr auto size() { return 5; } + constexpr auto size() const { return 6; } +}; + +// Test with different values of N for a sized view +template +constexpr void test_sized_view() { + std::ranges::adjacent_view v(View(8)); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + + auto expected_size = 8 - (N - 1); + assert(v.size() == expected_size); + assert(std::as_const(v).size() == expected_size); +} + +// Test with different values of N for a non-const sized view +template +constexpr void test_nonconst_sized() { + std::ranges::adjacent_view v(SizedNonConst(5)); + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + + auto expected_size = 5 - (N - 1); + assert(v.size() == expected_size); +} + +// Test with different values of N for a view with different const/non-const sizes +template +constexpr void test_strange_size() { + std::ranges::adjacent_view v(StrangeSizeView{}); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + + assert(v.size() == 5 - (N - 1)); + assert(std::as_const(v).size() == 6 - (N - 1)); +} + +template +constexpr void test_empty_range() { + std::ranges::adjacent_view v(View(0)); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); +} + +template +constexpr void test_N_greater_than_size() { + if constexpr (N > 2) { + std::ranges::adjacent_view v(View(2)); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::sized_range); + assert(v.size() == 0); + assert(std::as_const(v).size() == 0); + } +} + +template +constexpr void test() { + test_sized_view(); + test_nonconst_sized(); + test_strange_size(); + test_empty_range(); + test_N_greater_than_size(); +} + +constexpr bool test() { + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp index 68fdfb5b8475d..10c092cf36f2c 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.join/adaptor.pass.cpp @@ -18,14 +18,14 @@ #include "test_range.h" #include "types.h" -struct MoveOnlyOuter : SimpleForwardCommonOuter { +struct MoveOnlyView : SimpleForwardCommonOuter { using SimpleForwardCommonOuter::SimpleForwardCommonOuter; - constexpr MoveOnlyOuter(MoveOnlyOuter&&) = default; - constexpr MoveOnlyOuter(const MoveOnlyOuter&) = delete; + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView(const MoveOnlyView&) = delete; - constexpr MoveOnlyOuter& operator=(MoveOnlyOuter&&) = default; - constexpr MoveOnlyOuter& operator=(const MoveOnlyOuter&) = delete; + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(const MoveOnlyView&) = delete; }; struct Foo { @@ -51,13 +51,13 @@ constexpr bool test() { { // Test `views::join(move-only-view)` ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3}; - using Result = std::ranges::join_view; - std::same_as decltype(auto) v = std::views::join(MoveOnlyOuter{inners}); + using Result = std::ranges::join_view; + std::same_as decltype(auto) v = std::views::join(MoveOnlyView{inners}); assert(std::ranges::next(v.begin(), 9) == v.end()); assert(&(*v.begin()) == buffer1); - static_assert(std::invocable); - static_assert(!std::invocable); + static_assert(std::invocable); + static_assert(!std::invocable); } { @@ -86,13 +86,13 @@ constexpr bool test() { { // Test `move-only-view | views::join` ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3}; - using Result = std::ranges::join_view; - std::same_as decltype(auto) v = MoveOnlyOuter{inners} | std::views::join; + using Result = std::ranges::join_view; + std::same_as decltype(auto) v = MoveOnlyView{inners} | std::views::join; assert(std::ranges::next(v.begin(), 9) == v.end()); assert(&(*v.begin()) == buffer1); - static_assert(CanBePiped); - static_assert(!CanBePiped); + static_assert(CanBePiped); + static_assert(!CanBePiped); } { 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 index fc29a00014f67..48a0a6d206db5 100644 --- 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 @@ -20,83 +20,7 @@ #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_}; } -}; +#include "../../range_adaptor_types.h" template concept HasMinus = std::invocable, const T&, const U&>; 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 bcfa3407e6cb1..61f8935af3c4a 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 @@ -28,83 +28,6 @@ #include "../../range_adaptor_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_}; } -}; - // clang-format off template concept HasMinus = std::invocable,const T&, const U&>; @@ -161,10 +84,10 @@ constexpr bool test() { static_assert(!std::ranges::common_range); static_assert(!simple_view); - using Iter = std::ranges::iterator_t; + 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 Sentinel = std::ranges::sentinel_t; using ConstSentinel = std::ranges::sentinel_t; static_assert(!std::is_same_v); @@ -172,9 +95,9 @@ constexpr bool test() { static_assert(HasMinus); static_assert(HasMinus); static_assert(HasMinus); - auto it = v.begin(); + auto it = v.begin(); auto const_it = std::as_const(v).begin(); - auto st = v.end(); + auto st = v.end(); auto const_st = std::as_const(v).end(); assert(it - st == -5); assert(st - it == 5); @@ -193,10 +116,10 @@ constexpr bool test() { static_assert(!std::ranges::common_range); static_assert(!simple_view); - using Iter = std::ranges::iterator_t; + 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 Sentinel = std::ranges::sentinel_t; using ConstSentinel = std::ranges::sentinel_t; static_assert(!std::is_same_v); @@ -209,9 +132,9 @@ constexpr bool test() { static_assert(HasMinus); static_assert(HasMinus); - auto it = v.begin(); + auto it = v.begin(); auto const_it = std::as_const(v).begin(); - auto st = v.end(); + auto st = v.end(); auto const_st = std::as_const(v).end(); assert(it - st == -5); diff --git a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h index 288a78ac722e6..da5b94d75f466 100644 --- a/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h +++ b/libcxx/test/std/ranges/range.adaptors/range_adaptor_types.h @@ -429,13 +429,18 @@ static_assert(!simple_view); namespace adltest { struct iter_move_swap_iterator { - std::reference_wrapper iter_move_called_times; - std::reference_wrapper iter_swap_called_times; - int i = 0; + int* iter_move_called_times = nullptr; + int* iter_swap_called_times = nullptr; + int i = 0; - using iterator_category = std::input_iterator_tag; - using value_type = int; - using difference_type = std::intptr_t; + constexpr iter_move_swap_iterator() = default; + constexpr iter_move_swap_iterator(int& iter_move_called_times_ref, int& iter_swap_called_times_ref) + : iter_move_called_times(&iter_move_called_times_ref), + iter_swap_called_times(&iter_swap_called_times_ref), + i(0) {} + + using value_type = int; + using difference_type = std::intptr_t; constexpr int operator*() const { return i; } @@ -443,17 +448,24 @@ struct iter_move_swap_iterator { ++i; return *this; } - constexpr void operator++(int) { ++i; } + constexpr iter_move_swap_iterator operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; } + friend constexpr bool operator==(const iter_move_swap_iterator& x, const iter_move_swap_iterator& y) { + return x.i == y.i; + } friend constexpr int iter_move(iter_move_swap_iterator const& it) { - ++it.iter_move_called_times; + ++*it.iter_move_called_times; return it.i; } friend constexpr void iter_swap(iter_move_swap_iterator const& x, iter_move_swap_iterator const& y) { - ++x.iter_swap_called_times; - ++y.iter_swap_called_times; + ++*x.iter_swap_called_times; + ++*y.iter_swap_called_times; } }; @@ -465,4 +477,81 @@ struct IterMoveSwapRange { }; } // namespace adltest +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_}; } +}; + #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 505ed2e77ae3b..43235e23a6a98 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 @@ -56,6 +56,7 @@ constexpr bool test() { testOne>(); #if TEST_STD_VER >= 23 + testOne>(); testOne>(); testOne>(); testOne>();