diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 358889d8dbc37..111164caa2c9a 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -388,6 +388,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_starts_ends_with`` ``202106L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_ranges_stride`` ``202207L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_to_container`` ``202202L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_zip`` *unimplemented* diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst index 572d4321dc46f..ab6e25cf0854e 100644 --- a/libcxx/docs/ReleaseNotes/20.rst +++ b/libcxx/docs/ReleaseNotes/20.rst @@ -47,6 +47,7 @@ Implemented Papers - ``std::jthread`` and ```` are not guarded behind ``-fexperimental-library`` anymore - P2674R1: A trait for implicit lifetime types (`Github `__) - P0429R9: A Standard ``flat_map`` (`Github `__) +- P1899: ``stride_view`` (`Github ` __) Improvements and New Features ----------------------------- diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index db918a16e9a61..9a0a0ce79a055 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -730,6 +730,7 @@ set(files __ranges/single_view.h __ranges/size.h __ranges/split_view.h + __ranges/stride_view.h __ranges/subrange.h __ranges/take_view.h __ranges/take_while_view.h diff --git a/libcxx/include/__cxx03/module.modulemap b/libcxx/include/__cxx03/module.modulemap index 34a2d0f25fc45..45e90c9b23830 100644 --- a/libcxx/include/__cxx03/module.modulemap +++ b/libcxx/include/__cxx03/module.modulemap @@ -1753,6 +1753,7 @@ module cxx03_std_private_ranges_size [system] { export std_private_type_traits_make_unsigned } module cxx03_std_private_ranges_split_view [system] { header "__ranges/split_view.h" } +module cxx03_std_private_ranges_stride_view [system] { header "__ranges/stride_view.h" } module cxx03_std_private_ranges_subrange [system] { header "__ranges/subrange.h" export std_private_ranges_subrange_fwd diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h new file mode 100644 index 0000000000000..5ca398382948b --- /dev/null +++ b/libcxx/include/__ranges/stride_view.h @@ -0,0 +1,414 @@ +// -*- 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_STRIDE_VIEW_H +#define _LIBCPP___RANGES_STRIDE_VIEW_H + +#include <__assert> +#include <__compare/three_way_comparable.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/derived_from.h> +#include <__concepts/equality_comparable.h> +#include <__concepts/relation.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__functional/operations.h> +#include <__functional/ranges_operations.h> +#include <__iterator/advance.h> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/distance.h> +#include <__iterator/indirectly_comparable.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/view_interface.h> +#include <__type_traits/make_unsigned.h> + +#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 _Value __div_ceil(_Value __left, _Value __right) { + _Value __r = __left / __right; + if (__left % __right) { + ++__r; + } + return __r; +} + +template + requires view<_View> +class stride_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + range_difference_t<_View> __stride_ = 0; + + template + class __iterator; + +public: + _LIBCPP_HIDE_FROM_ABI constexpr explicit stride_view(_View __base, range_difference_t<_View> __stride) + : __base_(std::move(__base)), __stride_(__stride) { + _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__stride > 0, "The value of stride must be greater than 0"); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr range_difference_t<_View> stride() const noexcept { return __stride_; } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator(this, ranges::begin(__base_), 0); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return __iterator(this, ranges::begin(__base_), 0); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (common_range<_View> && sized_range<_View> && forward_range<_View>) { + auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_; + return __iterator(this, ranges::end(__base_), __missing); + } else if constexpr (common_range<_View> && !bidirectional_range<_View>) { + return __iterator(this, ranges::end(__base_), 0); + } else { + return default_sentinel; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires(range) + { + if constexpr (common_range && sized_range && forward_range) { + auto __missing = (__stride_ - ranges::distance(__base_) % __stride_) % __stride_; + return __iterator(this, ranges::end(__base_), __missing); + } else if constexpr (common_range<_View> && !bidirectional_range<_View>) { + return __iterator(this, ranges::end(__base_), 0); + } else { + return default_sentinel; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_)); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_)); + } +}; // class stride_view + +template +stride_view(_Range&&, range_difference_t<_Range>) -> stride_view>; + +template +struct __stride_iterator_category {}; + +template +struct __stride_iterator_category<_View> { + using _Cat _LIBCPP_NODEBUG = typename iterator_traits>::iterator_category; + using iterator_category = + _If, + /* then */ random_access_iterator_tag, + /* else */ _Cat >; +}; + +template + requires view<_View> +template +class stride_view<_View>::__iterator : public __stride_iterator_category<_View> { + using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, stride_view<_View>>; + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + + _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_Base> __current_ = iterator_t<_Base>(); + _LIBCPP_NO_UNIQUE_ADDRESS ranges::sentinel_t<_Base> __end_ = ranges::sentinel_t<_Base>(); + range_difference_t<_Base> __stride_ = 0; + range_difference_t<_Base> __missing_ = 0; + + friend stride_view; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator( + _Parent* __parent, ranges::iterator_t<_Base> __current, range_difference_t<_Base> __missing) + : __current_(std::move(__current)), + __end_(ranges::end(__parent->__base_)), + __stride_(__parent->__stride_), + __missing_(__missing) {} + + static consteval auto __get_stride_view_iterator_concept() { + if constexpr (random_access_range<_Base>) { + return random_access_iterator_tag{}; + } else if constexpr (bidirectional_range<_Base>) { + return bidirectional_iterator_tag{}; + } else if constexpr (forward_range<_Base>) { + return forward_iterator_tag{}; + } else { + return input_iterator_tag{}; + } + } + +public: + using difference_type = range_difference_t<_Base>; + using value_type = range_value_t<_Base>; + using iterator_concept = decltype(__get_stride_view_iterator_concept()); + // using iterator_category = inherited; + + _LIBCPP_HIDE_FROM_ABI __iterator() + requires default_initializable> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> && + convertible_to, sentinel_t<_Base>> + : __current_(std::move(__i.__current_)), + __end_(std::move(__i.__end_)), + __stride_(__i.__stride_), + __missing_(__i.__missing_) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> const& base() const& noexcept { return __current_; } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot dereference an iterator at the end."); + return *__current_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end."); + __missing_ = ranges::advance(__current_, __stride_, __end_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end."); + ++*this; + } + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires forward_range<_Base> + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__current_ != __end_, "Cannot increment an iterator already at the end."); + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + ranges::advance(__current_, __missing_ - __stride_); + __missing_ = 0; + 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 __n) + requires random_access_range<_Base> + { + if (__n > 0) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(ranges::distance(__current_, __end_) > __stride_ * (__n - 1), + "Advancing the iterator beyond the end is not allowed."); + ranges::advance(__current_, __stride_ * (__n - 1)); + __missing_ = ranges::advance(__current_, __stride_, __end_); + + } else if (__n < 0) { + ranges::advance(__current_, __stride_ * __n + __missing_); + __missing_ = 0; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n) + requires random_access_range<_Base> + { + return *this += -__n; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const + requires random_access_range<_Base> + { + return *(*this + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) { + return __x.__current_ == __x.__end_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(__iterator const& __x, __iterator const& __y) + requires equality_comparable> + { + return __x.__current_ == __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(__iterator const& __x, __iterator const& __y) + requires random_access_range<_Base> && three_way_comparable> + { + return __x.__current_ <=> __y.__current_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator const& __i, difference_type __s) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __s; + return __r; + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __s, __iterator const& __i) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __s; + return __r; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator const& __i, difference_type __s) + requires random_access_range<_Base> + { + auto __r = __i; + __r -= __s; + return __r; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(__iterator const& __x, __iterator const& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + if constexpr (forward_range<_Base>) { + auto __n = __x.__current_ - __y.__current_; + return (__n + __x.__missing_ - __y.__missing_) / __x.__stride_; + } + auto __n = __x.__current_ - __y.__current_; + if (__n < 0) { + return -ranges::__div_ceil(-__n, __x.__stride_); + } + return ranges::__div_ceil(__n, __x.__stride_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(default_sentinel_t, __iterator const& __x) + requires sized_sentinel_for, iterator_t<_Base>> + { + return ranges::__div_ceil(__x.__end_ - __x.__current_, __x.__stride_); + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(__iterator const& __x, default_sentinel_t __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + return -(__y - __x); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base> + iter_move(__iterator const& __it) noexcept(noexcept(ranges::iter_move(__it.__current_))) { + return ranges::iter_move(__it.__current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void + iter_swap(__iterator const& __x, + __iterator const& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_))) + requires indirectly_swappable> + { + return ranges::iter_swap(__x.__current_, __y.__current_); + } +}; // class stride_view::__iterator + +template +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Tp>; + +namespace views { +namespace __stride_view { +struct __fn { + // clang-format off + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const + noexcept(noexcept(stride_view{std::forward<_Range>(__range), __n})) + -> decltype( stride_view{std::forward<_Range>(__range), __n}) + { return stride_view(std::forward<_Range>(__range), __n); } + // clang-format on + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Np&& __n) const { + return __pipeable(std::__bind_back(*this, std::forward<_Np>(__n))); + } +}; +} // namespace __stride_view + +inline namespace __cpo { +inline constexpr auto stride = __stride_view::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_STRIDE_VIEW_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 63cf8e847751f..823b41ff77065 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1917,6 +1917,10 @@ module std [system] { module subrange_fwd { header "__fwd/subrange.h" } + module stride_view { + header "__ranges/stride_view.h" + export std.functional.bind_back + } module take_view { header "__ranges/take_view.h" export std.functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 96d7a6b897188..c82f6f9056fed 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -362,6 +362,17 @@ namespace std::ranges { class chunk_by_view; // C++23 namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23 + + // [range.stride.view], stride view + template + requires view + class stride_view; // C++23 + + template + constexpr bool enable_borrowed_range> = + enable_borrowed_range; // C++23 + + namespace views { inline constexpr unspecified stride = unspecified; } // C++23 } namespace std { @@ -448,6 +459,7 @@ namespace std { # include <__ranges/from_range.h> # include <__ranges/join_with_view.h> # include <__ranges/repeat_view.h> +# include <__ranges/stride_view.h> # include <__ranges/to.h> # include <__ranges/zip_transform_view.h> # include <__ranges/zip_view.h> diff --git a/libcxx/include/version b/libcxx/include/version index 16917a3bd9ddd..e72ee9b605957 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -210,6 +210,7 @@ __cpp_lib_ranges_join_with 202202L __cpp_lib_ranges_repeat 202207L __cpp_lib_ranges_slide 202202L __cpp_lib_ranges_starts_ends_with 202106L +__cpp_lib_ranges_stride 202207L __cpp_lib_ranges_to_container 202202L __cpp_lib_ranges_zip 202110L __cpp_lib_ratio 202306L @@ -527,6 +528,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_ranges_repeat 202207L // # define __cpp_lib_ranges_slide 202202L # define __cpp_lib_ranges_starts_ends_with 202106L +# define __cpp_lib_ranges_stride 202207L # define __cpp_lib_ranges_to_container 202202L // # define __cpp_lib_ranges_zip 202110L // # define __cpp_lib_reference_from_temporary 202202L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 7ede42e4f7b0a..798b645c306bb 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -335,16 +335,16 @@ export namespace std { namespace views { using std::ranges::views::chunk_by; } -#endif // _LIBCPP_STD_VER >= 23 -#if 0 // [range.stride], stride view using std::ranges::stride_view; namespace views { using std::ranges::views::stride; } +#endif // _LIBCPP_STD_VER >= 23 +#if 0 using std::ranges::cartesian_product_view; namespace views { diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp new file mode 100644 index 0000000000000..e564891b15a15 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/ctor.assert.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{extensive|debug}} +// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr explicit stride_view(_View, range_difference_t<_View>) + +#include + +#include "check_assertion.h" + +int main(int, char**) { + int range[] = {1, 2, 3}; + TEST_LIBCPP_ASSERT_FAILURE( + [&range] { std::ranges::stride_view sv(range, 0); }(), "The value of stride must be greater than 0"); + TEST_LIBCPP_ASSERT_FAILURE( + [&range] { std::ranges::stride_view sv(range, -1); }(), "The value of stride must be greater than 0"); + + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/base.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/base.nodiscard.verify.cpp new file mode 100644 index 0000000000000..0f64846bbff2f --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/base.nodiscard.verify.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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 that std::ranges::stride_view::iterator::base() is marked nodiscard. + +#include +#include + +void test() { + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 3); + auto it = view.begin(); + + it.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(it).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + } +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.nodiscard.verify.cpp new file mode 100644 index 0000000000000..366bcebd52d87 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.nodiscard.verify.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 that std::ranges::stride_view::iterator::operator*() is marked nodiscard. + +#include +#include + +void test() { + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 3); + auto it = view.begin(); + ++it; + *std::as_const(it); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + } +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp new file mode 100644 index 0000000000000..5094c0bf532d9 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/dereference.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}} +// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr decltype(auto) operator*() const + +#include "check_assertion.h" +#include + +int main(int, char**) { + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 3); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end."); + } + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 4); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(*std::as_const(it), "Cannot dereference an iterator at the end."); + } + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp new file mode 100644 index 0000000000000..6cd3b11fd7e89 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}} +// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr stride_view::& operator++() +// constexpr __iterator& operator++() +// constexpr void operator++(int) { +// constexpr __iterator operator++(int) + +#include + +#include "check_assertion.h" +#include "test_iterators.h" + +#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h" + +int main(int, char**) { + { + int range[] = {1, 2, 3}; + using Base = BasicTestView>; + auto view = std::ranges::views::stride(Base(cpp17_input_iterator(range), cpp17_input_iterator(range + 3)), 3); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end."); + TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end."); + } + { + int range[] = {1, 2, 3}; + using Base = BasicTestView, forward_iterator>; + auto view = std::ranges::views::stride(Base(forward_iterator(range), forward_iterator(range + 3)), 3); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(it++, "Cannot increment an iterator already at the end."); + TEST_LIBCPP_ASSERT_FAILURE(++it, "Cannot increment an iterator already at the end."); + } + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iter_move.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iter_move.nodiscard.verify.cpp new file mode 100644 index 0000000000000..a560bcc899841 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/iter_move.nodiscard.verify.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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 range_rvalue_reference_t<_Base> iter_move(__iterator const& __it) +// noexcept(noexcept(ranges::iter_move(__it.__current_))) + +#include + +#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h" + +constexpr bool test() { + { + int a[] = {4, 3, 2, 1}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin(); + + static_assert(std::is_same_v); + static_assert(noexcept(std::ranges::iter_move(svb))); + + // These lines need to be in sync so that clang-verify knows where the warning comes from. + // clang-format off + std::ranges::iter_move( // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + svb); + // clang-format on + } + return true; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator.nodiscard.verify.cpp new file mode 100644 index 0000000000000..e1121b7efc2bf --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator.nodiscard.verify.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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 that std::ranges::stride_view::iterator::operator- is marked nodiscard. + +#include + +#include "../../../../../std/ranges/range.adaptors/range.stride.view/types.h" + +constexpr bool test_non_forward_operator_minus() { + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + using Base = BasicTestView; + + auto base_view_offset_zero = Base(SizedInputIter(arr), SizedInputIter(arr + 10)); + auto base_view_offset_one = Base(SizedInputIter(arr + 1), SizedInputIter(arr + 10)); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + sv_one_offset_begin - // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + sv_zero_offset_begin; + return true; +} + +constexpr bool test_forward_operator_minus() { + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + using Base = BasicTestView; + + auto base_view_offset_zero = Base(arr, arr + 10); + auto base_view_offset_one = Base(arr + 1, arr + 10); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + sv_zero_offset_begin + // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + 1; + 1 + sv_zero_offset_begin; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + sv_one_offset_begin - 1; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + sv_one_offset_begin - // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + sv_zero_offset_begin; + + std::default_sentinel_t() - // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + sv_zero_offset_begin; + + sv_zero_offset_begin - // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::default_sentinel_t(); + + return true; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp new file mode 100644 index 0000000000000..0a84436444cd5 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/operator_plus_equal.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// REQUIRES: libcpp-hardening-mode={{fast|extensive|debug}} +// XFAIL:libcpp-hardening-mode=debug && availability-verbose_abort-missing + +// constexpr __iterator& operator+=(difference_type __n) + +#include + +#include "check_assertion.h" + +int main(int, char**) { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 2); + auto it = view.begin(); + TEST_LIBCPP_ASSERT_FAILURE(it += 3, "Advancing the iterator beyond the end is not allowed."); + + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/subscript.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/subscript.nodiscard.verify.cpp new file mode 100644 index 0000000000000..cfd048b66afe4 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/iterator/subscript.nodiscard.verify.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr decltype(auto) operator[](difference_type __n) const + +#include + +void test() { + { + int range[] = {1, 2, 3}; + auto view = std::ranges::views::stride(range, 3); + auto it = view.begin(); + it[0]; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + } +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp new file mode 100644 index 0000000000000..280d9b822f4a7 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// Test that +// std::view::stride() +// std::ranges::stride_view::base() +// std::ranges::stride_view::begin() +// std::ranges::stride_view::end() +// std::ranges::stride_view::size() +// std::ranges::stride_view::stride() +// are all marked nodiscard. + +#include + +#include "../../../../std/ranges/range.adaptors/range.stride.view/types.h" + +void test_base_nodiscard() { + const int range[] = {1, 2, 3}; + auto sv = std::ranges::stride_view(range, 3); + + sv.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(sv).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} + +void test_begin_nodiscard() { + const auto const_sv = std::views::stride(SimpleCommonConstView{}, 2); + auto unsimple_sv = std::views::stride(UnSimpleConstView{}, 2); + + const_sv.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + unsimple_sv.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} + +void test_views_stride_nodiscard() { + const int range[] = {1, 2, 3}; + + std::views::stride( // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + range, + 2); +} + +void test_end_nodiscard() { + const int range[] = {1, 2, 3}; + + const auto const_sv = std::views::stride(range, 2); + auto unsimple_sv = std::views::stride(UnSimpleConstView{}, 2); + + const_sv.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + unsimple_sv.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} + +void test_size_nodiscard() { + auto sv = std::views::stride(SimpleNoConstSizedCommonView(), 2); + const auto const_sv = std::views::stride(SimpleCommonConstView(), 2); + + sv.size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + const_sv.size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} + +void test_stride_nodiscard() { + const int range[] = {1, 2, 3}; + auto const_sv = std::views::stride(range, 2); + const_sv.stride(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index df19f03e7dba1..cc7c415f714b0 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -60,6 +60,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -110,6 +114,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -160,6 +168,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -213,6 +225,10 @@ # error "__cpp_lib_ranges_slide should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -305,6 +321,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++23" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++23" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++23" # endif @@ -427,6 +450,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++26" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++26" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index cde2f258b7732..999c0ba904a54 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -684,6 +684,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -1624,6 +1628,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -2735,6 +2743,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -4119,6 +4131,10 @@ # error "__cpp_lib_ranges_starts_ends_with should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif @@ -5719,6 +5735,13 @@ # error "__cpp_lib_ranges_starts_ends_with should have the value 202106L in c++23" # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++23" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++23" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++23" # endif @@ -7631,6 +7654,13 @@ # error "__cpp_lib_ranges_starts_ends_with should have the value 202106L in c++26" # endif +# ifndef __cpp_lib_ranges_stride +# error "__cpp_lib_ranges_stride should be defined in c++26" +# endif +# if __cpp_lib_ranges_stride != 202207L +# error "__cpp_lib_ranges_stride should have the value 202207L in c++26" +# endif + # ifndef __cpp_lib_ranges_to_container # error "__cpp_lib_ranges_to_container should be defined in c++26" # endif diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp new file mode 100644 index 0000000000000..37c4a20e6a196 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/adaptor.pass.cpp @@ -0,0 +1,149 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::views::stride_view + +#include + +#include "test_iterators.h" +#include "types.h" + +constexpr BasicTestView> make_input_view(int* begin, int* end) { + return BasicTestView>(cpp17_input_iterator(begin), cpp17_input_iterator(end)); +} + +using ForwardStrideView = std::ranges::stride_view>>; +using BidirStrideView = std::ranges::stride_view>>; +using RandomAccessStrideView = std::ranges::stride_view>>; + +using SizedForwardStrideView = + std::ranges::stride_view, random_access_iterator>>; +using SizedInputStrideView = std::ranges::stride_view>; + +static_assert(std::ranges::forward_range); +static_assert(std::ranges::bidirectional_range); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::forward_range); +static_assert(std::sized_sentinel_for, + std::ranges::iterator_t>); +static_assert(std::sized_sentinel_for, + std::ranges::iterator_t>); + +constexpr bool test() { + constexpr int N = 3; + int arr[N] = {1, 2, 3}; + + // Test that `std::views::stride` is a range adaptor. + // Check various forms of + + // view | stride + // two tests: first with stride of 1; second with stride of 2. + { + using View = BasicTestView>; + auto view = make_input_view(arr, arr + N); + std::same_as> decltype(auto) strided = view | std::views::stride(1); + auto strided_iter = strided.begin(); + + // Check that the begin() iter views arr[0] + assert(*strided_iter == arr[0]); + + // Check that the strided_iter, after advancing it 2 * 1 steps, views arr[2]. + std::ranges::advance(strided_iter, 2); + assert(*strided_iter == arr[2]); + } + { + using View = BasicTestView>; + auto view = make_input_view(arr, arr + N); + std::same_as> decltype(auto) strided = view | std::views::stride(2); + auto strided_iter = strided.begin(); + + assert(*strided_iter == arr[0]); + std::ranges::advance(strided_iter, 1); + assert(*strided_iter == arr[2]); + } + + // adaptor | stride + // two tests: first with stride of 1; second with stride of 2. + const auto i2 = [](int i) { return i * 2; }; + { + auto view = make_input_view(arr, arr + N); + const auto transform_stride_partial = std::views::transform(i2) | std::views::stride(1); + + auto transform_stride_applied = transform_stride_partial(view); + auto transform_stride_applied_iter = transform_stride_applied.begin(); + + assert(*transform_stride_applied_iter == i2(arr[0])); + std::ranges::advance(transform_stride_applied_iter, 2); + assert(*transform_stride_applied_iter == i2(arr[2])); + } + + { + auto view = make_input_view(arr, arr + N); + const auto transform_stride_partial = std::views::transform(i2) | std::views::stride(2); + + const auto transform_stride_applied = transform_stride_partial(view); + auto transform_stride_applied_iter = transform_stride_applied.begin(); + + assert(*transform_stride_applied_iter == i2(arr[0])); + std::ranges::advance(transform_stride_applied_iter, 1); + assert(*transform_stride_applied_iter == i2(arr[2])); + } + + // stride | adaptor + // two tests: first with stride of 1; second with stride of 2. + { + auto view = make_input_view(arr, arr + N); + const auto stride_transform = std::views::stride(view, 1) | std::views::transform(i2); + + auto stride_transform_iter = stride_transform.begin(); + + assert(*stride_transform_iter == i2(arr[0])); + std::ranges::advance(stride_transform_iter, 2); + assert(*stride_transform_iter == i2(arr[2])); + } + { + auto view = make_input_view(arr, arr + N); + const auto stride_transform = std::views::stride(view, 2) | std::views::transform(i2); + + auto stride_transform_iter = stride_transform.begin(); + + assert(*stride_transform_iter == i2(arr[0])); + std::ranges::advance(stride_transform_iter, 1); + assert(*stride_transform_iter == i2(arr[2])); + } + + // Check SFINAE friendliness + { + struct NotAViewableRange {}; + using View = BasicTestView>; + + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + + static_assert(CanBePiped); + static_assert(CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped); + } + + // A final sanity check. + { + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.pass.cpp new file mode 100644 index 0000000000000..17b5afb070b8b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/base.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 _View base() const& requires copy_constructible<_View>; +// constexpr _View base() &&; + +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +constexpr bool hasLValueQualifiedBase(T&& t) { + // Thanks to forwarding references, t's type is + // preserved from the caller. No matter the type of + // the argument, when it is used here, t is an l value + // (after all, it has a name). Therefore, this code + // will test whether the l value const-ref-qualified + // version of base is callable. + return requires { t.base(); }; +} + +using CopyableInputView = CopyableView>; +using MoveOnlyInputView = MoveOnlyView>; + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8}; + constexpr int N = 8; + + // l-value ref qualified + // const & + { + const auto str(std::ranges::stride_view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1)); + + static_assert(hasLValueQualifiedBase(str)); + + std::same_as decltype(auto) s = str.base(); + assert(*s.begin() == *buff); + } + + // & + { + auto str(std::ranges::stride_view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1)); + + std::same_as decltype(auto) s = str.base(); + assert(*s.begin() == *buff); + + static_assert(hasLValueQualifiedBase(str)); + } + + // r-value ref qualified + // && + { + auto str(std::ranges::stride_view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1)); + + static_assert(hasLValueQualifiedBase(str)); + + std::same_as decltype(auto) s = std::move(str).base(); + assert(*s.begin() == *buff); + } + + // const && + { + const auto str_a(std::ranges::stride_view( + CopyableInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1)); + + static_assert(hasLValueQualifiedBase(str_a)); + + std::same_as decltype(auto) s = std::move(str_a).base(); + assert(*s.begin() == *buff); + } + + // && + { + auto str(std::ranges::stride_view( + MoveOnlyInputView(cpp17_input_iterator(buff), cpp17_input_iterator(buff + N)), 1)); + + // Because the base of the stride view is move only, + // the const & version is not applicable and, therefore, + // there is no l-value qualified base method. + static_assert(!hasLValueQualifiedBase(str)); + + std::same_as decltype(auto) s = std::move(str).base(); + assert(*s.begin() == *buff); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp new file mode 100644 index 0000000000000..c032e1e940311 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/begin.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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<_View>) +// constexpr auto begin() const requires range + +// Note: Checks here are augmented by checks in +// iterator/ctor.copy.pass.cpp. + +#include +#include + +#include "types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = requires(T& t, const T& ct) { + // The return types for begin are different when this is const or not: + // the iterator's _Const non-type-template parameter is true in the former + // and false in the latter. + requires !std::same_as; +}; + +template +// There is a begin but it's not const qualified => Only non-const qualified begin. +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +// There is a const-qualified begin and there are not both const- and non-const qualified begin => Only const-qualified begin. +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +static_assert(HasOnlyNonConstBegin>); +static_assert(HasOnlyConstBegin>); +static_assert(HasConstAndNonConstBegin>); + +constexpr bool test() { + const auto unsimple_const_view = UnSimpleConstView(); + const auto sv_unsimple_const = std::ranges::stride_view(unsimple_const_view, 1); + static_assert(std::same_as); + + auto simple_const_view = SimpleCommonConstView(); + auto sv_simple_const = std::ranges::stride_view(simple_const_view, 1); + static_assert(std::same_as); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp new file mode 100644 index 0000000000000..cfadffbda28de --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/borrowing.compile.pass.cpp @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// 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> = ranges::enable_borrowed_range; + +#include + +#include "test_range.h" + +static_assert(std::ranges::enable_borrowed_range< std::ranges::stride_view>); +static_assert(!std::ranges::enable_borrowed_range< std::ranges::stride_view>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.verify.cpp new file mode 100644 index 0000000000000..d580d240f7a3b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/concept.verify.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// expected-no-diagnostics + +// template requires view<_View> + +#include "almost_satisfies_types.h" +#include "test_iterators.h" +#include "test_range.h" + +template D> +concept CanStrideView = requires { std::ranges::stride_view{I{}, D}; }; + +// Ensure that the InputRangeNotIndirectlyReadable is a valid range. +static_assert(std::ranges::range); +// Ensure that the InputRangeNotIndirectlyReadable's is not an input range ... +static_assert(!std::ranges::input_range>); +// Because CanStrideView requires that the range/view type be default constructible, let's double check that ... +static_assert(std::is_constructible_v); +// And now, finally, let's make sure that we cannot stride over a range whose iterator is not an input iterator ... +static_assert(!CanStrideView); + +// Ensure that a range that is not a view cannot be the subject of a stride_view. +static_assert(std::ranges::range>); +static_assert(std::ranges::input_range>); +static_assert(std::movable>); +static_assert(!std::ranges::view>); +static_assert(!CanStrideView, 1>); + +// And now, let's satisfy all the prerequisites and make sure that we can stride over a range (that is an input range +// and is a view!) +static_assert(std::ranges::range>); +static_assert(std::ranges::input_range>); +static_assert(std::ranges::view>); +static_assert(CanStrideView, 1>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp new file mode 100644 index 0000000000000..df83e25d7fe6f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctad.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// stride_view(_Range&&, range_difference_t<_Range>) -> stride_view>; + +#include "types.h" +#include +#include + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct Range { + int* begin() const; + int* end() const; +}; + +constexpr bool testCTAD() { + int a[] = {1, 2, 3, 4, 5}; + + using BaseRange = BasicTestRange>; + using BaseView = BasicTestView; + + auto base_view = BaseView(a, a + 5); + auto base_view_for_move = BaseView(a, a + 5); + + auto base_range = BaseRange(cpp17_input_iterator(a), cpp17_input_iterator(a + 5)); + auto base_range_for_move = BaseRange(cpp17_input_iterator(a), cpp17_input_iterator(a + 5)); + + auto copied_stride_base_view = std::ranges::stride_view(base_view, 2); + auto moved_stride_base_view = std::ranges::stride_view(std::move(base_view_for_move), 2); + + auto copied_stride_base_range = std::ranges::stride_view(base_range, 2); + auto moved_stride_base_range = std::ranges::stride_view(std::move(base_range_for_move), 2); + + static_assert(std::same_as< decltype(copied_stride_base_view), std::ranges::stride_view>); + static_assert(std::same_as< decltype(moved_stride_base_view), std::ranges::stride_view>); + + static_assert( + std::same_as< decltype(copied_stride_base_range), std::ranges::stride_view> >); + static_assert( + std::same_as< decltype(moved_stride_base_range), std::ranges::stride_view> >); + + assert(*copied_stride_base_range.begin() == 1); + assert(*moved_stride_base_range.begin() == 1); + + assert(*copied_stride_base_view.begin() == 1); + assert(*moved_stride_base_view.begin() == 1); + + auto copied_stride_range_it = copied_stride_base_range.begin(); + copied_stride_range_it++; + assert(*copied_stride_range_it == 3); + copied_stride_range_it++; + copied_stride_range_it++; + assert(copied_stride_range_it == copied_stride_base_range.end()); + + auto moved_stride_range_it = moved_stride_base_range.begin(); + moved_stride_range_it++; + moved_stride_range_it++; + assert(*moved_stride_range_it == 5); + moved_stride_range_it++; + assert(moved_stride_range_it == moved_stride_base_range.end()); + + auto copied_stride_view_it = copied_stride_base_view.begin(); + copied_stride_view_it++; + assert(*copied_stride_view_it == 3); + copied_stride_view_it++; + copied_stride_view_it++; + assert(copied_stride_view_it == copied_stride_base_view.end()); + + auto moved_stride_view_it = copied_stride_base_view.begin(); + moved_stride_view_it++; + moved_stride_view_it++; + assert(*moved_stride_view_it == 5); + moved_stride_view_it++; + assert(moved_stride_view_it == moved_stride_base_view.end()); + + return true; +} + +int main(int, char**) { + testCTAD(); + static_assert(testCTAD()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp new file mode 100644 index 0000000000000..ef360c8d17eba --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/ctor.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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 stride_view(_View, range_difference_t<_View>) + +#include + +#include "test_convertible.h" +#include "test_iterators.h" +#include "types.h" + +// There is no default ctor for stride_view. +using View = BasicTestView>; +static_assert(!std::is_default_constructible_v>); + +// Test that the stride_view can only be explicitly constructed. +static_assert(!test_convertible, View, int>()); + +constexpr bool test() { + { + int arr[] = {1, 2, 3}; + // Test that what we will stride over is move only. + static_assert(!std::is_copy_constructible_v>>); + static_assert(std::is_move_constructible_v>>); + + MoveOnlyView> mov(cpp17_input_iterator(arr), cpp17_input_iterator(arr + 3)); + // Because MoveOnlyView is, well, move only, we can test that it is moved + // from when the stride view is constructed. + std::ranges::stride_view>> mosv(std::move(mov), 1); + + // While we are here, make sure that the ctor captured the stride. + assert(mosv.stride() == 1); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp new file mode 100644 index 0000000000000..a33349d554761 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/end.pass.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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) + +// Note: Checks here are augmented by checks in +// iterator/ctor.copy.pass.cpp. + +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept HasConstEnd = requires(const T& ct) { ct.end(); }; + +template +concept HasEnd = requires(T& t) { t.end(); }; + +template +concept HasConstAndNonConstEnd = + HasConstEnd && requires(T& t, const T& ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstEnd = HasEnd && !HasConstEnd; + +template +concept HasOnlyConstEnd = HasConstEnd && !HasConstAndNonConstEnd; + +static_assert(HasOnlyNonConstEnd>); +static_assert(HasOnlyConstEnd>); +static_assert(HasConstAndNonConstEnd>); + +constexpr bool test_non_default_sentinel() { + { + const int data[3] = {1, 2, 3}; + // A const, simple, common-, sized- and forward-range + // Note: sized because it is possible to get a difference between its + // beginning and its end. + auto v = BasicTestView{data, data + 3}; + auto sv = std::ranges::stride_view>(v, 1); + static_assert(!std::is_same_v); + } + + { + int data[3] = {1, 2, 3}; + // ForwardTestView is not sized and not bidirectional, but it is common. + // Note: It is not sized because BasicTestView has no member function named size (by default) + // and nor is it possible to get a difference between its beginning and its end. + using ForwardTestView = BasicTestView, forward_iterator>; + + auto v = ForwardTestView{forward_iterator(data), forward_iterator(data + 3)}; + auto sv = std::ranges::stride_view(v, 1); + static_assert(!std::is_same_v); + } + + { + // TODO: Start here. + static_assert(!simple_view); + static_assert(std::ranges::common_range); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::forward_range); + + // An unconst, unsimple, common-, sized- and forward-range + auto v = UnSimpleNoConstCommonView{}; + auto sv = std::ranges::stride_view(v, 1); + static_assert(!std::is_same_v); + } + return true; +} + +constexpr bool test_default_sentinel() { + { + static_assert(!simple_view); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::sized_range); + static_assert(std::ranges::forward_range); + + auto v = UnsimpleUnCommonConstView{}; + auto sv = std::ranges::stride_view(v, 1); + ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end())); + } + + { + static_assert(simple_view); + static_assert(!std::ranges::common_range); + + auto v = SimpleUnCommonConstView{}; + auto sv = std::ranges::stride_view(v, 1); + + ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(sv.end())); + } + return true; +} + +int main(int, char**) { + test_non_default_sentinel(); + test_default_sentinel(); + static_assert(test_non_default_sentinel()); + static_assert(test_default_sentinel()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp new file mode 100644 index 0000000000000..7d32432725388 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/base.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// 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_t<_Base> const& base() const& noexcept +// constexpr iterator_t<_Base> base() && + +#include +#include + +#include "../types.h" + +constexpr bool base_noexcept() { + { + int arr[] = {1, 2, 3}; + auto stride = std::ranges::stride_view(arr, 1); + [[maybe_unused]] auto stride_iter = stride.begin(); + + // Check that calling base on an iterator where this is an lvalue reference + // is noexcept. + static_assert(noexcept(stride_iter.base())); + // Calling base on an iterator where this is an rvalue reference may except. + static_assert(!noexcept((std::move(stride_iter).base()))); + } + + return true; +} + +constexpr bool base_const() { + { + int arr[] = {1, 2, 3}; + auto stride = std::ranges::stride_view(arr, 1); + [[maybe_unused]] auto stride_iter = stride.begin(); + + // Calling base on an iterator where this is lvalue returns a const ref to an iterator. + static_assert(std::is_const_v>); + // Calling base on an iterator where this is an rvalue reference returns a non-const iterator. + static_assert(!std::is_const_v); + } + + return true; +} + +constexpr bool base_move() { + // Keep track of how many times the original iterator is moved + // and/or copied during the test. + int move_counter = 0; + int copy_counter = 0; + + auto start = SizedInputIter(); + start.move_counter = &move_counter; + start.copy_counter = ©_counter; + auto stop = SizedInputIter(); + + auto view = BasicTestView{start, stop}; + assert(move_counter == 0); + // One copies of _start_ occurs when it is copied to the basic test view's member variable. + assert(copy_counter == 1); + + auto sv = std::ranges::stride_view>(view, 1); + // There is a copy of _view_ made when it is passed by value. + // There is a move done of _view_ when it is used as the initial value of __base. + assert(move_counter == 1); + assert(copy_counter == 2); + + auto svi = sv.begin(); + // Another copy of _start_ when begin uses the iterator to the first element + // of the view underlying sv as the by-value parameter to the stride view iterator's + // constructor. + assert(copy_counter == 3); + // Another move of __start_ happens right after that when it is std::move'd to + // become the first __current of the iterator. + assert(move_counter == 2); + + [[maybe_unused]] auto result = std::move(svi).base(); + // Ensure that base std::move'd the iterator and did not copy it. + assert(move_counter == 3); + assert(copy_counter == 3); + return true; +} + +constexpr bool base_copy() { + // See base_move() for complete description of when/why + // moves/copies take place.. + int move_counter = 0; + int copy_counter = 0; + auto start = SizedInputIter(); + + start.move_counter = &move_counter; + start.copy_counter = ©_counter; + auto stop = SizedInputIter(); + + auto view = BasicTestView{start, stop}; + assert(move_counter == 0); + assert(copy_counter == 1); + + auto sv = std::ranges::stride_view>(view, 1); + assert(move_counter == 1); + assert(copy_counter == 2); + + [[maybe_unused]] auto svi = sv.begin(); + assert(copy_counter == 3); + assert(move_counter == 2); + + [[maybe_unused]] const SizedInputIter result = svi.base(); + // Ensure that base did _not_ std::move'd the iterator. + assert(move_counter == 2); + assert(copy_counter == 4); + + return true; +} + +int main(int, char**) { + base_noexcept(); + static_assert(base_noexcept()); + + base_const(); + static_assert(base_const()); + + base_move(); + static_assert(base_move()); + + base_copy(); + static_assert(base_copy()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp new file mode 100644 index 0000000000000..eb0998cb0c8bb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.copy.pass.cpp @@ -0,0 +1,374 @@ +//===----------------------------------------------------------------------===// +// +// 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<_Base>> && +// convertible_to, sentinel_t<_Base>> + +#include + +#include "../types.h" +#include "test_macros.h" + +struct NotSimpleViewIterEnd; +template +struct NotSimpleViewConstIterEnd; +template +struct NotSimpleViewConstIterBegin; + +struct NotSimpleViewIterBegin : InputIter { + template + friend constexpr bool + operator==(const NotSimpleViewIterBegin&, const NotSimpleViewConstIterEnd&) { + return true; + } + template + friend constexpr bool + operator==(const NotSimpleViewIterBegin&, const NotSimpleViewConstIterBegin&) { + return true; + } +}; + +template +struct NotSimpleViewConstIterBegin : InputIter> { + constexpr NotSimpleViewConstIterBegin() = default; + constexpr NotSimpleViewConstIterBegin(NotSimpleViewConstIterBegin&&) = default; + constexpr NotSimpleViewConstIterBegin& operator=(const NotSimpleViewConstIterBegin&) = default; + constexpr NotSimpleViewConstIterBegin& operator=(NotSimpleViewConstIterBegin&&) = default; + + constexpr NotSimpleViewConstIterBegin(const NotSimpleViewConstIterBegin&) {} + constexpr NotSimpleViewConstIterBegin(const NotSimpleViewIterBegin&) + requires CopyConvertible + {} + constexpr NotSimpleViewConstIterBegin(NotSimpleViewIterBegin&&) + requires MoveConvertible + {} + + friend constexpr bool + operator==(const NotSimpleViewConstIterBegin&, const NotSimpleViewIterEnd&) { + return true; + } + friend constexpr bool + operator==(const NotSimpleViewConstIterBegin&, const NotSimpleViewIterBegin&) { + return true; + } +}; + +struct NotSimpleViewIterEnd : InputIter { + template + friend constexpr bool + operator==(const NotSimpleViewIterEnd&, const NotSimpleViewConstIterBegin&) { + return true; + } + template + friend constexpr bool + operator==(const NotSimpleViewIterEnd&, const NotSimpleViewConstIterEnd&) { + return true; + } + + friend constexpr bool operator==(const NotSimpleViewIterEnd&, const NotSimpleViewIterBegin&) { return true; } +}; + +template +struct NotSimpleViewConstIterEnd : InputIter> { + constexpr NotSimpleViewConstIterEnd() = default; + constexpr NotSimpleViewConstIterEnd(NotSimpleViewConstIterEnd&&) = default; + constexpr NotSimpleViewConstIterEnd& operator=(const NotSimpleViewConstIterEnd&) = default; + constexpr NotSimpleViewConstIterEnd& operator=(NotSimpleViewConstIterEnd&&) = default; + + constexpr NotSimpleViewConstIterEnd(const NotSimpleViewConstIterEnd&) {} + constexpr NotSimpleViewConstIterEnd(const NotSimpleViewIterEnd&) + requires CopyConvertible + {} + constexpr NotSimpleViewConstIterEnd(NotSimpleViewIterEnd&&) + requires MoveConvertible + {} + + friend constexpr bool + operator==(const NotSimpleViewConstIterEnd&, const NotSimpleViewIterEnd&) { + return true; + } + friend constexpr bool + operator==(const NotSimpleViewConstIterEnd&, const NotSimpleViewIterBegin&) { + return true; + } +}; + +/* + * Goal: We will need a way to get a stride_view::__iterator and a + * stride_view::__iterator because those are the two possible types + * of the stride_view::__iterator constructor. The template value is determined + * by whether the stride_view::__iterator is derivative of a stride_view over a + * view that is simple. + * + * So, first things first, we need to build a stride_view over a (non-)simple view. + * There are (at least) two ways that a view can be non-simple: + * 1. The iterator type for const begin is different than the iterator type for begin + * 2. The iterator type for const end is different that the iterator type for end + * + * So, let's create two different classes where that is the case so that we can test + * for those conditions individually. We parameterize with a template to decide + * whether to + * 1. enable converting constructors between the non-const and the const version. + * That feature is important for testing the stride_view::__iterator converting + * constructor from a stride_view::_iterator iterator. + * 2. enable copyability. That feature is important for testing whether the requirement + * the that copy constructor for the stride_view::__iterator type actually moves + * the underlying iterator. + */ +template +struct NotSimpleViewDifferentBegin : std::ranges::view_base { + constexpr NotSimpleViewConstIterBegin begin() const { return {}; } + constexpr NotSimpleViewIterBegin begin() { return {}; } + + constexpr NotSimpleViewIterEnd end() const { return {}; } +}; + +template +struct NotSimpleViewDifferentEnd : std::ranges::view_base { + constexpr NotSimpleViewIterBegin begin() const { return {}; } + constexpr NotSimpleViewConstIterEnd end() const { return {}; } + constexpr NotSimpleViewIterEnd end() { return {}; } +}; + +constexpr bool non_simple_view_iter_ctor_test() { + using NotSimpleStrideView = std::ranges::stride_view>; + using NotSimpleStrideViewIter = std::ranges::iterator_t; + using NotSimpleStrideViewIterConst = std::ranges::iterator_t; + static_assert(!std::is_same_v); + return true; +} + +constexpr bool non_const_iterator_copy_ctor() { + // All tests share the following general configuration. + // + // Instantiate a stride view StrideView over a non-simple view (NotSimpleViewBeingStrided) whose + // 1. std::ranges::iterator_t base's type is NotSimpleViewBeingStridedIterator + // 2. std::ranges::iterator_t base's type is NotSimpleViewBeingStridedConstIterator + // 3. NotSimpleViewBeingStridedIterator is ONLY move-convertible to NotSimpleViewBeingStridedConstIterator + // 4. std::ranges::sentinel_t are the same whether SV is const or not. + // 5. the type of StrideView::end is the same whether StrideView is const or not. + // 6. the type of StrideView::begin is stride_view::iterator when StrideView is const and + // stride_view::iterator when StrideView is non const. + // Visually, it looks like this: + // + // NotSimpleViewBeingStrided(Const)Iterator <----- + // ^ | + // | | + // | begin (const?) | + // | | + // NotSimpleViewBeingStrided | + // ^ | + // | | + // | Strides over | + // | | + // StrideView | + // | | + // | begin (const?) | + // | | + // \/ | + // StrideView(Const)Iter | + // | | + // | base | + // | | + // --------------------------------- + + { + // Stride over non-simple view over whose iterators are copy convertible -- should look (statically) + // like it is possible copy construct the stride view's iterator (the move-only requirement comes from + // a move of the current between the copied-from iterator to the copied-to iterator). + using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd; + // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(std::convertible_to); + static_assert(std::constructible_from); + } + + { + // Stride over non-simple view over whose iterators are move convertible -- should look (statically) + // like it is possible copy construct the stride view's iterator (the move-only requirement comes from + // a move of the current between the copied-from iterator to the copied-to iterator). + using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd; + // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(std::convertible_to); + static_assert(std::constructible_from); + } + + { + // Stride over non-simple view over whose iterators are not convertible -- should not be able + // to copy construct the stride view's iterator. + using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd; + // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(!std::convertible_to); + static_assert(!std::constructible_from); + } + + { + // Stride over non-simple view over whose iterators are not convertible -- should not be able + // to copy construct the stride view's iterator. + using NotSimpleViewBeingStrided = NotSimpleViewDifferentEnd; + using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(std::convertible_to); + static_assert(std::convertible_to); + + StrideView str{NotSimpleViewBeingStrided{}, 5}; + // Confirm (5) + ASSERT_SAME_TYPE(StrideViewIter, decltype(str.begin())); + + // Now, do what we wanted the whole time: make sure that we can copy construct a + // stride_view::iterator from a stride_view::iterator. The copy + // constructor requires that the new __current_ StrideViewConstIter (type + // NotSimpleViewBeingStridedConstIterator) be constructable + // from the moved str.begin() __current_ (type NotSimpleViewBeingStridedConstIterator). + StrideViewConstIter iterator_copy{str.begin()}; + } + + { + // Stride over non-simple view over whose iterators are copy convertible -- should look (statically) + // like it is possible copy construct the stride view's iterator (the move-only requirement comes from + // a move of the current between the copied-from iterator to the copied-to iterator). + using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin; + // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(std::convertible_to); + static_assert(std::constructible_from); + } + + { + // Stride over non-simple view over whose iterators are move convertible -- should look (statically) + // like it is possible copy construct the stride view's iterator (the move-only requirement comes from + // a move of the current between the copied-from iterator to the copied-to iterator). + using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin; + // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(std::convertible_to); + static_assert(std::constructible_from); + } + + { + // Stride over non-simple view over whose iterators are not convertible -- should not be able + // to copy construct the stride view's iterator. + using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin; + // using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + // using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + // using StrideViewSentinel = std::ranges::sentinel_t; + // using StrideViewConstSentinel = std::ranges::sentinel_t; + + static_assert(!std::convertible_to); + static_assert(!std::constructible_from); + } + + { + // The NotSimpleViewBeingStrided template parameters mean that NotSimpleViewBeingStridedIterator + // can be move-converted to NotSimpleViewBeingStridedConstIterator but not copy-converted. + using NotSimpleViewBeingStrided = NotSimpleViewDifferentBegin; + using NotSimpleViewBeingStridedIterator = std::ranges::iterator_t; + using NotSimpleViewBeingStridedConstIterator = std::ranges::iterator_t; + + using StrideView = std::ranges::stride_view; + + using StrideViewIter = std::ranges::iterator_t; + using StrideViewConstIter = std::ranges::iterator_t; + + using StrideViewSentinel = std::ranges::sentinel_t; + using StrideViewConstSentinel = std::ranges::sentinel_t; + + // Confirm (1) and (2) + ASSERT_SAME_TYPE(NotSimpleViewBeingStridedIterator, decltype(std::declval().base())); + ASSERT_SAME_TYPE(NotSimpleViewBeingStridedConstIterator, decltype(std::declval().base())); + // Confirm (3) + static_assert(std::convertible_to); + static_assert(std::convertible_to); + // Confirm (4) + ASSERT_SAME_TYPE(StrideViewSentinel, StrideViewConstSentinel); + + StrideView str{NotSimpleViewBeingStrided{}, 5}; + // Confirm (5) + ASSERT_SAME_TYPE(StrideViewIter, decltype(str.begin())); + + // Now, do what we wanted the whole time: make sure that we can copy construct a + // stride_view::iterator from a stride_view::iterator. The copy + // constructor requires that the new __current_ StrideViewConstIter (type + // NotSimpleViewBeingStridedConstIterator) be constructable + // from the moved str.begin() __current_ (type NotSimpleViewBeingStridedConstIterator). + StrideViewConstIter iterator_copy{str.begin()}; + } + return true; +} + +int main(int, char**) { + non_simple_view_iter_ctor_test(); + static_assert(non_simple_view_iter_ctor_test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.verify.cpp new file mode 100644 index 0000000000000..eb90fa2e674e2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/ctor.default.verify.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// expected-no-diagnostics + +// __iterator() requires default_initializable> = default; + +#include + +#include "../types.h" + +struct NonDefaultConstructibleIterator : InputIter { + NonDefaultConstructibleIterator() = delete; + constexpr NonDefaultConstructibleIterator(int) {} +}; + +struct ViewWithNonDefaultConstructibleIterator : std::ranges::view_base { + constexpr NonDefaultConstructibleIterator begin() const { return NonDefaultConstructibleIterator{5}; } + constexpr std::default_sentinel_t end() const { return {}; } +}; +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +// If the type of the iterator of the range being strided is non-default +// constructible, then the stride view's iterator should not be default +// constructible, either! +static_assert(!std::is_default_constructible< std::ranges::iterator_t>()); +// If the type of the iterator of the range being strided is default +// constructible, then the stride view's iterator should be default +// constructible, too! +static_assert(std::is_default_constructible< + std::ranges::iterator_t< std::ranges::stride_view>>>()); diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp new file mode 100644 index 0000000000000..4c51c4b03965b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/decrement.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr __iterator& operator--() +// constexpr __iterator operator--(int) + +#include +#include + +#include "../types.h" + +template +concept CanPostDecrement = std::is_same_v()--)> && requires(T& t) { t--; }; +template +concept CanPreDecrement = std::is_same_v()))> && requires(T& t) { --t; }; + +// What operators are valid for an iterator derived from a stride view +// over an input view. +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(!std::ranges::bidirectional_range); +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); + +// What operators are valid for an iterator derived from a stride view +// over a forward view. +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(!std::ranges::bidirectional_range); +static_assert(!CanPostDecrement); +static_assert(!CanPostDecrement); + +// What operators are valid for an iterator derived from a stride view +// over a bidirectional view. +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(CanPostDecrement); +static_assert(CanPostDecrement); + +// What operators are valid for an iterator derived from a stride view +// over a random access view. +using RandomAccessView = BasicTestView>; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(CanPostDecrement); +static_assert(CanPostDecrement); + +template + requires(std::bidirectional_iterator) +constexpr bool test_operator_decrement(Iter begin, Iter end, Difference delta) { + using Base = BasicTestView; + + auto base_view_offset_zero = Base(begin, end); + // Because of the requires on the Iter template type, we are sure + // that the type of sv_incr_one is a bidirectional range. + auto sv_incr_diff = std::ranges::stride_view(base_view_offset_zero, delta); + auto sv_incr_end = sv_incr_diff.end(); + + // Recreate the "missing" calculation here -- to make sure that it matches. + auto missing = delta - (std::ranges::distance(base_view_offset_zero) % delta) % delta; + + auto sought = end + (missing - delta); + + assert(*sought == *(--sv_incr_end)); + assert(*sought == *(sv_incr_end)); + + sv_incr_end = sv_incr_diff.end(); + sv_incr_end--; + assert(*(end + (missing - delta)) == *(sv_incr_end)); + + return true; +} + +int main(int, char**) { + constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_operator_decrement(vec.begin(), vec.end(), 3); + test_operator_decrement(arr, arr + 11, 3); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp new file mode 100644 index 0000000000000..c1d2c3b6d7cd9 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/equal.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) +// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y) + +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +constexpr void testOne() { + using Range = BasicTestView; + static_assert(std::ranges::common_range); + using StrideView = std::ranges::stride_view; + + { + // simple test + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6, 7}; + const Range input(Iter{buffer}, Iter{buffer + 8}); + const StrideView sv(input, 1); + const StrideView sv_too(input, 2); + auto b = sv.begin(); + auto e = sv.end(); + auto b_too = sv_too.begin(); + + assert(b == b); + assert(!(b != b)); + + // When Range is a bidirectional_range, the type of e is + // default_sentinel_t and those do not compare to one another. + if constexpr (!std::ranges::bidirectional_range) { + assert(e == e); + assert(!(e != e)); + } + assert(!(b == e)); + assert(b != e); + + std::advance(b, 8); + std::advance(b_too, 4); + + assert(b == b_too); + assert(!(b != b_too)); + + assert(b == b); + assert(!(b != b)); + + // See above. + if constexpr (!std::ranges::bidirectional_range) { + assert(e == e); + assert(!(e != e)); + } + + assert(b == e); + assert(!(b != e)); + } + + // Default-constructed iterators compare equal. + { + int buffer[] = {0, 1, 2, -1, 4, 5, 6}; + const Range input(Iter{buffer}, Iter{buffer + 7}); + const std::ranges::stride_view sv(input, 1); + using StrideViewIter = decltype(sv.begin()); + StrideViewIter i1; + StrideViewIter i2; + assert(i1 == i2); + assert(!(i1 != i2)); + } + } +} + +constexpr bool test() { + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp new file mode 100644 index 0000000000000..08845f59d58ed --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/greater_than.pass.cpp @@ -0,0 +1,426 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr __iterator& operator++() +// constexpr void operator++(int) +// constexpr __iterator operator++(int) +// constexpr __iterator& operator--() +// constexpr __iterator operator--(int) +// constexpr __iterator& operator+=(difference_type __n) +// constexpr __iterator& operator-=(difference_type __n) +// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) +// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y) + +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlus = std::is_same_v() += 1)> && requires(T& t) { t += 1; }; +template +concept CanMinusEqual = std::is_same_v() -= 1)> && requires(T& t) { t -= 1; }; + +template +concept CanMinus = + // Note: Do *not* use std::iterator_traits here because T may not have + // all the required pieces when it is not a forward_range. + std::is_same_v() - std::declval())> && + requires(T& t) { t - t; }; + +template +concept CanSentinelMinus = + // Note: Do *not* use std::iterator_traits here because T may not have + // all the required pieces when it is not a forward_range. + std::is_same_v() - std::default_sentinel)> && + std::is_same_v())> && requires(T& t) { + t - std::default_sentinel; + std::default_sentinel - t; + }; + +template +concept CanDifferencePlus = std::is_same_v() + 1)> && requires(T& t) { + t + 1; +} && std::is_same_v())> && requires(T& t) { 1 + t; }; +template +concept CanDifferenceMinus = std::is_same_v() - 1)> && requires(T& t) { t - 1; }; + +template +concept CanPostDecrement = std::is_same_v()--)> && requires(T& t) { t--; }; +template +concept CanPreDecrement = std::is_same_v())> && requires(T& t) { --t; }; + +template +concept CanSubscript = requires(T& t) { t[5]; }; + +// What operators are valid for an iterator derived from a stride view +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); +static_assert(!CanPlus); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert(std::invocable, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert(std::invocable, StrideViewOverInputViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverInputViewIterator>); + +static_assert(!std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert(!std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); +static_assert(!CanPlus); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert(std::invocable, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert(std::invocable, StrideViewOverForwardViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverForwardViewIterator>); + +static_assert(!std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(CanPostDecrement); +static_assert(CanPreDecrement); +static_assert(!CanPlus); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert( + std::invocable, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>); +static_assert(std::invocable, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>); + +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a random access view. (non sized sentinel) +using RandomAccessView = BasicTestView>; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(CanPostDecrement); +static_assert(CanPreDecrement); +static_assert(CanPlus); +static_assert(CanMinusEqual); +static_assert(CanMinus); +static_assert(CanDifferencePlus); +static_assert(CanDifferenceMinus); +static_assert(!CanSentinelMinus); +static_assert( + std::invocable, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>); +static_assert(std::invocable, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>); + +static_assert( + std::is_invocable_v, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); + +static_assert(CanSubscript); + +using EqualableView = BasicTestView>; +using EqualableViewStrideView = std::ranges::stride_view; +using EqualableViewStrideViewIter = std::ranges::iterator_t; + +static_assert(std::equality_comparable>); +static_assert(std::equality_comparable); + +static_assert(!std::three_way_comparable>); +static_assert(!std::ranges::random_access_range); +static_assert(!std::three_way_comparable); + +using ThreeWayComparableView = BasicTestView>; +using ThreeWayComparableViewStrideView = std::ranges::stride_view; +using ThreeWayComparableStrideViewIter = std::ranges::iterator_t; + +static_assert(std::three_way_comparable>); +static_assert(std::ranges::random_access_range); +static_assert(std::three_way_comparable); + +using UnEqualableView = ViewOverNonCopyableIterator>; +using UnEqualableViewStrideView = std::ranges::stride_view; +using UnEqualableViewStrideViewIter = std::ranges::iterator_t; + +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable); + +static_assert(!std::three_way_comparable>); +static_assert(!std::ranges::random_access_range); +static_assert(!std::three_way_comparable); + +template + requires std::sized_sentinel_for && (!std::forward_iterator) +constexpr bool test_non_forward_operator_minus(Iter zero_begin, Iter one_begin, Iter end) { + using Base = BasicTestView; + // Test the non-forward-range operator- between two iterators (i.e., ceil) and an + // iterator and a default sentinel. + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::weakly_incrementable); + static_assert(!std::ranges::forward_range); + + // First, what operators are valid for an iterator derived from a stride view + // over a sized input view. + + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPlus); + static_assert(!CanMinusEqual); + static_assert(!CanDifferencePlus); + static_assert(!CanDifferenceMinus); + static_assert(CanSentinelMinus); + + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(std::is_invocable_v, std::default_sentinel_t, StrideViewIterator>); + static_assert(std::is_invocable_v, StrideViewIterator, std::default_sentinel_t>); + static_assert(!CanSubscript); + + auto base_view_offset_zero = Base(zero_begin, end); + auto base_view_offset_one = Base(one_begin, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + auto sv_zero_offset_zeroth_index = sv_zero_offset_begin; + auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + + auto sv_one_offset_oneth_index = sv_one_offset_begin; + auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + + static_assert(std::sized_sentinel_for, std::ranges::iterator_t>); + static_assert(CanMinus); + + // Check positive __n with exact multiple of left's stride. + assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1); + assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2); + // Check positive __n with non-exact multiple of left's stride (will do ceil here). + assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1); + assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2); + + // Check negative __n with exact multiple of left's stride. + assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1); + assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2); + // Check negative __n with non-exact multiple of left's stride (will do ceil here). + assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1); + assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2); + + assert(stride_view_over_base_zero_offset.end() == std::default_sentinel); + assert(std::default_sentinel == stride_view_over_base_zero_offset.end()); + + assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0); + assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0); + // assert((std::default_sentinel - )== 0); + + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == + std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == + -std::ranges::distance(stride_view_over_base_zero_offset)); + + return true; +} + +template +constexpr bool test_forward_operator_minus(Iter begin, Iter end, difference_type distance) { + // Test the forward-range operator- between two iterators (i.e., no ceil) and + // an iterator and a default sentinel. + using Base = BasicTestView; + + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::ranges::forward_range); + static_assert(std::weakly_incrementable); + + // First, what operators are valid for an iterator derived from a stride view + // over a sized forward view (even though it is actually much more than that!). + + static_assert(CanMinus); + static_assert(CanSentinelMinus); + + auto base_view_offset_zero = Base(begin, end); + auto base_view_offset_one = Base(begin + 1, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + auto sv_zero_offset_should_be_one = sv_zero_offset_begin; + auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin; + auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin; + + auto sv_one_offset_should_be_two = sv_one_offset_begin; + auto sv_one_offset_should_be_five = ++sv_one_offset_begin; + + static_assert(std::sized_sentinel_for, std::ranges::iterator_t>); + static_assert(CanMinus); + static_assert(std::forward_iterator>); + + // Check positive __n with exact multiple of left's stride. + assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1); + assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2); + + // Check positive __n with non-exact multiple of left's stride. + assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0); + assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1); + + // Check negative __n with exact multiple of left's stride. + assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1); + assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2); + + // Check negative __n with non-exact multiple of left's stride. + assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0); + assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1); + + // Make sure that all sentinel operations work! + assert(stride_view_over_base_zero_offset.end() == std::default_sentinel); + assert(std::default_sentinel == stride_view_over_base_zero_offset.end()); + + assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0); + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == + std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == + -std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance); + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance); + return true; +} + +constexpr bool test_properly_handling_missing() { + // Check whether __missing_ gets handled properly. + using Base = BasicTestView; + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto base = Base(arr, arr + 10); + auto strider = std::ranges::stride_view(base, 7); + + auto strider_iter = strider.end(); + + strider_iter--; + assert(*strider_iter == 8); + + // Now that we are back among the valid, we should + // have a normal stride length back (i.e., __missing_ + // should be equal to 0). + strider_iter--; + assert(*strider_iter == 1); + + strider_iter++; + assert(*strider_iter == 8); + + // By striding past the end, we are going to generate + // another __missing_ != 0 value. + strider_iter++; + assert(strider_iter == strider.end()); + + // Make sure that all sentinel operations work! + assert(strider.end() == std::default_sentinel); + assert(std::default_sentinel == strider.end()); + + assert(strider_iter - std::default_sentinel == 0); + assert(std::default_sentinel - strider.end() == 0); + assert(std::default_sentinel - strider_iter == 0); + + // Let's make sure that the newly regenerated __missing__ gets used. + strider_iter += -2; + assert(*strider_iter == 1); + + return true; +} + +int main(int, char**) { + { + constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_operator_minus(arr, arr + 11, 4); + test_forward_operator_minus(vec.begin(), vec.end(), 4); + } + + { + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_non_forward_operator_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10)); + } + + test_properly_handling_missing(); + static_assert(test_properly_handling_missing()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp new file mode 100644 index 0000000000000..036605d065fd3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/increment.pass.cpp @@ -0,0 +1,205 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr __iterator& operator++() +// constexpr void operator++(int) +// constexpr __iterator operator++(int) + +#include +#include + +#include "../types.h" + +template +concept CanPostIncrementVoid = std::is_same_v()++)> && requires(T& t) { t++; }; +template +concept CanPostIncrementIterator = std::is_same_v()++)> && requires(T& t) { t = t++; }; +template +concept CanPreIncrementIterator = std::is_same_v()))> && requires(T& t) { t = ++t; }; + +// A stride view with a base that is a non forward range returns void from operator++ +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; +static_assert(CanPostIncrementVoid); +static_assert(!CanPostIncrementIterator); +static_assert(CanPreIncrementIterator); + +// A stride view with a base that is a forward range returns void from operator++ +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; +static_assert(!CanPostIncrementVoid); +static_assert(CanPostIncrementIterator); +static_assert(CanPreIncrementIterator); + +template + requires std::sized_sentinel_for && (!std::forward_iterator) +constexpr bool test_non_forward_operator_increment(Iter zero_begin, Iter three_begin, Iter end) { + using Base = BasicTestView; + + auto base_view_offset_zero = Base(zero_begin, end); + auto base_view_offset_three = Base(three_begin, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_three_offset = std::ranges::stride_view(base_view_offset_three, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_three_offset_begin = stride_view_over_base_three_offset.begin(); + + auto sv_zero_offset_third_index = sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + ++sv_zero_offset_third_index; + assert(*sv_three_offset_begin == *sv_zero_offset_third_index); + + sv_zero_offset_third_index = sv_zero_offset_begin; + sv_zero_offset_third_index++; + assert(*sv_three_offset_begin == *sv_zero_offset_third_index); + + // See if both get to the end (with pre-increment). + auto sv_zero_offset_incremented_to_end = sv_zero_offset_begin; + ++sv_zero_offset_incremented_to_end; // 3 + ++sv_zero_offset_incremented_to_end; // 6 + ++sv_zero_offset_incremented_to_end; // 9 + ++sv_zero_offset_incremented_to_end; // End + + auto sv_three_offset_incremented_to_end = sv_three_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + ++sv_three_offset_incremented_to_end; // 6 + ++sv_three_offset_incremented_to_end; // 9 + ++sv_three_offset_incremented_to_end; // End + + assert(sv_three_offset_incremented_to_end == sv_zero_offset_incremented_to_end); + assert(sv_three_offset_incremented_to_end == stride_view_over_base_three_offset.end()); + assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end()); + + // See if both get to the end (with post-increment). + sv_zero_offset_incremented_to_end = sv_zero_offset_begin; + sv_zero_offset_incremented_to_end++; // 3 + sv_zero_offset_incremented_to_end++; // 6 + sv_zero_offset_incremented_to_end++; // 9 + sv_zero_offset_incremented_to_end++; // End + + sv_three_offset_incremented_to_end = sv_three_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + sv_three_offset_incremented_to_end++; // 6 + sv_three_offset_incremented_to_end++; // 9 + sv_three_offset_incremented_to_end++; // End + + assert(sv_three_offset_incremented_to_end == sv_zero_offset_incremented_to_end); + assert(sv_three_offset_incremented_to_end == stride_view_over_base_three_offset.end()); + assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end()); + + return true; +} + +template +constexpr bool test_forward_operator_increment(Iter begin, Iter end) { + using Base = BasicTestView; + + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::ranges::forward_range); + static_assert(std::weakly_incrementable); + + auto base_view_offset_zero = Base(begin, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + + // Create a ground truth for comparison. + auto sv_zero_offset_third_index_key = stride_view_over_base_zero_offset.begin(); + sv_zero_offset_third_index_key++; + + auto sv_zero_offset_third_index = ++sv_zero_offset_begin; + assert(*sv_zero_offset_third_index == *sv_zero_offset_begin); + assert(*sv_zero_offset_third_index == *sv_zero_offset_third_index_key); + + sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + sv_zero_offset_third_index = sv_zero_offset_begin; + sv_zero_offset_third_index++; + assert(*sv_zero_offset_third_index == *sv_zero_offset_third_index_key); + + sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_zero_offset_incremented_to_end = sv_zero_offset_begin; + ++sv_zero_offset_incremented_to_end; // 3 + ++sv_zero_offset_incremented_to_end; // 6 + ++sv_zero_offset_incremented_to_end; // 9 + ++sv_zero_offset_incremented_to_end; // End + + auto sv_zero_offset_incremented_to_end_reset = sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // 3 + sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // 6 + sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // 9 + sv_zero_offset_incremented_to_end_reset = ++sv_zero_offset_incremented_to_end_reset; // End + + assert(sv_zero_offset_incremented_to_end == sv_zero_offset_incremented_to_end_reset); + assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end()); + + sv_zero_offset_incremented_to_end = sv_zero_offset_begin; + sv_zero_offset_incremented_to_end++; // 3 + sv_zero_offset_incremented_to_end++; // 6 + sv_zero_offset_incremented_to_end++; // 9 + sv_zero_offset_incremented_to_end++; // End + assert(sv_zero_offset_incremented_to_end == stride_view_over_base_zero_offset.end()); + + return true; +} + +constexpr bool test_properly_handling_missing() { + // Check whether __missing_ gets handled properly. + using Base = BasicTestView; + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto base = Base(arr, arr + 10); + auto strider = std::ranges::stride_view(base, 7); + + auto strider_iter = strider.end(); + + strider_iter--; + assert(*strider_iter == 8); + + // Now that we are back among the valid, we should + // have a normal stride length back (i.e., __missing_ + // should be equal to 0). + strider_iter--; + assert(*strider_iter == 1); + + strider_iter++; + assert(*strider_iter == 8); + + // By striding past the end, we are going to generate + // another __missing_ != 0 value. + strider_iter++; + assert(strider_iter == strider.end()); + + // Make sure that all sentinel operations work! + assert(strider.end() == std::default_sentinel); + assert(std::default_sentinel == strider.end()); + + assert(strider_iter - std::default_sentinel == 0); + assert(std::default_sentinel - strider.end() == 0); + assert(std::default_sentinel - strider_iter == 0); + + // Let's make sure that the newly regenerated __missing__ gets used. + strider_iter += -2; + assert(*strider_iter == 1); + + return true; +} + +int main(int, char**) { + { + constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_operator_increment(arr, arr + 11); + test_forward_operator_increment(vec.begin(), vec.end()); + } + + { + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_non_forward_operator_increment(SizedInputIter(arr), SizedInputIter(arr + 3), SizedInputIter(arr + 10)); + } + + test_properly_handling_missing(); + static_assert(test_properly_handling_missing()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp new file mode 100644 index 0000000000000..5275115437bcc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_move.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// friend constexpr range_rvalue_reference_t<_Base> iter_move(__iterator const& __it) +// noexcept(noexcept(ranges::iter_move(__it.__current_))) + +#include +#include + +#include "../types.h" +#include "test_macros.h" + +template +concept iter_moveable = requires(T&& t) { std::ranges::iter_move(t); }; + +constexpr bool test() { + { + int a[] = {4, 3, 2, 1}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin(); + + static_assert(iter_moveable>); + ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(svb))); + static_assert(noexcept(std::ranges::iter_move(svb))); + + auto&& result = std::ranges::iter_move(svb); + assert(iter_move_counter == 1); + assert(result == 4); + + svb++; + result = std::ranges::iter_move(svb); + assert(iter_move_counter == 2); + assert(result == 3); + } + + { + int a[] = {1, 2, 3, 4}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a, a + 4, &iter_move_counter), 1).begin(); + + static_assert(iter_moveable>); + ASSERT_SAME_TYPE(int, decltype(std::ranges::iter_move(svb))); + static_assert(!noexcept(std::ranges::iter_move(svb))); + + auto&& result = std::ranges::iter_move(svb); + assert(iter_move_counter == 1); + assert(result == 1); + + svb++; + result = std::ranges::iter_move(svb); + assert(iter_move_counter == 2); + assert(result == 2); + } + + { + std::vector a = {4, 5, 6, 7, 8}; + + int iter_move_counter(0); + using View = IterMoveIterSwapTestRange::iterator, true, false>; + + using StrideView = std::ranges::stride_view; + auto svb = StrideView(View(a.begin(), a.end(), &iter_move_counter), 1).begin(); + + static_assert(!noexcept(std::ranges::iter_move(svb))); + + auto&& result = std::ranges::iter_move(svb); + assert(iter_move_counter == 1); + assert(result == 4); + + svb++; + result = std::ranges::iter_move(svb); + assert(iter_move_counter == 2); + assert(result == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp new file mode 100644 index 0000000000000..c9b87dfec6ecd --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/iter_swap.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// friend constexpr void iter_swap(__iterator const& __x, __iterator const& __y) +// noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_))) +// requires indirectly_swappable> + +#include + +#include "../types.h" + +template +concept swappable = requires(T&& t, T&& u) { std::ranges::iter_swap(t, u); }; + +constexpr bool test() { + { + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8}; + + int iter_move_counter_one(0); + int iter_move_counter_two(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin(); + auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin(); + + static_assert(swappable>); + static_assert(noexcept(std::ranges::iter_swap(svba, svbb))); + + assert(a[0] == 1); + assert(b[0] == 5); + + std::ranges::iter_swap(svba, svbb); + assert(iter_move_counter_one == 1); + assert(iter_move_counter_two == 1); + + assert(a[0] == 5); + assert(b[0] == 1); + } + + { + int a[] = {1, 2, 3, 4}; + int b[] = {5, 6, 7, 8}; + + int iter_move_counter_one(0); + int iter_move_counter_two(0); + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + auto svba = StrideView(View(a, a + 4, &iter_move_counter_one), 1).begin(); + auto svbb = StrideView(View(b, b + 4, &iter_move_counter_two), 1).begin(); + + static_assert(swappable>); + static_assert(!noexcept(std::ranges::iter_swap(svba, svbb))); + + assert(a[0] == 1); + assert(b[0] == 5); + + std::ranges::iter_swap(svba, svbb); + + assert(iter_move_counter_one == 1); + assert(iter_move_counter_two == 1); + assert(a[0] == 5); + assert(b[0] == 1); + } + + { + using View = IterMoveIterSwapTestRange; + using StrideView = std::ranges::stride_view; + + static_assert(!swappable>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp new file mode 100644 index 0000000000000..13eecf9283dee --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/less_than.pass.cpp @@ -0,0 +1,426 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr __iterator& operator++() +// constexpr void operator++(int) +// constexpr __iterator operator++(int) +// constexpr __iterator& operator--() +// constexpr __iterator operator--(int) +// constexpr __iterator& operator+=(difference_type __n) +// constexpr __iterator& operator-=(difference_type __n) +// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) +// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y) + +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlusEqual = std::is_same_v() += 1)> && requires(T& t) { t += 1; }; +template +concept CanMinusEqual = std::is_same_v() -= 1)> && requires(T& t) { t -= 1; }; + +template +concept CanMinus = + // Note: Do *not* use std::iterator_traits here because T may not have + // all the required pieces when it is not a forward_range. + std::is_same_v() - std::declval())> && + requires(T& t) { t - t; }; + +template +concept CanSentinelMinus = + // Note: Do *not* use std::iterator_traits here because T may not have + // all the required pieces when it is not a forward_range. + std::is_same_v() - std::default_sentinel)> && + std::is_same_v())> && requires(T& t) { + t - std::default_sentinel; + std::default_sentinel - t; + }; + +template +concept CanDifferencePlus = std::is_same_v() + 1)> && requires(T& t) { + t + 1; +} && std::is_same_v())> && requires(T& t) { 1 + t; }; +template +concept CanDifferenceMinus = std::is_same_v() - 1)> && requires(T& t) { t - 1; }; + +template +concept CanPostDecrement = std::is_same_v()--)> && requires(T& t) { t--; }; +template +concept CanPreDecrement = std::is_same_v())> && requires(T& t) { --t; }; + +template +concept CanSubscript = requires(T& t) { t[5]; }; + +// What operators are valid for an iterator derived from a stride view +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); +static_assert(!CanPlusEqual); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert(std::invocable, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert(std::invocable, StrideViewOverInputViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverInputViewIterator>); + +static_assert(!std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert(!std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); +static_assert(!CanPlusEqual); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert(std::invocable, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert(std::invocable, StrideViewOverForwardViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverForwardViewIterator>); + +static_assert(!std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(CanPostDecrement); +static_assert(CanPreDecrement); +static_assert(!CanPlusEqual); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert( + std::invocable, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>); +static_assert(std::invocable, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>); + +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a random access view. (non sized sentinel) +using RandomAccessView = BasicTestView>; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(CanPostDecrement); +static_assert(CanPreDecrement); +static_assert(CanPlusEqual); +static_assert(CanMinusEqual); +static_assert(CanMinus); +static_assert(CanDifferencePlus); +static_assert(CanDifferenceMinus); +static_assert(!CanSentinelMinus); +static_assert( + std::invocable, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>); +static_assert(std::invocable, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>); + +static_assert( + std::is_invocable_v, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); + +static_assert(CanSubscript); + +using EqualableView = BasicTestView>; +using EqualableViewStrideView = std::ranges::stride_view; +using EqualableViewStrideViewIter = std::ranges::iterator_t; + +static_assert(std::equality_comparable>); +static_assert(std::equality_comparable); + +static_assert(!std::three_way_comparable>); +static_assert(!std::ranges::random_access_range); +static_assert(!std::three_way_comparable); + +using ThreeWayComparableView = BasicTestView>; +using ThreeWayComparableViewStrideView = std::ranges::stride_view; +using ThreeWayComparableStrideViewIter = std::ranges::iterator_t; + +static_assert(std::three_way_comparable>); +static_assert(std::ranges::random_access_range); +static_assert(std::three_way_comparable); + +using UnEqualableView = ViewOverNonCopyableIterator>; +using UnEqualableViewStrideView = std::ranges::stride_view; +using UnEqualableViewStrideViewIter = std::ranges::iterator_t; + +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable); + +static_assert(!std::three_way_comparable>); +static_assert(!std::ranges::random_access_range); +static_assert(!std::three_way_comparable); + +template + requires std::sized_sentinel_for && (!std::forward_iterator) +constexpr bool test_non_forward_operator_minus(Iter zero_begin, Iter one_begin, Iter end) { + using Base = BasicTestView; + // Test the non-forward-range operator- between two iterators (i.e., ceil) and an + // iterator and a default sentinel. + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::weakly_incrementable); + static_assert(!std::ranges::forward_range); + + // First, what operators are valid for an iterator derived from a stride view + // over a sized input view. + + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPlusEqual); + static_assert(!CanMinusEqual); + static_assert(!CanDifferencePlus); + static_assert(!CanDifferenceMinus); + static_assert(CanSentinelMinus); + + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(std::is_invocable_v, std::default_sentinel_t, StrideViewIterator>); + static_assert(std::is_invocable_v, StrideViewIterator, std::default_sentinel_t>); + static_assert(!CanSubscript); + + auto base_view_offset_zero = Base(zero_begin, end); + auto base_view_offset_one = Base(one_begin, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + auto sv_zero_offset_zeroth_index = sv_zero_offset_begin; + auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + + auto sv_one_offset_oneth_index = sv_one_offset_begin; + auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + + static_assert(std::sized_sentinel_for, std::ranges::iterator_t>); + static_assert(CanMinus); + + // Check positive __n with exact multiple of left's stride. + assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1); + assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2); + // Check positive __n with non-exact multiple of left's stride (will do ceil here). + assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1); + assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2); + + // Check negative __n with exact multiple of left's stride. + assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1); + assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2); + // Check negative __n with non-exact multiple of left's stride (will do ceil here). + assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1); + assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2); + + assert(stride_view_over_base_zero_offset.end() == std::default_sentinel); + assert(std::default_sentinel == stride_view_over_base_zero_offset.end()); + + assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0); + assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0); + // assert((std::default_sentinel - )== 0); + + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == + std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == + -std::ranges::distance(stride_view_over_base_zero_offset)); + + return true; +} + +template +constexpr bool test_forward_operator_minus(Iter begin, Iter end, difference_type distance) { + // Test the forward-range operator- between two iterators (i.e., no ceil) and + // an iterator and a default sentinel. + using Base = BasicTestView; + + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::ranges::forward_range); + static_assert(std::weakly_incrementable); + + // First, what operators are valid for an iterator derived from a stride view + // over a sized forward view (even though it is actually much more than that!). + + static_assert(CanMinus); + static_assert(CanSentinelMinus); + + auto base_view_offset_zero = Base(begin, end); + auto base_view_offset_one = Base(begin + 1, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + auto sv_zero_offset_should_be_one = sv_zero_offset_begin; + auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin; + auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin; + + auto sv_one_offset_should_be_two = sv_one_offset_begin; + auto sv_one_offset_should_be_five = ++sv_one_offset_begin; + + static_assert(std::sized_sentinel_for, std::ranges::iterator_t>); + static_assert(CanMinus); + static_assert(std::forward_iterator>); + + // Check positive __n with exact multiple of left's stride. + assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1); + assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2); + + // Check positive __n with non-exact multiple of left's stride. + assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0); + assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1); + + // Check negative __n with exact multiple of left's stride. + assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1); + assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2); + + // Check negative __n with non-exact multiple of left's stride. + assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0); + assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1); + + // Make sure that all sentinel operations work! + assert(stride_view_over_base_zero_offset.end() == std::default_sentinel); + assert(std::default_sentinel == stride_view_over_base_zero_offset.end()); + + assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0); + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == + std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == + -std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance); + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance); + return true; +} + +constexpr bool test_properly_handling_missing() { + // Check whether __missing_ gets handled properly. + using Base = BasicTestView; + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto base = Base(arr, arr + 10); + auto strider = std::ranges::stride_view(base, 7); + + auto strider_iter = strider.end(); + + strider_iter--; + assert(*strider_iter == 8); + + // Now that we are back among the valid, we should + // have a normal stride length back (i.e., __missing_ + // should be equal to 0). + strider_iter--; + assert(*strider_iter == 1); + + strider_iter++; + assert(*strider_iter == 8); + + // By striding past the end, we are going to generate + // another __missing_ != 0 value. + strider_iter++; + assert(strider_iter == strider.end()); + + // Make sure that all sentinel operations work! + assert(strider.end() == std::default_sentinel); + assert(std::default_sentinel == strider.end()); + + assert(strider_iter - std::default_sentinel == 0); + assert(std::default_sentinel - strider.end() == 0); + assert(std::default_sentinel - strider_iter == 0); + + // Let's make sure that the newly regenerated __missing__ gets used. + strider_iter += -2; + assert(*strider_iter == 1); + + return true; +} + +int main(int, char**) { + { + constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_operator_minus(arr, arr + 11, 4); + test_forward_operator_minus(vec.begin(), vec.end(), 4); + } + + { + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_non_forward_operator_minus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10)); + } + + test_properly_handling_missing(); + static_assert(test_properly_handling_missing()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp new file mode 100644 index 0000000000000..c3d71b7387ea7 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/operator.pass.cpp @@ -0,0 +1,426 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr __iterator& operator++() +// constexpr void operator++(int) +// constexpr __iterator operator++(int) +// constexpr __iterator& operator--() +// constexpr __iterator operator--(int) +// constexpr __iterator& operator+=(difference_type __n) +// constexpr __iterator& operator-=(difference_type __n) +// friend constexpr bool operator==(__iterator const& __x, default_sentinel_t) +// friend constexpr bool operator==(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator>(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<=(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator>=(__iterator const& __x, __iterator const& __y) +// friend constexpr bool operator<=>(__iterator const& __x, __iterator const& __y) + +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlus = std::is_same_v() += 1)> && requires(T& t) { t += 1; }; +template +concept CanMinusEqual = std::is_same_v() -= 1)> && requires(T& t) { t -= 1; }; + +template +concept CanMinus = + // Note: Do *not* use std::iterator_traits here because T may not have + // all the required pieces when it is not a forward_range. + std::is_same_v() - std::declval())> && + requires(T& t) { t - t; }; + +template +concept CanSentinelMinus = + // Note: Do *not* use std::iterator_traits here because T may not have + // all the required pieces when it is not a forward_range. + std::is_same_v() - std::default_sentinel)> && + std::is_same_v())> && requires(T& t) { + t - std::default_sentinel; + std::default_sentinel - t; + }; + +template +concept CanDifferencePlus = std::is_same_v() + 1)> && requires(T& t) { + t + 1; +} && std::is_same_v())> && requires(T& t) { 1 + t; }; +template +concept CanDifferenceMinus = std::is_same_v() - 1)> && requires(T& t) { t - 1; }; + +template +concept CanPostDecrement = std::is_same_v()--)> && requires(T& t) { t--; }; +template +concept CanPreDecrement = std::is_same_v())> && requires(T& t) { --t; }; + +template +concept CanSubscript = requires(T& t) { t[5]; }; + +// What operators are valid for an iterator derived from a stride view +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); +static_assert(!CanPlus); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert(std::invocable, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert(std::invocable, StrideViewOverInputViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverInputViewIterator>); + +static_assert(!std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert(!std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverInputViewIterator, StrideViewOverInputViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(!CanPostDecrement); +static_assert(!CanPreDecrement); +static_assert(!CanPlus); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert(std::invocable, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert(std::invocable, StrideViewOverForwardViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverForwardViewIterator>); + +static_assert(!std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); +static_assert( + !std::is_invocable_v, StrideViewOverForwardViewIterator, StrideViewOverForwardViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(CanPostDecrement); +static_assert(CanPreDecrement); +static_assert(!CanPlus); +static_assert(!CanMinusEqual); +static_assert(!CanMinus); +static_assert(!CanDifferencePlus); +static_assert(!CanDifferenceMinus); +static_assert(CanSentinelMinus); +static_assert( + std::invocable, StrideViewOverBidirectionalViewIterator, StrideViewOverBidirectionalViewIterator>); +static_assert(std::invocable, StrideViewOverBidirectionalViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverBidirectionalViewIterator>); + +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); +static_assert(!std::is_invocable_v, + StrideViewOverBidirectionalViewIterator, + StrideViewOverBidirectionalViewIterator>); + +static_assert(!CanSubscript); + +// What operators are valid for an iterator derived from a stride view +// over a random access view. (non sized sentinel) +using RandomAccessView = BasicTestView>; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>; + +static_assert(std::weakly_incrementable); + +static_assert(CanPostDecrement); +static_assert(CanPreDecrement); +static_assert(CanPlus); +static_assert(CanMinusEqual); +static_assert(CanMinus); +static_assert(CanDifferencePlus); +static_assert(CanDifferenceMinus); +static_assert(!CanSentinelMinus); +static_assert( + std::invocable, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>); +static_assert(std::invocable, StrideViewOverRandomAccessViewIterator, std::default_sentinel_t>); +static_assert(std::invocable, std::default_sentinel_t, StrideViewOverRandomAccessViewIterator>); + +static_assert( + std::is_invocable_v, StrideViewOverRandomAccessViewIterator, StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); +static_assert(std::is_invocable_v, + StrideViewOverRandomAccessViewIterator, + StrideViewOverRandomAccessViewIterator>); + +static_assert(CanSubscript); + +using EqualableView = BasicTestView>; +using EqualableViewStrideView = std::ranges::stride_view; +using EqualableViewStrideViewIter = std::ranges::iterator_t; + +static_assert(std::equality_comparable>); +static_assert(std::equality_comparable); + +static_assert(!std::three_way_comparable>); +static_assert(!std::ranges::random_access_range); +static_assert(!std::three_way_comparable); + +using ThreeWayComparableView = BasicTestView>; +using ThreeWayComparableViewStrideView = std::ranges::stride_view; +using ThreeWayComparableStrideViewIter = std::ranges::iterator_t; + +static_assert(std::three_way_comparable>); +static_assert(std::ranges::random_access_range); +static_assert(std::three_way_comparable); + +using UnEqualableView = ViewOverNonCopyableIterator>; +using UnEqualableViewStrideView = std::ranges::stride_view; +using UnEqualableViewStrideViewIter = std::ranges::iterator_t; + +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable); + +static_assert(!std::three_way_comparable>); +static_assert(!std::ranges::random_access_range); +static_assert(!std::three_way_comparable); + +template + requires std::sized_sentinel_for && (!std::forward_iterator) +constexpr bool test_non_forward_operator_plus(Iter zero_begin, Iter one_begin, Iter end) { + using Base = BasicTestView; + // Test the non-forward-range operator- between two iterators (i.e., ceil) and an + // iterator and a default sentinel. + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::weakly_incrementable); + static_assert(!std::ranges::forward_range); + + // First, what operators are valid for an iterator derived from a stride view + // over a sized input view. + + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPlus); + static_assert(!CanMinusEqual); + static_assert(!CanDifferencePlus); + static_assert(!CanDifferenceMinus); + static_assert(CanSentinelMinus); + + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(!std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(std::is_invocable_v, StrideViewIterator, StrideViewIterator>); + static_assert(std::is_invocable_v, std::default_sentinel_t, StrideViewIterator>); + static_assert(std::is_invocable_v, StrideViewIterator, std::default_sentinel_t>); + static_assert(!CanSubscript); + + auto base_view_offset_zero = Base(zero_begin, end); + auto base_view_offset_one = Base(one_begin, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + auto sv_zero_offset_zeroth_index = sv_zero_offset_begin; + auto sv_zero_offset_third_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + auto sv_zero_offset_sixth_index = ++sv_zero_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + + auto sv_one_offset_oneth_index = sv_one_offset_begin; + auto sv_one_offset_fourth_index = ++sv_one_offset_begin; // With a stride of 3, so ++ moves 3 indexes. + + static_assert(std::sized_sentinel_for, std::ranges::iterator_t>); + static_assert(CanMinus); + + // Check positive __n with exact multiple of left's stride. + assert(sv_zero_offset_third_index - sv_zero_offset_zeroth_index == 1); + assert(sv_zero_offset_sixth_index - sv_zero_offset_zeroth_index == 2); + // Check positive __n with non-exact multiple of left's stride (will do ceil here). + assert(sv_one_offset_oneth_index - sv_zero_offset_zeroth_index == 1); + assert(sv_one_offset_fourth_index - sv_zero_offset_zeroth_index == 2); + + // Check negative __n with exact multiple of left's stride. + assert(sv_zero_offset_zeroth_index - sv_zero_offset_third_index == -1); + assert(sv_zero_offset_zeroth_index - sv_zero_offset_sixth_index == -2); + // Check negative __n with non-exact multiple of left's stride (will do ceil here). + assert(sv_zero_offset_zeroth_index - sv_one_offset_oneth_index == -1); + assert(sv_zero_offset_zeroth_index - sv_one_offset_fourth_index == -2); + + assert(stride_view_over_base_zero_offset.end() == std::default_sentinel); + assert(std::default_sentinel == stride_view_over_base_zero_offset.end()); + + assert(std::default_sentinel - stride_view_over_base_zero_offset.end() == 0); + assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0); + // assert((std::default_sentinel - )== 0); + + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == + std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == + -std::ranges::distance(stride_view_over_base_zero_offset)); + + return true; +} + +template +constexpr bool test_forward_operator_plus(Iter begin, Iter end, difference_type distance) { + // Test the forward-range operator- between two iterators (i.e., no ceil) and + // an iterator and a default sentinel. + using Base = BasicTestView; + + using StrideViewIterator = std::ranges::iterator_t>; + static_assert(std::ranges::forward_range); + static_assert(std::weakly_incrementable); + + // First, what operators are valid for an iterator derived from a stride view + // over a sized forward view (even though it is actually much more than that!). + + static_assert(CanMinus); + static_assert(CanSentinelMinus); + + auto base_view_offset_zero = Base(begin, end); + auto base_view_offset_one = Base(begin + 1, end); + auto stride_view_over_base_zero_offset = std::ranges::stride_view(base_view_offset_zero, 3); + auto stride_view_over_base_one_offset = std::ranges::stride_view(base_view_offset_one, 3); + + auto sv_zero_offset_begin = stride_view_over_base_zero_offset.begin(); + auto sv_one_offset_begin = stride_view_over_base_one_offset.begin(); + + auto sv_zero_offset_should_be_one = sv_zero_offset_begin; + auto sv_zero_offset_should_be_four = ++sv_zero_offset_begin; + auto sv_zero_offset_should_be_seven = ++sv_zero_offset_begin; + + auto sv_one_offset_should_be_two = sv_one_offset_begin; + auto sv_one_offset_should_be_five = ++sv_one_offset_begin; + + static_assert(std::sized_sentinel_for, std::ranges::iterator_t>); + static_assert(CanMinus); + static_assert(std::forward_iterator>); + + // Check positive __n with exact multiple of left's stride. + assert(sv_zero_offset_should_be_four - sv_zero_offset_should_be_one == 1); + assert(sv_zero_offset_should_be_seven - sv_zero_offset_should_be_one == 2); + + // Check positive __n with non-exact multiple of left's stride. + assert(sv_one_offset_should_be_two - sv_zero_offset_should_be_one == 0); + assert(sv_one_offset_should_be_five - sv_zero_offset_should_be_one == 1); + + // Check negative __n with exact multiple of left's stride. + assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_four == -1); + assert(sv_zero_offset_should_be_one - sv_zero_offset_should_be_seven == -2); + + // Check negative __n with non-exact multiple of left's stride. + assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_two == 0); + assert(sv_zero_offset_should_be_one - sv_one_offset_should_be_five == -1); + + // Make sure that all sentinel operations work! + assert(stride_view_over_base_zero_offset.end() == std::default_sentinel); + assert(std::default_sentinel == stride_view_over_base_zero_offset.end()); + + assert(stride_view_over_base_zero_offset.end() - std::default_sentinel == 0); + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == + std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == + -std::ranges::distance(stride_view_over_base_zero_offset)); + assert(stride_view_over_base_zero_offset.begin() - std::default_sentinel == -distance); + assert(std::default_sentinel - stride_view_over_base_zero_offset.begin() == distance); + return true; +} + +constexpr bool test_properly_handling_missing() { + // Check whether __missing_ gets handled properly. + using Base = BasicTestView; + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + auto base = Base(arr, arr + 10); + auto strider = std::ranges::stride_view(base, 7); + + auto strider_iter = strider.end(); + + strider_iter--; + assert(*strider_iter == 8); + + // Now that we are back among the valid, we should + // have a normal stride length back (i.e., __missing_ + // should be equal to 0). + strider_iter--; + assert(*strider_iter == 1); + + strider_iter++; + assert(*strider_iter == 8); + + // By striding past the end, we are going to generate + // another __missing_ != 0 value. + strider_iter++; + assert(strider_iter == strider.end()); + + // Make sure that all sentinel operations work! + assert(strider.end() == std::default_sentinel); + assert(std::default_sentinel == strider.end()); + + assert(strider_iter - std::default_sentinel == 0); + assert(std::default_sentinel - strider.end() == 0); + assert(std::default_sentinel - strider_iter == 0); + + // Let's make sure that the newly regenerated __missing__ gets used. + strider_iter += -2; + assert(*strider_iter == 1); + + return true; +} + +int main(int, char**) { + { + constexpr int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + test_forward_operator_plus(arr, arr + 11, 4); + test_forward_operator_plus(vec.begin(), vec.end(), 4); + } + + { + int arr[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_non_forward_operator_plus(SizedInputIter(arr), SizedInputIter(arr + 1), SizedInputIter(arr + 10)); + } + + test_properly_handling_missing(); + static_assert(test_properly_handling_missing()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp new file mode 100644 index 0000000000000..d18dc0b0d1b2f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 __n, const __iterator &__i) +// constexpr __iterator operator+(const __iterator &__i, difference_type __n) + +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlus = + std::is_same_v() + std::declval())> && + std::is_same_v() + std::declval())> && + requires(T& t, T::__iterator::difference_type& u) { t = t + u; } && + requires(T& t, T::__iterator::difference_type& u) { t = u + t; }; + +// Make sure that we cannot use + on a stride view iterator and difference_type +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::input_range); +static_assert(!CanPlus); + +// Make sure that we cannot use + on a stride view iterator and difference_type +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::forward_range); +static_assert(!CanPlus); + +// Make sure that we cannot use + on a stride view iterator and difference_type +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(!CanPlus); + +// Make sure that we can use += on a stride view iterator and difference_type +// over a random access view. (non sized sentinel) +template > +using RandomAccessView = BasicTestView; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>>; + +static_assert(std::ranges::random_access_range>); +static_assert(CanPlus); + +constexpr bool test_random_access_operator_plus_equal() { + using Iter = std::vector::iterator; + using Diff = Iter::difference_type; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + // Test the operator+ between an iterator and its difference type. Pay attention solely to + // stride views over random-access ranges because operator+ is not applicable to others. + + auto begin = vec.begin(); + auto end = vec.end(); + Diff distance = 4; + + // Do not use the RandomAccessView defined in types.h to give the test user more power + // to customize an iterator and a default sentinel. + using Base = RandomAccessView; + static_assert(std::ranges::random_access_range); + + auto base_view = Base(begin, end); + auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1); + + auto base_view_offset = Base(begin + distance, end); + auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1); + + assert(*(stride_view_over_base_view.begin() + distance) == *(stride_view_over_base_view_offset.begin())); + + auto distance_to_last = (end - 1) - begin; + auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, distance_to_last); + + assert(*(stride_view_over_base_view_big_step.begin() + 1) == + *(stride_view_over_base_view.begin() + distance_to_last)); + assert((stride_view_over_base_view_big_step.begin() + 2) == (stride_view_over_base_view.end())); + + return true; +} + +consteval bool do_static_tests() { + assert(test_random_access_operator_plus_equal()); + return true; +} + +int main(int, char**) { + static_assert(do_static_tests()); + assert(do_static_tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp new file mode 100644 index 0000000000000..17cb295a48e6b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/iterator/plus_equal.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr __iterator& operator+=(difference_type __n) + +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +concept CanPlus = + std::is_same_v() += std::declval())> && + requires(T& t, T::__iterator::difference_type& u) { t += u; }; + +// Make sure that we cannot use += on a stride view iterator +// over an input view.(sized sentinel) +using InputView = BasicTestView, sized_sentinel>>; +using StrideViewOverInputViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::input_range); +static_assert(!CanPlus); + +// Make sure that we cannot use += on a stride view iterator +// over a forward view.(sized sentinel) +using ForwardView = BasicTestView, sized_sentinel>>; +using StrideViewOverForwardViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::forward_range); +static_assert(!CanPlus); + +// Make sure that we cannot use += on a stride view iterator +// over a bidirectional view. (sized sentinel) +using BidirectionalView = BasicTestView, sized_sentinel>>; +using StrideViewOverBidirectionalViewIterator = std::ranges::iterator_t>; + +static_assert(std::ranges::bidirectional_range); +static_assert(!CanPlus); + +// Make sure that we can use += on a stride view iterator +// over a random access view. (non sized sentinel) +template > +using RandomAccessView = BasicTestView; +using StrideViewOverRandomAccessViewIterator = std::ranges::iterator_t>>; + +static_assert(std::ranges::random_access_range>); +static_assert(CanPlus); + +constexpr bool test_random_access_operator_plus_equal() { + using Iter = std::vector::iterator; + using Diff = Iter::difference_type; + std::vector vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + // Test the operator+ between an iterator and its difference type. Pay attention solely to + // stride views over random-access ranges because operator+ is not applicable to others. + + auto begin = vec.begin(); + auto end = vec.end(); + Diff distance = 4; + + // Test the forward-range operator+= between an iterator and its difference type. + // Do not use the RandomAccessView defined in types.h to give the test user more power + // to customize an iterator and a default sentinel. + using Base = RandomAccessView; + static_assert(std::ranges::random_access_range); + + auto base_view = Base(begin, end); + auto stride_view_over_base_view = std::ranges::stride_view(base_view, 1); + + auto base_view_offset = Base(begin + distance, end); + auto stride_view_over_base_view_offset = std::ranges::stride_view(base_view_offset, 1); + + auto sv_bv_begin = stride_view_over_base_view.begin(); + auto sv_bv_offset_begin = stride_view_over_base_view_offset.begin(); + + auto sv_bv_begin_after_distance = sv_bv_begin += distance; + assert(*sv_bv_begin == *sv_bv_offset_begin); + assert(*sv_bv_begin_after_distance == *sv_bv_offset_begin); + + auto big_step = (end - 1) - begin; + auto stride_view_over_base_view_big_step = std::ranges::stride_view(base_view, big_step); + sv_bv_begin = stride_view_over_base_view_big_step.begin(); + + // This += should move us into a position where the __missing_ will come into play. + // Do a -= 1 here to confirm that the __missing_ is taken into account. + sv_bv_begin += 2; + sv_bv_begin -= 1; + assert(*sv_bv_begin == *(stride_view_over_base_view.begin() + big_step)); + return true; +} + +consteval bool do_static_tests() { + assert(test_random_access_operator_plus_equal()); + return true; +} + +int main(int, char**) { + static_assert(do_static_tests()); + assert(do_static_tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.verify.cpp new file mode 100644 index 0000000000000..64dec9460323f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/size.verify.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// expected-no-diagnostics + +// constexpr auto size() + +#include + +#include "test_iterators.h" +#include "types.h" + +// There is no size member function on a stride view over a view that +// is *not* a sized range +static_assert(!std::ranges::sized_range>>); +static_assert(!std::ranges::sized_range>>>); + +// There is a size member function on a stride view over a view that +// *is* a sized range +static_assert(std::ranges::sized_range, true>>); +static_assert(std::ranges::sized_range, true>>>); + +constexpr bool test() { + { + // Test with stride as exact multiple of number of elements in view strided over. + constexpr auto iota_twelve = std::views::iota(0, 12); + static_assert(std::ranges::sized_range); + constexpr auto stride_iota_twelve = std::views::stride(iota_twelve, 3); + static_assert(std::ranges::sized_range); + static_assert(4 == stride_iota_twelve.size(), "Striding by 3 through a 12 member list has size 4."); + } + + { + // Test with stride as inexact multiple of number of elements in view strided over. + constexpr auto iota_twenty_two = std::views::iota(0, 22); + static_assert(std::ranges::sized_range); + constexpr auto stride_iota_twenty_two = std::views::stride(iota_twenty_two, 3); + static_assert(std::ranges::sized_range); + static_assert(8 == stride_iota_twenty_two.size(), "Striding by 3 through a 22 member list has size 8."); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp new file mode 100644 index 0000000000000..b9879c1c473f8 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/stride.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr range_difference_t<_View> stride() const noexcept; + +#include + +#include "test_iterators.h" +#include "types.h" + +constexpr bool test() { + using View = BasicTestView>; + int arr[]{1, 2, 3}; + auto arrv(View(cpp17_input_iterator(arr), cpp17_input_iterator(arr + 3))); + // Mark str const so that we confirm that stride is a const member function. + const std::ranges::stride_view str(arrv, 1); + + // Make sure that stride member function is noexcept. + static_assert(noexcept(str.stride())); + // Make sure that the type returned by stride matches what is expected. + ASSERT_SAME_TYPE(std::ranges::range_difference_t, decltype(str.stride())); + // Make sure that we get back a stride equal to the one that we gave in the ctor. + assert(str.stride() == 1); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h new file mode 100644 index 0000000000000..5879c5022006e --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h @@ -0,0 +1,296 @@ +//===----------------------------------------------------------------------===// +// +// 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_STRIDE_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +// Concepts + +template +concept IterDifferable = std::invocable, Iter, Iter>; + +// Iterators + +// The base for an iterator that keeps a count of the times that it is +// moved and copied. +template +struct IterBase { + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + + int* move_counter = nullptr; + int* copy_counter = nullptr; + + Iter value_{}; + + constexpr IterBase() = default; + constexpr explicit IterBase(Iter value) : value_(value) {} + + constexpr IterBase(const IterBase& other) noexcept { + copy_counter = other.copy_counter; + move_counter = other.move_counter; + if (copy_counter != nullptr) { + (*copy_counter)++; + } + value_ = other.value_; + } + + constexpr IterBase(IterBase&& other) noexcept { + copy_counter = other.copy_counter; + move_counter = other.move_counter; + if (move_counter != nullptr) { + (*move_counter)++; + } + value_ = std::move(other.value_); + } + constexpr IterBase& operator=(const IterBase& other) = default; + constexpr IterBase& operator=(IterBase&& other) = default; +}; + +// The base for an input iterator that keeps a count of the times that it is +// moved and copied. +template + requires((!IsSized) || (IsSized && IterDifferable)) +struct InputIter : IterBase { + using Base = IterBase; + + using typename Base::difference_type; + using typename Base::value_type; + + using iterator_concept = std::input_iterator_tag; + using iterator_category = std::input_iterator_tag; + + using Base::Base; + + constexpr value_type operator*() const { return *Base::value_; } + constexpr Derived& operator++() { + Base::value_++; + return static_cast(*this); + } + constexpr Derived operator++(int) { + auto nv = *this; + Base::value_++; + return nv; + } + friend constexpr bool operator==(const Derived& left, const Derived& right) { return left.value_ == right.value_; } + friend constexpr difference_type operator-(const Derived& left, const Derived& right) + requires IsSized + { + return left.value_ - right.value_; + } +}; + +// In input iterator that is unsized. +struct UnsizedInputIter : InputIter { + using InputIter::InputIter; +}; +static_assert(std::input_iterator); +static_assert(!std::sized_sentinel_for); + +// In input iterator that is sized. +struct SizedInputIter : InputIter { + using InputIter::InputIter; +}; +static_assert(std::input_iterator); +static_assert(std::sized_sentinel_for); + +// Views + +// Put IterMoveIterSwapTestRangeIterator in a namespace to test ADL of CPOs iter_swap and iter_move +// (see iter_swap.pass.cpp and iter_move.pass.cpp). +namespace adl { +template +struct IterMoveIterSwapTestRangeIterator + : InputIter, Iter, false> { + int* counter_{nullptr}; + + using InputIter, Iter, false>:: + InputIter; + + constexpr IterMoveIterSwapTestRangeIterator(Iter value, int* counter) + : InputIter, Iter, false>(value), + counter_(counter) {} + + friend constexpr void iter_swap(IterMoveIterSwapTestRangeIterator t, IterMoveIterSwapTestRangeIterator u) noexcept + requires IsIterSwappable && IsNoExceptIterMoveable + { + (*t.counter_)++; + (*u.counter_)++; + std::swap(*t.value_, *u.value_); + } + + friend constexpr void iter_swap(IterMoveIterSwapTestRangeIterator t, IterMoveIterSwapTestRangeIterator u) + requires IsIterSwappable && (!IsNoExceptIterMoveable) + { + (*t.counter_)++; + (*u.counter_)++; + std::swap(*t.value_, *u.value_); + } + + friend constexpr auto iter_move(const IterMoveIterSwapTestRangeIterator& t) + requires(!IsNoExceptIterMoveable) + { + (*t.counter_)++; + return *t.value_; + } + friend constexpr auto iter_move(const IterMoveIterSwapTestRangeIterator& t) noexcept + requires IsNoExceptIterMoveable + { + (*t.counter_)++; + return *t.value_; + } +}; +} // namespace adl + +template +struct IterMoveIterSwapTestRange : std::ranges::view_base { + adl::IterMoveIterSwapTestRangeIterator begin_; + adl::IterMoveIterSwapTestRangeIterator end_; + constexpr IterMoveIterSwapTestRange(const Iter& begin, const Iter& end, int* counter) + : begin_(adl::IterMoveIterSwapTestRangeIterator(begin, counter)), + end_(adl::IterMoveIterSwapTestRangeIterator(end, counter)) {} + constexpr adl::IterMoveIterSwapTestRangeIterator begin() const { return begin_; } + constexpr adl::IterMoveIterSwapTestRangeIterator end() const { return end_; } +}; + +// Views + +// Depending upon configuration, ViewOrRange is either a View or not. +template +struct MaybeView {}; +template <> +struct MaybeView : std::ranges::view_base {}; + +template Sent = sentinel_wrapper, + bool IsSized = false, + bool IsView = false, + bool IsCopyable = false > + requires((!IsSized) || (IsSized && IterDifferable)) +struct BasicTestViewOrRange : MaybeView { + Iter begin_{}; + Iter end_{}; + + constexpr BasicTestViewOrRange(const Iter& b, const Iter& e) : begin_(b), end_(e) {} + + constexpr Iter begin() { return begin_; } + constexpr Iter begin() const { return begin_; } + constexpr Sent end() { return Sent{end_}; } + constexpr Sent end() const { return Sent{end_}; } + + constexpr auto size() const + requires IsSized + { + return begin_ - end_; + } + + constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other) = default; + constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default; + + constexpr BasicTestViewOrRange(const BasicTestViewOrRange&) + requires(!IsCopyable) + = delete; + constexpr BasicTestViewOrRange(const BasicTestViewOrRange&) + requires IsCopyable + = default; + + constexpr BasicTestViewOrRange& operator=(const BasicTestViewOrRange&) + requires(!IsCopyable) + = delete; + constexpr BasicTestViewOrRange& operator=(const BasicTestViewOrRange&) + requires IsCopyable + = default; +}; + +template Sent = sentinel_wrapper, bool IsSized = false> + requires((!IsSized) || (IsSized && IterDifferable)) +using BasicTestView = BasicTestViewOrRange; + +template Sent = sentinel_wrapper, bool IsCopyable = true> +using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange; + +static_assert(std::ranges::view>>); +static_assert(std::ranges::view, + sentinel_wrapper>, + false>>); + +static_assert(std::copyable>>); +template Sent = sentinel_wrapper> +using CopyableView = MaybeCopyableAlwaysMoveableView; +static_assert(std::copyable>>); + +template Sent = sentinel_wrapper> +using MoveOnlyView = MaybeCopyableAlwaysMoveableView; +static_assert(!std::copyable>>); + +template +struct MaybeConstCommonSimpleView : std::ranges::view_base { + int* begin(); + int* begin() const + requires(IsConst && IsSimple); + double* begin() const + requires(IsConst && !IsSimple); + + int* end() + requires(IsCommon); + void* end() + requires(!IsCommon); + + int* end() const + requires(IsConst && IsCommon && IsSimple); + double* end() const + requires(IsConst && IsCommon && !IsSimple); + + void* end() const + requires(IsConst && !IsCommon); + + size_t size() const + requires(IsSized); +}; + +using UnSimpleNoConstCommonView = MaybeConstCommonSimpleView; +using UnSimpleConstView = MaybeConstCommonSimpleView; +using UnsimpleUnCommonConstView = MaybeConstCommonSimpleView; +using SimpleUnCommonConstView = MaybeConstCommonSimpleView; +using SimpleCommonConstView = MaybeConstCommonSimpleView; +using SimpleNoConstSizedCommonView = MaybeConstCommonSimpleView; + +// Don't move/hold the iterator itself, copy/hold the base +// of that iterator and reconstruct the iterator on demand. +// May result in aliasing (if, e.g., Iterator is an iterator +// over int *). +template Sent = sentinel_wrapper> +struct ViewOverNonCopyableIterator : std::ranges::view_base { + constexpr explicit ViewOverNonCopyableIterator(Iter it, Sent sent) : it_(base(it)), sent_(base(sent)) {} + + ViewOverNonCopyableIterator(ViewOverNonCopyableIterator&&) = default; + ViewOverNonCopyableIterator& operator=(ViewOverNonCopyableIterator&&) = default; + + constexpr Iter begin() const { return Iter(it_); } + constexpr Sent end() const { return Sent(sent_); } + +private: + decltype(base(std::declval())) it_; + decltype(base(std::declval())) sent_; +}; + +// Ranges + +template Sent = sentinel_wrapper, bool IsSized = false> + requires((!IsSized) || (IsSized && IterDifferable)) +using BasicTestRange = BasicTestViewOrRange; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_STRIDE_TYPES_H diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index c1e579c775746..0fed19e88c93a 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1139,6 +1139,11 @@ def add_version_header(tc): "values": {"c++23": 202106}, "headers": ["algorithm"], }, + { + "name": "__cpp_lib_ranges_stride", + "values": {"c++23": 202207}, + "headers": ["ranges"], + }, { "name": "__cpp_lib_ranges_to_container", "values": {"c++23": 202202},