diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index a36848ebd24b4..979fe7d0737f3 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -486,7 +486,7 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_philox_engine`` *unimplemented* ---------------------------------------------------------- ----------------- - ``__cpp_lib_ranges_concat`` *unimplemented* + ``__cpp_lib_ranges_concat`` ``202403L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ratio`` ``202306L`` ---------------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index 91123ffa3e34b..b05aecc58a2d4 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -41,6 +41,7 @@ Implemented Papers - N4258: Cleaning-up noexcept in the Library (`Github `__) - P0767R1: Deprecate POD (`Github `__) - P1361R2: Integration of chrono with text formatting (`Github `__) +- P2542R8: ``views::concat`` (`Github `__) - P2255R2: A type trait to detect reference binding to temporary (implemented the type traits only) (`Github `__) - P2372R3: Fixing locale handling in chrono formatters (`Github `__) - P2562R1: ``constexpr`` Stable Sorting (`Github `__) @@ -56,6 +57,7 @@ Implemented Papers - P2944R3: Comparisons for ``reference_wrapper`` (`Github `__) - P3379R0: Constrain ``std::expected equality`` operators (`Github `__) + Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index 5460664369793..869cf79e25e48 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -68,8 +68,8 @@ "`LWG4071 `__","``reference_wrapper`` comparisons are not SFINAE-friendly","2024-06 (St. Louis)","|Complete|","19","" "`LWG4074 `__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","|Complete|","21","" "`LWG4076 `__","``concat_view`` should be freestanding","2024-06 (St. Louis)","","","" -"`LWG4079 `__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","","","" -"`LWG4082 `__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","","","" +"`LWG4079 `__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","|Complete|","21","" +"`LWG4082 `__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","|Complete|","21","" "`LWG4083 `__","``views::as_rvalue`` should reject non-input ranges","2024-06 (St. Louis)","","","" "`LWG4096 `__","``views::iota(views::iota(0))`` should be rejected","2024-06 (St. Louis)","","","" "`LWG4098 `__","``views::adjacent<0>`` should reject non-forward ranges","2024-06 (St. Louis)","","","" diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index e8b0c9559f40b..f86e7c5d37714 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -54,7 +54,7 @@ "`P3142R0 `__","Printing Blank Lines with ``println``","2024-03 (Tokyo)","|Complete|","19","Implemented as a DR against C++23. (MSVC STL and libstdc++ will do the same.)" "`P2845R8 `__","Formatting of ``std::filesystem::path``","2024-03 (Tokyo)","","","" "`P0493R5 `__","Atomic minimum/maximum","2024-03 (Tokyo)","","","" -"`P2542R8 `__","``views::concat``","2024-03 (Tokyo)","","","" +"`P2542R8 `__","``views::concat``","2024-03 (Tokyo)","|Complete|","21","" "`P2591R5 `__","Concatenation of strings and string views","2024-03 (Tokyo)","|Complete|","19","" "`P2248R8 `__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","" "`P2810R4 `__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index c6b87a34a43e9..eaf38d9860245 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -699,6 +699,7 @@ set(files __ranges/as_rvalue_view.h __ranges/chunk_by_view.h __ranges/common_view.h + __ranges/concat_view.h __ranges/concepts.h __ranges/container_compatible_range.h __ranges/counted.h diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h new file mode 100644 index 0000000000000..b5a32c619dc06 --- /dev/null +++ b/libcxx/include/__ranges/concat_view.h @@ -0,0 +1,653 @@ +// -*- 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_CONCAT_VIEW_H +#define _LIBCPP___RANGES_CONCAT_VIEW_H + +#include <__assert> +#include <__concepts/common_reference_with.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/copyable.h> +#include <__concepts/derived_from.h> +#include <__concepts/equality_comparable.h> +#include <__concepts/swappable.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/distance.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/next.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/movable_box.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__ranges/zip_view.h> +#include <__type_traits/conditional.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/make_unsigned.h> +#include <__type_traits/maybe_const.h> +#include <__utility/forward.h> +#include <__utility/in_place.h> +#include <__utility/move.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 26 + +namespace ranges { + +# ifdef __cpp_pack_indexing +template +using __extract_last _LIBCPP_NODEBUG = _Tp...[sizeof...(_Tp) - 1]; +# else +template +struct __extract_last_impl : __extract_last_impl<_Tail...> {}; +template +struct __extract_last_impl<_Tp> { + using type _LIBCPP_NODEBUG = _Tp; +}; + +template +using __extract_last _LIBCPP_NODEBUG = __extract_last_impl<_Tp...>::type; +# endif + +template +struct __all_but_first_model_sized_range; + +template +struct __all_but_first_model_sized_range<_Const, _Head, _Tail...> { + static constexpr bool value = (sized_range<__maybe_const<_Const, _Tail>> && ...); +}; + +template +concept __all_random_access = (random_access_range<__maybe_const<_Const, _Views>> && ...); + +template +concept __all_bidirectional = (bidirectional_range<__maybe_const<_Const, _Views>> && ...); + +template +concept __all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...); + +template +struct __all_common_ignore_last { + static constexpr bool value = + common_range<__maybe_const<_Const, _First>> && __all_common_ignore_last<_Const, _Tail...>::value; +}; + +template +struct __all_common_ignore_last<_Const, _Tail> { + static constexpr bool value = true; +}; + +template +concept __concat_is_random_access = + (__all_random_access<_Const, _Rs...>) && (__all_common_ignore_last<_Const, _Rs...>::value); + +template +concept __concat_is_bidirectional = + (__all_bidirectional<_Const, _Rs...>) && (__all_common_ignore_last<_Const, _Rs...>::value); + +template + requires((view<_Views> && ...) && (sizeof...(_Views) > 0) && __concatable<_Views...>) +class concat_view : public view_interface> { + tuple<_Views...> __views_; + + template + class __iterator; + +public: + _LIBCPP_HIDE_FROM_ABI constexpr concat_view() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit concat_view(_Views... __views) : __views_(std::move(__views)...) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() + requires(!(__simple_view<_Views> && ...)) + { + __iterator __it(this, in_place_index<0>, ranges::begin(std::get<0>(__views_))); + __it.template __satisfy<0>(); + return __it; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() const + requires((range && ...) && __concatable) + { + __iterator __it(this, in_place_index<0>, ranges::begin(std::get<0>(__views_))); + __it.template __satisfy<0>(); + return __it; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!(__simple_view<_Views> && ...)) + { + if constexpr (common_range<__maybe_const>>) { + constexpr auto __n = sizeof...(_Views); + return __iterator(this, in_place_index<__n - 1>, ranges::end(std::get<__n - 1>(__views_))); + } else { + return default_sentinel; + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires(range && ...) + { + if constexpr (common_range<__maybe_const>>) { + constexpr auto __n = sizeof...(_Views); + return __iterator(this, in_place_index<__n - 1>, ranges::end(std::get<__n - 1>(__views_))); + } else { + return default_sentinel; + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires(sized_range<_Views> && ...) + { + return std::apply( + [](auto... __sizes) { return (make_unsigned_t>(__sizes) + ...); }, + ranges::__tuple_transform(ranges::size, __views_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires(sized_range && ...) + { + return std::apply( + [](auto... __sizes) { return (make_unsigned_t>(__sizes) + ...); }, + ranges::__tuple_transform(ranges::size, __views_)); + } +}; + +template +concat_view(_Views&&...) -> concat_view...>; + +template +struct __concat_view_iterator_category {}; + +template + requires __all_forward<_Const, _Views...> +struct __concat_view_iterator_category<_Const, _Views...> { +private: + constexpr static bool __derive_pack_random_iterator = + (derived_from>>::iterator_category, + random_access_iterator_tag> && + ...); + constexpr static bool __derive_pack_bidirectional_iterator = + (derived_from>>::iterator_category, + bidirectional_iterator_tag> && + ...); + constexpr static bool __derive_pack_forward_iterator = + (derived_from>>::iterator_category, + forward_iterator_tag> && + ...); + +public: + using iterator_category = + _If...>>, + input_iterator_tag, + _If<__derive_pack_random_iterator, + random_access_iterator_tag, + _If<__derive_pack_bidirectional_iterator, + bidirectional_iterator_tag, + _If<__derive_pack_forward_iterator, forward_iterator_tag, input_iterator_tag > > > >; +}; + +template + requires((view<_Views> && ...) && (sizeof...(_Views) > 0) && __concatable<_Views...>) +template +class concat_view<_Views...>::__iterator : public __concat_view_iterator_category<_Const, _Views...> { +public: + using iterator_concept = + _If<__concat_is_random_access<_Const, _Views...>, + random_access_iterator_tag, + _If<__concat_is_bidirectional<_Const, _Views...>, + bidirectional_iterator_tag, + _If< __all_forward<_Const, _Views...>, forward_iterator_tag, input_iterator_tag > > >; + using value_type = __concat_value_t<__maybe_const<_Const, _Views>...>; + using difference_type = common_type_t>...>; + +private: + using __base_iter _LIBCPP_NODEBUG = variant>...>; + __base_iter __it_; + __maybe_const<_Const, concat_view>* __parent_ = nullptr; + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __satisfy() { + if constexpr (_Idx < (sizeof...(_Views) - 1)) { + if (std::get<_Idx>(__it_) == ranges::end(std::get<_Idx>(__parent_->__views_))) { + __it_.template emplace<_Idx + 1>(ranges::begin(std::get<_Idx + 1>(__parent_->__views_))); + __satisfy<_Idx + 1>(); + } + } + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __prev() { + if constexpr (_Idx == 0) { + --std::get<0>(__it_); + } else { + if (std::get<_Idx>(__it_) == ranges::begin(std::get<_Idx>(__parent_->__views_))) { + __it_.template emplace<_Idx - 1>(ranges::end(std::get<_Idx - 1>(__parent_->__views_))); + __prev<_Idx - 1>(); + } else { + --std::get<_Idx>(__it_); + } + } + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __advance_fwd(difference_type __offset, difference_type __steps) { + using __underlying_diff_type = iter_difference_t>; + if constexpr (_Idx == sizeof...(_Views) - 1) { + std::get<_Idx>(__it_) += static_cast<__underlying_diff_type>(__steps); + } else { + difference_type __n_size = ranges::size(std::get<_Idx>(__parent_->__views_)); + if (__offset + __steps < __n_size) { + std::get<_Idx>(__it_) += static_cast<__underlying_diff_type>(__steps); + } else { + __it_.template emplace<_Idx + 1>(ranges::begin(std::get<_Idx + 1>(__parent_->__views_))); + __advance_fwd<_Idx + 1>(0, __offset + __steps - __n_size); + } + } + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __advance_bwd(difference_type __offset, difference_type __steps) { + using __underlying_diff_type = iter_difference_t>; + if constexpr (_Idx == 0) { + std::get<_Idx>(__it_) -= static_cast<__underlying_diff_type>(__steps); + } else { + if (__offset >= __steps) { + std::get<_Idx>(__it_) -= static_cast<__underlying_diff_type>(__steps); + } else { + auto __prev_size = ranges::distance(std::get<_Idx - 1>(__parent_->__views_)); + __it_.template emplace<_Idx - 1>(ranges::end(std::get<_Idx - 1>(__parent_->__views_))); + __advance_bwd<_Idx - 1>(__prev_size, __steps - __offset); + } + } + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto __invoke_at_index(_Func&& __func) const { + return [&__func, this](this auto&& __self) { + if (_Is == __it_.index()) { + return __func.template operator()<_Is>(); + } else if constexpr (_Is + 1 < sizeof...(_Views)) { + return __self.template operator()<_Is + 1>(); + } + __builtin_unreachable(); + }.template operator()<0>(); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __apply_at_index(size_t __index, _Func&& __func, index_sequence<_Is...>) const { + ((__index == _Is ? (__func(integral_constant{}), 0) : 0), ...); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __apply_at_index(size_t __index, _Func&& __func) const { + __apply_at_index(__index, std::forward<_Func>(__func), make_index_sequence<_Idx>{}); + } + + template + _LIBCPP_HIDE_FROM_ABI explicit constexpr __iterator(__maybe_const<_Const, concat_view>* __parent, _Args&&... __args) + requires constructible_from<__base_iter, _Args&&...> + : __it_(std::forward<_Args>(__args)...), __parent_(__parent) {} + + friend class concat_view; + friend class __iterator; + +public: + _LIBCPP_HIDE_FROM_ABI __iterator() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && (convertible_to, iterator_t> && ...) + : __it_([&__src = __i.__it_](size_t __idx, index_sequence<_Indices...>) -> __base_iter { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__src.valueless_by_exception(), "Trying to convert from a valueless iterator of concat_view."); + using __src_lref = decltype((__src)); + using __construction_fptr = __base_iter (*)(__src_lref); + static constexpr __construction_fptr __vtable[]{[](__src_lref __src_var) -> __base_iter { + return __base_iter(in_place_index<_Indices>, std::__unchecked_get<_Indices>(std::move(__src_var))); + }...}; + return __vtable[__idx](__src); + }(__i.__it_.index(), make_index_sequence>{})), + __parent_(__i.__parent_) {} + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it_.valueless_by_exception(), "Trying to dereference a valueless iterator of concat_view."); + return __variant_detail::__visitation::__variant::__visit_value( + [](auto&& __it) -> __concat_reference_t<__maybe_const<_Const, _Views>...> { return *__it; }, __it_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it_.valueless_by_exception(), "Trying to increment a valueless iterator of concat_view."); + size_t __active_index = __it_.index(); + __apply_at_index>(__active_index, [&](auto __index_constant) { + constexpr size_t __i = __index_constant.value; + ++std::__unchecked_get<__i>(__it_); + __satisfy<__i>(); + }); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires(__all_forward<_Const, _Views> && ...) + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires __concat_is_bidirectional<_Const, _Views...> + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it_.valueless_by_exception(), "Trying to decrement a valueless iterator of concat_view."); + size_t __active_index = __it_.index(); + __apply_at_index>(__active_index, [&](auto __index_constant) { + constexpr size_t __i = __index_constant.value; + __prev<__i>(); + }); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires __concat_is_bidirectional<_Const, _Views...> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) + requires(equality_comparable>> && ...) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view."); + return __x.__it_ == __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const + requires __concat_is_random_access<_Const, _Views...> + { + return *((*this) + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __it, difference_type __n) + requires __concat_is_random_access<_Const, _Views...> + { + auto __temp = __it; + __temp += __n; + return __temp; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __it) + requires __concat_is_random_access<_Const, _Views...> + { + return __it + __n; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n) + requires __concat_is_random_access<_Const, _Views...> + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it_.valueless_by_exception(), "Trying to increment a valueless iterator of concat_view."); + size_t __active_index = __it_.index(); + if (__n > 0) { + __variant_detail::__visitation::__variant::__visit_value( + [&](auto& __active_it) { + __apply_at_index__views_)>>(__active_index, [&](auto __index_constant) { + constexpr size_t __i = __index_constant.value; + auto& __active_view = std::get<__i>(__parent_->__views_); + difference_type __idx = __active_it - ranges::begin(__active_view); + __advance_fwd<__i>(__idx, __n); + }); + }, + __it_); + } + + else if (__n < 0) { + __variant_detail::__visitation::__variant::__visit_value( + [&](auto& __active_it) { + __apply_at_index__views_)>>(__active_index, [&](auto __index_constant) { + constexpr size_t __i = __index_constant.value; + auto& __active_view = std::get<__i>(__parent_->__views_); + difference_type __idx = __active_it - ranges::begin(__active_view); + __advance_bwd<__i>(__idx, -__n); + }); + }, + __it_); + } + + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n) + requires __concat_is_random_access<_Const, _Views...> + { + *this += -__n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __it, default_sentinel_t) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view with the default sentinel."); + constexpr auto __last_idx = sizeof...(_Views) - 1; + return __it.__it_.index() == __last_idx && + std::__unchecked_get<__last_idx>(__it.__it_) == ranges::end(std::get<__last_idx>(__it.__parent_->__views_)); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires(__all_random_access<_Const, _Views> && ...) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view."); + return __x.__it_ < __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires(__all_random_access<_Const, _Views> && ...) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view."); + return __x.__it_ > __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires(__all_random_access<_Const, _Views> && ...) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view."); + return __x.__it_ <= __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires(__all_random_access<_Const, _Views> && ...) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view."); + return __x.__it_ >= __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires((random_access_range<__maybe_const<_Const, _Views>> && ...) && + (three_way_comparable<__maybe_const<_Const, _Views>> && ...)) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator of concat_view."); + return __x.__it_ <=> __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto) iter_move(const __iterator& __it) noexcept( + + ((is_nothrow_invocable_v< decltype(ranges::iter_move), const iterator_t<__maybe_const<_Const, _Views>>& > && + is_nothrow_convertible_v< range_rvalue_reference_t<__maybe_const<_Const, _Views>>, + __concat_rvalue_reference_t<__maybe_const<_Const, _Views>...> >) && + ...)) + + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it.__it_.valueless_by_exception(), "Trying to apply iter_move to a valueless iterator of concat_view."); + return __variant_detail::__visitation::__variant::__visit_value( + [](const auto& __i) -> __concat_rvalue_reference_t<__maybe_const<_Const, _Views>...> { + return ranges::iter_move(__i); + }, + __it.__it_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y) + + noexcept((noexcept(ranges::swap(*__x, *__y))) && + (noexcept(ranges::iter_swap(std::declval>>(), + std::declval>>())) && + ...)) + + requires swappable_with, iter_reference_t<__iterator>> && + (... && indirectly_swappable>>) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to swap iterators of concat_view where at least one iterator is valueless."); + __variant_detail::__visitation::__variant::__visit_value( + [&](const auto& __it1, const auto& __it2) { + if constexpr (is_same_v) { + ranges::iter_swap(__it1, __it2); + } else { + ranges::swap(*__x, *__y); + } + }, + __x.__it_, + __y.__it_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires __concat_is_random_access<_Const, _Views...> + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to subtract two iterators of concat_view where at least one iterator is valueless."); + return __x.__invoke_at_index([&]() -> difference_type { + return __y.__invoke_at_index([&]() -> difference_type { + if (__index_x > __index_y) { + auto __dx = ranges::distance( + ranges::begin(std::get<__index_x>(__x.__parent_->__views_)), std::get<__index_x>(__x.__it_)); + auto __dy = ranges::distance( + std::get<__index_y>(__y.__it_), ranges::end(std::get<__index_y>(__y.__parent_->__views_))); + difference_type __s = [&](this auto&& __self) -> difference_type { + if constexpr (__start < __end) { + return ranges::size(std::get<__start>(__x.__parent_->__views_)) + + __self.template operator()<__start + 1, __end>(); + } + return 0; + }.template operator()<__index_y + 1, __index_x>(); + return __dy + __s + __dx; + } else if (__index_x < __index_y) { + return -(__y - __x); + } else { + return std::get<__index_x>(__x.__it_) - std::get<__index_y>(__y.__it_); + } + }); + }); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __it, difference_type __n) + requires __concat_is_random_access<_Const, _Views...> + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__it.__it_.valueless_by_exception(), "Trying to subtract a valuess iterators of concat_view."); + auto __temp = __it; + __temp -= __n; + return __temp; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, default_sentinel_t) + requires(sized_sentinel_for>, iterator_t<__maybe_const<_Const, _Views>>> && + ...) && + (__all_but_first_model_sized_range<_Const, _Views...>::value) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS( + !__x.__it_.valueless_by_exception(), + "Trying to subtract a valuess iterators of concat_view from the default sentinel."); + return __x.__invoke_at_index([&]() -> difference_type { + auto __dx = + ranges::distance(std::get<__index_x>(__x.__it_), ranges::end(std::get<__index_x>(__x.__parent_->__views_))); + difference_type __s = [&](this auto&& __self) -> difference_type { + if constexpr (__start < __end) { + return ranges::size(std::get<__start>(__x.__parent_->__views_)) + + __self.template operator()<__start + 1, __end>(); + } + return 0; + }.template operator()<__index_x + 1, sizeof...(_Views)>(); + return -(__dx + __s); + }); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(default_sentinel_t, const __iterator& __x) + requires(sized_sentinel_for>, iterator_t<__maybe_const<_Const, _Views>>> && + ...) && + (__all_but_first_model_sized_range<_Const, _Views...>::value) + { + return -(__x - default_sentinel); + } +}; + +namespace views { +namespace __concat { +struct __fn { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto + operator()(_Range&& __range) noexcept(noexcept(views::all((std::forward<_Range>(__range))))) + -> decltype(views::all((std::forward<_Range>(__range)))) { + return views::all(std::forward<_Range>(__range)); + } + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto + operator()(_FirstRange&& __first, _TailRanges&&... __tail) noexcept( + noexcept(concat_view(std::forward<_FirstRange>(__first), std::forward<_TailRanges>(__tail)...))) + -> decltype(concat_view(std::forward<_FirstRange>(__first), std::forward<_TailRanges>(__tail)...)) { + return concat_view(std::forward<_FirstRange>(__first), std::forward<_TailRanges>(__tail)...); + } +}; +} // namespace __concat + +inline namespace __cpo { +inline constexpr auto concat = __concat::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 26 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_CONCAT_VIEW_H diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h index bf75fe8a6fef4..fefec1634f66e 100644 --- a/libcxx/include/__ranges/concepts.h +++ b/libcxx/include/__ranges/concepts.h @@ -30,6 +30,7 @@ #include <__type_traits/common_reference.h> #include <__type_traits/common_type.h> #include <__type_traits/is_reference.h> +#include <__type_traits/maybe_const.h> #include <__type_traits/remove_cvref.h> #include <__type_traits/remove_reference.h> #include <__utility/declval.h> diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index c431c0cb407f3..48ba8d72b1cf0 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1856,6 +1856,7 @@ module std [system] { export std.functional.bind_back } module common_view { header "__ranges/common_view.h" } + module concat_view { header "__ranges/concat_view.h" } module concepts { header "__ranges/concepts.h" } module container_compatible_range { header "__ranges/container_compatible_range.h" } module counted { diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 96d7a6b897188..1ca628662bdd9 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -197,6 +197,16 @@ namespace std::ranges { inline constexpr unspecified filter = unspecified; } + // [range.concat], concat view + template + requires (view && ...) && (sizeof...(Views) > 0) && + concatable + class concat_view; + + namespace views { + inline constexpr unspecified concat = unspecified; + } + // [range.drop], drop view template class drop_view; @@ -453,6 +463,10 @@ namespace std { # include <__ranges/zip_view.h> # endif +# if _LIBCPP_STD_VER >= 26 +# include <__ranges/concat_view.h> +# endif + # include // standard-mandated includes diff --git a/libcxx/include/version b/libcxx/include/version index aae9277a7dfc6..72e2709900b8a 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -589,7 +589,7 @@ __cpp_lib_void_t 201411L # undef __cpp_lib_out_ptr # define __cpp_lib_out_ptr 202311L // # define __cpp_lib_philox_engine 202406L -// # define __cpp_lib_ranges_concat 202403L +# define __cpp_lib_ranges_concat 202403L # define __cpp_lib_ratio 202306L // # define __cpp_lib_rcu 202306L # define __cpp_lib_reference_wrapper 202403L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 7ede42e4f7b0a..3accaeb3a881e 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -176,6 +176,15 @@ export namespace std { } // namespace views #endif // _LIBCPP_STD_VER >= 23 +#if _LIBCPP_STD_VER >= 26 + // [range.concat.view], concat_view + using std::ranges::concat_view; + + namespace views { + using std::ranges::views::concat; + } // namespace views +#endif // _LIBCPP_STD_VER >= 26 + // [range.filter], filter view using std::ranges::filter_view; diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp new file mode 100644 index 0000000000000..0646e242f1457 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp @@ -0,0 +1,384 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: has-unix-headers +// REQUIRES: std-at-least-c++26 +// UNSUPPORTED: no-exceptions, libcpp-hardening-mode=none +// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing + +#include +#include +#include +#include + +#include "check_assertion.h" +#include "double_move_tracker.h" +#include "test_iterators.h" + +int val[] = {1, 2, 3}; + +bool flag = false; + +template +struct Iter; + +template +struct Iter { + using value_type = int; + using difference_type = std::ptrdiff_t; + using reference = int&; + using pointer = int*; + using iterator_category = std::random_access_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + +private: + int* ptr_ = nullptr; + + template + friend struct Iter; + +public: + Iter() = default; + Iter(int* ptr) : ptr_(ptr) {} + Iter(const Iter&) = default; + Iter(Iter&& other) noexcept : ptr_(other.ptr_) {} + + template + Iter(const Iter& other) : ptr_(other.ptr_) {} + + Iter& operator=(const Iter&) = default; + Iter& operator=(Iter&& other) noexcept { + ptr_ = other.ptr_; + return *this; + } + + reference operator*() const { return *ptr_; } + pointer operator->() const { return ptr_; } + reference operator[](difference_type n) const { return ptr_[n]; } + + Iter& operator++() { + ++ptr_; + return *this; + } + Iter operator++(int) { + auto tmp = *this; + ++*this; + return tmp; + } + Iter& operator--() { + --ptr_; + return *this; + } + Iter operator--(int) { + auto tmp = *this; + --*this; + return tmp; + } + + Iter& operator+=(difference_type n) { + ptr_ += n; + return *this; + } + Iter& operator-=(difference_type n) { + ptr_ -= n; + return *this; + } + + template + friend Iter operator+(Iter it, difference_type n); + + template + friend Iter operator+(difference_type n, Iter it); + + template + friend Iter operator-(Iter it, difference_type n); + + template + friend difference_type operator-(Iter a, Iter b); + + friend bool operator==(Iter a, Iter b) { return a.ptr_ == b.ptr_; }; + friend bool operator<(Iter a, Iter b) { return a.ptr_ < b.ptr_; } + friend bool operator>(Iter a, Iter b) { return a.ptr_ > b.ptr_; } + friend bool operator<=(Iter a, Iter b) { return a.ptr_ <= b.ptr_; } + friend bool operator>=(Iter a, Iter b) { return a.ptr_ >= b.ptr_; } + friend auto operator<=>(Iter a, Iter b) { return a.ptr_ <=> b.ptr_; } +}; + +template +inline Iter operator+(Iter it, std::ptrdiff_t n) { + return Iter(it.ptr_ + n); +} + +template +inline Iter operator+(std::ptrdiff_t n, Iter it) { + return Iter(it.ptr_ + n); +} + +template +inline Iter operator-(Iter it, std::ptrdiff_t n) { + return Iter(it.ptr_ - n); +} + +template +inline std::ptrdiff_t operator-(Iter a, Iter b) { + return a.ptr_ - b.ptr_; +} + +template +struct Range : std::ranges::view_base { + using iterator = Iter; + using const_iterator = Iter; + using sentinel = sentinel_wrapper; + + int* data_; + std::size_t size_; + + Range() : data_(val), size_(4) {} + + Range(int* data, std::size_t size) : data_(data), size_(size) {} + + iterator begin() { return iterator(data_); } + iterator end() { return iterator(data_ + size_); } + + const_iterator begin() const { return const_iterator(data_); } + const_iterator end() const { return const_iterator(data_ + size_); } + + std::size_t size() const { return size_; } +}; + +static_assert(std::ranges::range>); +static_assert(std::ranges::sized_range>); + +int main() { + { + // valueless by exception test operator* + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + auto f = std::ranges::distance(r1); + (void)f; + TEST_LIBCPP_ASSERT_FAILURE([=] { *iter1; }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator== + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + auto iter3 = cv.begin(); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([=] { (void)(iter1 == iter3); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator== with a sentinel + flag = false; + Range<0> r1; + Range<1> r2; + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([=] { (void)(iter1 == std::default_sentinel); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator-- + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { --iter1; }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator++ + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { ++iter1; }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator+= + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { iter1 += 1; }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator> + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(iter1 > iter2); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator>= + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(iter1 >= iter2); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator< + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(iter1 < iter2); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator<= + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(iter1 <= iter2); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator- between two iterators + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(iter1 - iter2); }(), "valueless by exception"); + } + } + + { + // valueless by exception test operator- with a constant + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(iter1 - 1); }(), "valueless by exception"); + } + } + + { + // valueless by exception test constructor + flag = false; + Range<0> r1; + Range<1> r2; + + auto cv = std::views::concat(r1, r2); + auto iter1 = cv.begin(); + auto iter2 = std::ranges::next(cv.begin(), 4); + flag = true; + try { + iter1 = std::move(iter2); + } catch (...) { + TEST_LIBCPP_ASSERT_FAILURE( + [&] { + std::ranges::iterator_t it3(iter1); + (void)it3; + }(), + "valueless by exception"); + } + } +} 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..2bdb50a5f182d 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 @@ -387,17 +387,11 @@ # error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_concat -# error "__cpp_lib_ranges_concat should be defined in c++26" -# endif -# if __cpp_lib_ranges_concat != 202403L -# error "__cpp_lib_ranges_concat should have the value 202403L in c++26" -# endif -# else -# ifdef __cpp_lib_ranges_concat -# error "__cpp_lib_ranges_concat should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_concat +# error "__cpp_lib_ranges_concat should be defined in c++26" +# endif +# if __cpp_lib_ranges_concat != 202403L +# error "__cpp_lib_ranges_concat should have the value 202403L in c++26" # endif # ifndef __cpp_lib_ranges_join_with 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 7bd8e8979e6f3..9c4036556eb94 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 @@ -7569,17 +7569,11 @@ # error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_concat -# error "__cpp_lib_ranges_concat should be defined in c++26" -# endif -# if __cpp_lib_ranges_concat != 202403L -# error "__cpp_lib_ranges_concat should have the value 202403L in c++26" -# endif -# else -# ifdef __cpp_lib_ranges_concat -# error "__cpp_lib_ranges_concat should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_concat +# error "__cpp_lib_ranges_concat should be defined in c++26" +# endif +# if __cpp_lib_ranges_concat != 202403L +# error "__cpp_lib_ranges_concat should have the value 202403L in c++26" # endif # ifndef __cpp_lib_ranges_contains diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp new file mode 100644 index 0000000000000..d61e4db898ecf --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +struct Range : std::ranges::view_base { + using Iterator = forward_iterator; + using Sentinel = sentinel_wrapper; + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Sentinel end() const { return Sentinel(Iterator(end_)); } + +private: + int* begin_; + int* end_; +}; + +template +constexpr void compareViews(View v, std::initializer_list list) { + auto b1 = v.begin(); + auto e1 = v.end(); + auto b2 = list.begin(); + auto e2 = list.end(); + for (; b1 != e1 && b2 != e2;) { + assert(*b1 == *b2); + ++b1; + ++b2; + } + assert(b1 == e1); + assert(b2 == e2); +} + +constexpr bool test() { + int arr[] = {0, 1, 2, 3}; + int arr2[] = {4, 5, 6, 7}; + + { + Range range(arr, arr + 4); + + { + decltype(auto) result = std::views::concat(range); + compareViews(result, {0, 1, 2, 3}); + ASSERT_SAME_TYPE(decltype(std::views::all(range)), decltype(result)); + } + } + + { + Range first(arr, arr + 4); + Range tail(arr2, arr2 + 4); + + { + decltype(auto) result = std::views::concat(first, tail); + compareViews(result, {0, 1, 2, 3, 4, 5, 6, 7}); + using Type = std::ranges::concat_view; + ASSERT_SAME_TYPE(Type, decltype(result)); + } + } + + return true; +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp new file mode 100644 index 0000000000000..ce0f247dd3740 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include + +#include +#include "test_iterators.h" +#include "types.h" + +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = HasConstBegin && requires(T& t, const T& ct) { + requires !std::same_as; +}; + +template +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +constexpr void tests() { + // check the case of simple view + { + int buffer[4] = {1, 2, 3, 4}; + std::ranges::concat_view v(SimpleCommon{buffer}, SimpleCommon{buffer}); + static_assert(std::is_same_v); + assert(v.begin() == std::as_const(v).begin()); + + using View = decltype(v); + static_assert(HasOnlyConstBegin); + static_assert(!HasOnlyNonConstBegin); + static_assert(!HasConstAndNonConstBegin); + } + + // not all underlying ranges model simple view + { + int buffer[4] = {1, 2, 3, 4}; + std::ranges::concat_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer}); + static_assert(!std::is_same_v); + assert(v.begin() == std::as_const(v).begin()); + + using View = decltype(v); + static_assert(!HasOnlyConstBegin); + static_assert(!HasOnlyNonConstBegin); + static_assert(HasConstAndNonConstBegin); + } + + // first view is empty + { + std::vector v1; + std::vector v2 = {1, 2, 3, 4}; + std::ranges::concat_view view(v1, v2); + auto it = view.begin(); + assert(*it == 1); + assert(it + 4 == view.end()); + } + + // first few views is empty + { + std::vector v1; + std::vector v2; + std::vector v3 = {1, 2, 3, 4}; + std::ranges::concat_view view(v1, v2, v3); + auto it = view.begin(); + assert(*it == 1); + assert(it + 4 == view.end()); + } +} + +constexpr bool test() { + tests(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/constraints.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/constraints.verify.cpp new file mode 100644 index 0000000000000..b848b654916d0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/constraints.verify.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include +#include +#include "test_macros.h" + +int main(int, char**) { + std::vector v{1, 2, 3}; + auto r = std::views::counted(std::back_inserter(v), 3); + auto c = std::views::concat(r); + // expected-error@*:* {{}} + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp new file mode 100644 index 0000000000000..b9e7e8e3f1bb1 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include "test_iterators.h" + +struct View : std::ranges::view_base { + View() = default; + forward_iterator begin() const; + sentinel_wrapper> end() const; +}; +static_assert(std::ranges::view); + +// A range that is not a view +struct Range { + Range() = default; + forward_iterator begin() const; + sentinel_wrapper> end() const; +}; +static_assert(std::ranges::range && !std::ranges::view); + +constexpr bool test() { + { + View v; + std::ranges::concat_view view(v); + static_assert(std::is_same_v>); + } + + // Test with a range that isn't a view, to make sure we properly use views::all_t in the implementation. + { + Range r; + std::ranges::concat_view view(r); + static_assert(std::is_same_v>>); + } + + // Test a view which has a range and a view + { + Range r; + View v; + std::ranges::concat_view view(r, v); + static_assert(std::is_same_v, View>>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.pass.cpp new file mode 100644 index 0000000000000..cdaba3e914dc5 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#include "check_assertion.h" + +constexpr int buff[] = {1, 2, 3, 4}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 4) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +struct NoDefaultView : std::ranges::view_base { + NoDefaultView() = delete; + int* begin() const; + int* end() const; +}; + +struct NoexceptView : std::ranges::view_base { + NoexceptView() noexcept; + int const* begin() const; + int const* end() const; +}; + +struct HelperView : std::ranges::view_base { + constexpr HelperView(const int* begin, const int* end) : begin_(begin), end_(end) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + +private: + int const* begin_; + int const* end_; +}; + +constexpr void test_with_one_view() { + { + using View = std::ranges::concat_view; + View view; + auto it = view.begin(); + auto end = view.end(); + assert(*it++ == 1); + assert(*it++ == 2); + assert(*it++ == 3); + assert(*it++ == 4); + assert(it == end); + } +} + +constexpr void test_with_more_than_one_view() { + { + using View = std::ranges::concat_view; + int arr1[] = {1, 2}; + int arr2[] = {3, 4}; + HelperView range1(arr1, arr1 + 2); + HelperView range2(arr2, arr2 + 2); + View view(range1, range2); + auto it = view.begin(); + auto end = view.end(); + assert(*it++ == 1); + assert(*it++ == 2); + assert(*it++ == 3); + assert(*it++ == 4); + assert(it == end); + } +} + +constexpr bool tests() { + test_with_one_view(); + test_with_more_than_one_view(); + + // Check cases where the default constructor isn't provided + { + static_assert(!std::is_default_constructible_v>); + } + + // Check noexcept-ness + { + { + using View = std::ranges::concat_view; + static_assert(!noexcept(View())); + } + { + using View = std::ranges::concat_view; + static_assert(noexcept(View())); + } + } + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.verify.cpp new file mode 100644 index 0000000000000..b540a65ed701a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.verify.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include +#include +#include +#include "test_macros.h" + +struct NoSizeRange : std::ranges::view_base { + constexpr auto size() { return 0; } +}; + +int main(int, char**) { + { + // LWG 4082 + std::vector v{1, 2, 3}; + auto r = std::views::counted(std::back_inserter(v), 3); + auto c = std::views::concat(r); + // expected-error@*:* {{}} + } + + { + // input is not a view + int x = 1; + auto c = std::views::concat(x); + // expected-error@*:* {{}} + } + + { + // input is a view but has 0 size + auto c = std::views::concat(NoSizeRange{}); + // expected-error@*:* {{}} + } + + { + // inputs are non-concatable + std::vector v1{1, 2}; + std::vector v2{"Hello", "World"}; + auto c = std::views::concat(v1, v2); + // expected-error@*:* {{}} + } + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.view.pass.cpp new file mode 100644 index 0000000000000..90d290fc68f3b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.view.pass.cpp @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include + +#include "test_macros.h" +#include "types.h" + +struct Range : std::ranges::view_base { + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + +private: + int* begin_; + int* end_; +}; + +struct MoveAwareView : std::ranges::view_base { + int moves = 0; + constexpr MoveAwareView() = default; + constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; } + constexpr MoveAwareView& operator=(MoveAwareView&& other) { + moves = other.moves + 1; + other.moves = 0; + return *this; + } + constexpr const int* begin() const { return &moves; } + constexpr const int* end() const { return &moves + 1; } +}; + +constexpr bool test() { + int buff[] = {1, 2}; + int buff2[] = {3, 4}; + + // constructor from views + { + Range range(buff, buff + 2); + Range range2(buff2, buff2 + 2); + std::ranges::concat_view view(range, range2); + auto it = view.begin(); + auto end = view.end(); + assert(*it++ == 1); + assert(*it++ == 2); + assert(*it++ == 3); + assert(*it++ == 4); + assert(it == end); + } + + // Make sure we move the view + { + MoveAwareView mv; + std::ranges::concat_view v{std::move(mv), MoveAwareView{}}; + auto it = v.begin(); + assert(*it++ == 2); // one move from the local variable to parameter, one move from parameter to member + assert(*it++ == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp new file mode 100644 index 0000000000000..eff0f99d76b37 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include "test_iterators.h" +#include "types.h" + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[2] = {6, 7}; + + { + std::ranges::concat_view v(SimpleCommon{buffer1}, SimpleCommon{buffer2}); + static_assert(std::is_same_v); + static_assert(std::ranges::common_range); + assert(v.begin() + 7 == v.end()); + static_assert(std::is_same_v); + } + + { + std::ranges::concat_view v(SimpleCommon{buffer1}, SimpleNonCommon{buffer2}); + assert(v.begin() + 7 == v.end()); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + std::ranges::concat_view v(SimpleCommon{buffer1}, NonSimpleCommon{buffer2}); + assert(v.begin() + 7 == v.end()); + static_assert(!std::is_same_v); + } + + { + std::ranges::concat_view v(SimpleCommon{buffer1}, NonSimpleNonCommon{buffer2}); + assert(v.begin() + 7 == v.end()); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.pass.cpp new file mode 100644 index 0000000000000..2fd5e801ea570 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/arithmetic.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include + +#include "../../range_adaptor_types.h" + +template +concept canPlusEqual = requires(T& t, U& u) { t += u; }; + +template +concept canMinusEqual = requires(T& t, U& u) { t -= u; }; + +constexpr bool test() { + int buffer1[5] = {1, 2, 3, 4, 5}; + int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + SimpleCommonRandomAccessSized a{buffer1}; + SimpleCommonRandomAccessSized b{buffer2}; + + { + // operator+(x, n) and operator+= + std::ranges::concat_view v(a, b); + auto it1 = v.begin(); + + auto it2 = it1 + 3; + auto x2 = *it2; + assert(x2 == buffer1[3]); + + auto it3 = 3 + it1; + auto x3 = *it3; + assert(x3 == buffer1[3]); + + it1 += 3; + assert(it1 == it2); + auto x1 = *it2; + assert(x1 == buffer1[3]); + + using Iter = decltype(it1); + static_assert(canPlusEqual); + } + + { + // operator-(x, n) and operator-= + std::ranges::concat_view v(a, b); + auto it1 = v.end(); + + auto it2 = it1 - 3; + auto x2 = *it2; + assert(x2 == buffer2[6]); + + it1 -= 3; + assert(it1 == it2); + auto x1 = *it2; + assert(x1 == buffer2[6]); + + using Iter = decltype(it1); + static_assert(canMinusEqual); + } + + { + // operator-(x, y) + std::ranges::concat_view v(a, b); + assert((v.end() - v.begin()) == 14); + + auto it1 = v.begin() + 2; + auto it2 = v.end() - 1; + assert((it1 - it2) == -11); + } + + { + // One of the ranges is not random access + std::ranges::concat_view v(a, b, ForwardSizedView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!std::invocable, Iter, std::intptr_t>); + static_assert(!std::invocable, std::intptr_t, Iter>); + static_assert(!canPlusEqual); + static_assert(!std::invocable, Iter, std::intptr_t>); + static_assert(!std::invocable, Iter, Iter>); + static_assert(!canMinusEqual); + } + + { + // One of the ranges does not have sized sentinel + std::ranges::concat_view v(a, b, InputCommonView{buffer1}); + using Iter = decltype(v.begin()); + static_assert(!std::invocable, Iter, Iter>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp new file mode 100644 index 0000000000000..a68749d938dc9 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include "test_iterators.h" +#include "test_range.h" + +#include "../types.h" + +template +constexpr void test() { + using Sentinel = sentinel_wrapper; + using View = minimal_view; + using ConcatView = std::ranges::concat_view; + + auto make_concat_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + return ConcatView(std::move(view)); + }; + + { + // test with one view + std::array array{0, 1, 2, 3, 4}; + ConcatView view = make_concat_view(array.data(), array.data() + array.size()); + decltype(auto) it1 = view.begin(); + decltype(auto) it2 = view.begin(); + std::same_as decltype(auto) result = (it1 == it2); + assert(result); + + ++it1; + assert(!(it1 == it2)); + } + + { + // test with more than one view + constexpr static std::array array1{0, 1, 2}; + constexpr static std::array array2{0, 1, 2}; + constexpr static std::ranges::concat_view view(std::views::all(array1), std::views::all(array2)); + decltype(auto) it1 = view.begin(); + decltype(auto) it2 = view.begin(); + std::same_as decltype(auto) result = (it1 == it2); + assert(result); + + ++it2; + ++it2; + assert(!(it1 == it2)); + ++it2; + assert(*it1 == *it2); + } + + { + std::array array{0, 1, 2, 3, 4}; + ConcatView view = make_concat_view(array.data(), array.data() + array.size()); + assert(!(view.begin() == view.end())); + } +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp new file mode 100644 index 0000000000000..3be6282c8a368 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.other.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: has-unix-headers, std-at-least-c++26 + +#include +#include + +#include "../../range_adaptor_types.h" + +using ConstIterIncompatibleView = + BasicView, + forward_iterator, + random_access_iterator, + random_access_iterator>; +static_assert(!std::convertible_to, + std::ranges::iterator_t>); + +constexpr bool test() { + int buffer[3] = {1, 2, 3}; + + { + std::ranges::concat_view v(NonSimpleCommon{buffer}); + auto iter1 = v.begin(); + std::ranges::iterator_t iter2 = iter1; + assert(iter1 == iter2); + + static_assert(!std::is_same_v); + + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + } + + { + // underlying non-const to const not convertible + std::ranges::concat_view v(ConstIterIncompatibleView{buffer}); + auto iter1 = v.begin(); + auto iter2 = std::as_const(v).begin(); + + static_assert(!std::is_same_v); + static_assert(!std::constructible_from); + static_assert(!std::constructible_from); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.pass.cpp new file mode 100644 index 0000000000000..3a1dee11e1e8f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: has-unix-headers, std-at-least-c++26 + +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "../types.h" + +int globalBuff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + +struct MoveOnlyView : std::ranges::view_base { + int start_; + int* ptr_; + constexpr explicit MoveOnlyView(int* ptr = globalBuff, int start = 0) : start_(start), ptr_(ptr) {} + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr int* begin() const { return ptr_ + start_; } + constexpr int* end() const { return ptr_ + 8; } +}; + +struct NoDefaultInit { + typedef std::random_access_iterator_tag iterator_category; + typedef int value_type; + typedef std::ptrdiff_t difference_type; + typedef int* pointer; + typedef int& reference; + typedef NoDefaultInit self; + + NoDefaultInit(int*); + + reference operator*() const; + pointer operator->() const; + auto operator<=>(const self&) const = default; + bool operator==(int*) const; + + self& operator++(); + self operator++(int); + + self& operator--(); + self operator--(int); + + self& operator+=(difference_type n); + self operator+(difference_type n) const; + friend self operator+(difference_type n, self x); + + self& operator-=(difference_type n); + self operator-(difference_type n) const; + difference_type operator-(const self&) const; + + reference operator[](difference_type n) const; +}; + +struct IterNoDefaultInitView : std::ranges::view_base { + NoDefaultInit begin() const; + int* end() const; + NoDefaultInit begin(); + int* end(); +}; + +constexpr bool test() { + std::ranges::concat_view concatView; + auto iter = std::move(concatView).begin(); + std::ranges::iterator_t> i2(iter); + (void)i2; + std::ranges::iterator_t> constIter(iter); + (void)constIter; + + static_assert(std::default_initializable>>); + static_assert(!std::default_initializable>>); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp new file mode 100644 index 0000000000000..4f6b3bb580188 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include "test_macros.h" +#include "../../range_adaptor_types.h" + +template +concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; }; + +struct NonBidi : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = forward_iterator; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(iterator(buffer_ + size_)); } +}; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{5, 6, 7, 8}; + + // Test with a single view + { + std::ranges::concat_view view(a); + auto it = std::ranges::next(view.begin(), view.end()); + assert(it == view.end()); + + auto& result = --it; + ASSERT_SAME_TYPE(decltype(result)&, decltype(--it)); + assert(&result == &it); + assert(result == view.begin() + 3); + } + + // Test with more than one view + { + std::ranges::concat_view view(a, b); + auto it = std::ranges::next(view.begin(), view.end()); + assert(it == view.end()); + + auto& result = --it; + assert(&result == &it); + + --it; + assert(*it == 7); + assert(it == view.begin() + 6); + } + + // Test going forward and then backward on the same iterator + { + std::ranges::concat_view view(a, b); + auto it = view.begin(); + ++it; + --it; + assert(*it == a[0]); + ++it; + ++it; + --it; + assert(*it == a[1]); + ++it; + ++it; + --it; + assert(*it == a[2]); + ++it; + ++it; + --it; + assert(*it == a[3]); + } + + // Test post-decrement + { + std::ranges::concat_view view(a, b); + auto it = std::ranges::next(view.begin(), view.end()); + assert(it == view.end()); // test the test + auto result = it--; + ASSERT_SAME_TYPE(decltype(result), decltype(it--)); + assert(result == view.end()); + assert(it == (result - 1)); + } + + // bidirectional + { + int buffer[2] = {1, 2}; + + std::ranges::concat_view v(BidiCommonView{buffer}, std::views::iota(0, 5)); + auto it = v.begin(); + using Iter = decltype(it); + + ++it; + ++it; + + static_assert(std::is_same_v); + auto& it_ref = --it; + assert(&it_ref == &it); + + assert(it == ++v.begin()); + + static_assert(std::is_same_v); + auto tmp = it--; + assert(it == v.begin()); + assert(tmp == ++v.begin()); + } + + // non bidirectional + { + int buffer[3] = {4, 5, 6}; + std::ranges::concat_view v(a, NonBidi{buffer}); + using Iter = std::ranges::iterator_t; + static_assert(!canDecrement); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp new file mode 100644 index 0000000000000..6af8d415fd999 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include "test_iterators.h" +#include "../types.h" + +template > +constexpr void test() { + { + // test with one view + using View = minimal_view; + using ConcatView = std::ranges::concat_view; + using ConcatIterator = std::ranges::iterator_t; + + auto make_concat_view = [](auto begin, auto end) { + View view{Iter(begin), Sent(Iter(end))}; + return ConcatView(std::move(view)); + }; + + std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + ConcatView view = make_concat_view(array.data(), array.data() + array.size()); + ConcatIterator iter = view.begin(); + int& result = *iter; + ASSERT_SAME_TYPE(int&, decltype(*iter)); + assert(&result == array.data()); + } + + { + // test with more than one view + std::array array1{0, 1, 2}; + std::array array2{0, 1, 2}; + std::ranges::concat_view view(std::views::all(array1), std::views::all(array2)); + decltype(auto) it1 = view.begin(); + decltype(auto) it2 = view.begin() + 3; + + ASSERT_SAME_TYPE(int&, decltype(*it1)); + assert(*it1 == *it2); + } + + { + // constness + constexpr static std::array array1{0, 1, 2}; + constexpr static std::array array2{0, 1, 2}; + constexpr static std::ranges::concat_view view(std::views::all(array1), std::views::all(array2)); + decltype(auto) it1 = view.begin(); + decltype(auto) it2 = view.begin() + 3; + + ASSERT_SAME_TYPE(const int&, decltype(*it1)); + assert(*it1 == *it2); + } +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp new file mode 100644 index 0000000000000..7125338c553db --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#include +#include +#include "test_iterators.h" + +#include "../../range_adaptor_types.h" + +struct InputRange : IntBufferView { + using IntBufferView::IntBufferView; + using iterator = cpp20_input_iterator; + constexpr iterator begin() const { return iterator(buffer_); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(iterator(buffer_ + size_)); } +}; + +constexpr bool test() { + std::array a{1, 2, 3, 4}; + std::array b{1.1, 2.2, 3.3}; + + // one view + { + std::ranges::concat_view view(a); + auto it = view.begin(); + using Iter = decltype(it); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + auto& result = ++it; + assert(&result == &it); + assert(*result == 2); + } + + // more than one view + { + std::ranges::concat_view view(a, b); + auto it = view.begin(); + using Iter = decltype(it); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + auto& result = ++it; + assert(&result == &it); + assert(*result == 2); + } + + // more than one view + { + std::ranges::concat_view view(a, b, std::views::iota(0, 5)); + auto it = view.begin(); + using Iter = decltype(it); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + auto& result = ++it; + assert(&result == &it); + assert(*result == 2); + } + + // input range + { + int buffer[3] = {4, 5, 6}; + std::ranges::concat_view view(a, InputRange{buffer}); + auto it = view.begin(); + using Iter = decltype(it); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + auto& result = ++it; + assert(&result == &it); + assert(*result == 2); + } + + // Increment an iterator multiple times + { + std::ranges::concat_view view(a); + + auto it = view.begin(); + assert(*it == a[0]); + + ++it; + assert(*it == a[1]); + ++it; + assert(*it == a[2]); + ++it; + assert(*it == a[3]); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp new file mode 100644 index 0000000000000..203b428565201 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +struct Range : std::ranges::view_base { + using Iterator = forward_iterator; + using Sentinel = sentinel_wrapper; + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Sentinel end() const { return Sentinel(Iterator(end_)); } + +private: + int* begin_; + int* end_; +}; + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&) {} +}; + +template +constexpr bool test() { + using Sentinel = sentinel_wrapper; + using View = minimal_view; + + { + std::array array1{0, 1, 2, 3, 4}; + std::array array2{5, 6, 7, 8, 9}; + + View v1{Iterator(array1.data()), Sentinel(Iterator(array1.data() + array1.size()))}; + View v2{Iterator(array2.data()), Sentinel(Iterator(array2.data() + array2.size()))}; + std::ranges::concat_view view(std::move(v1), std::move(v2)); + + auto it = view.begin(); + assert(std::ranges::iter_move(view.begin()) == 0); + static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove); + } + + { + // iter_move may throw + auto throwingMoveRange = + std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; }); + std::ranges::concat_view v(throwingMoveRange); + auto it = v.begin(); + static_assert(!noexcept(std::ranges::iter_move(it))); + } + + return true; +} + +constexpr bool tests() { + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test(); + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp new file mode 100644 index 0000000000000..6cacbc15bd6cb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +template +concept has_iter_swap = requires(It it) { std::ranges::iter_swap(it, it); }; + +struct ThrowingMove { + ThrowingMove() = default; + ThrowingMove(ThrowingMove&&) {} + ThrowingMove& operator=(ThrowingMove&&) { return *this; } +}; + +template +constexpr void test() { + using Sentinel = sentinel_wrapper; + using View = minimal_view; + using ConcatView = std::ranges::concat_view; + + { + std::array array1{0, 1, 2, 3, 4}; + std::array array2{5, 6, 7, 8, 9}; + + View v1{Iterator(array1.data()), Sentinel(Iterator(array1.data() + array1.size()))}; + View v2{Iterator(array2.data()), Sentinel(Iterator(array2.data() + array2.size()))}; + std::ranges::concat_view view(std::move(v1), std::move(v2)); + + auto it1 = view.begin(); + auto it2 = ++view.begin(); + + static_assert(std::is_same_v); + static_assert(noexcept(iter_swap(it1, it2)) == IsNoexcept); + + assert(*it1 == 0 && *it2 == 1); + iter_swap(it1, it2); + assert(*it1 == 1); + assert(*it2 == 0); + } + + { + // iter swap may throw + std::array iterSwapMayThrow{}; + std::ranges::concat_view v(iterSwapMayThrow); + auto iter1 = v.begin(); + auto iter2 = ++v.begin(); + static_assert(!noexcept(std::ranges::iter_swap(iter1, iter2))); + } +} + +constexpr bool tests() { + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test(); + + // Test that iter_swap requires the underlying iterator to be iter_swappable + { + using Iterator = int const*; + using View = minimal_view; + using ConcatView = std::ranges::concat_view; + using ConcatIterator = std::ranges::iterator_t; + static_assert(!std::indirectly_swappable); + static_assert(!has_iter_swap); + } + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.pass.cpp new file mode 100644 index 0000000000000..069e41d67981d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/member_types.compile.pass.cpp @@ -0,0 +1,167 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include +#include + +#include "test_iterators.h" + +#include "../../range_adaptor_types.h" + +template +struct ForwardView : std::ranges::view_base { + forward_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +struct InputView : std::ranges::view_base { + cpp17_input_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +concept HasIterCategory = requires { typename T::iterator_category; }; + +template +struct DiffTypeIter { + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = T; + + int operator*() const; + DiffTypeIter& operator++(); + void operator++(int); + friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default; +}; + +template +struct DiffTypeRange { + DiffTypeIter begin() const; + DiffTypeIter end() const; +}; + +struct Foo {}; +struct Bar {}; + +struct ConstVeryDifferentRange { + int* begin(); + int* end(); + + forward_iterator begin() const; + forward_iterator end() const; +}; + +constexpr bool test() { + int buffer[] = {1, 2, 3, 4}; + { + // random_access_iterator_tag + std::ranges::concat_view v(buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); + } + + { + // 3 views + std::ranges::concat_view v(buffer, buffer, buffer); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); + } + + { + // bidirectional_iterator_tag + std::ranges::concat_view v(BidiCommonView{buffer}); + using Iter = decltype(v.begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // forward_iterator_tag + using Iter = std::ranges::iterator_t>>; + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(HasIterCategory); + } + + { + // input_iterator_tag + using Iter = std::ranges::iterator_t>>; + + static_assert(std::is_same_v); + static_assert(!HasIterCategory); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + { + // difference_type of single view + std::ranges::concat_view v{DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + + { + // difference_type of multiple views should be the common type + std::ranges::concat_view v{DiffTypeRange{}, DiffTypeRange{}}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v>); + } + + const std::array foos{Foo{}}; + { + // value_type of user-defined type + std::ranges::concat_view v{foos}; + using Iter = decltype(v.begin()); + static_assert(std::is_same_v); + } + + { + // const-iterator different from iterator + std::ranges::concat_view v{ConstVeryDifferentRange{}}; + using Iter = decltype(v.begin()); + using ConstIter = decltype(std::as_const(v).begin()); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/minus.pass.cpp new file mode 100644 index 0000000000000..86d8fc527a4d2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/minus.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + // Test two iterators + { + std::array array1{0, 1}; + std::array array2{2, 3}; + std::ranges::concat_view view(std::views::all(array1), std::views::all(array2)); + auto it1 = view.begin(); + it1++; + it1++; + auto it2 = view.begin(); + auto res = it1 - it2; + assert(res == 2); + } + + // Test one iterator and one sentinel + { + std::array array1{0, 1}; + std::array array2{2, 3}; + std::ranges::concat_view view(std::views::all(array1), std::views::all(array2)); + auto it1 = view.begin(); + auto res = std::default_sentinel_t{} - it1; + assert(res == 4); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/random_access.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/random_access.pass.cpp new file mode 100644 index 0000000000000..69afe2c4d7e76 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/random_access.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto operator[](difference_type n) const requires +// all_random_access + +#include +#include + +#include "../../range_adaptor_types.h" + +constexpr bool test() { + int buffer1[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + int buffer2[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + // random_access_range and common range + std::ranges::concat_view v(SimpleCommonRandomAccessSized{buffer1}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v); + } + + { + // random_access_range and common range, with last view is not a common range + std::ranges::concat_view v(SimpleCommonRandomAccessSized{buffer1}, SimpleNonCommonRandomAccessSized{buffer2}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v); + } + + { + // random_access_range and non common range + std::ranges::concat_view v(SimpleNonCommonRandomAccessSized{buffer1}, NonSimpleCommonRandomAccessSized{buffer2}); + auto it = v.begin(); + const auto canSubscript = [](auto&& it) { return requires { it[0]; }; }; + static_assert(!canSubscript(it)); + + static_assert(std::is_same_v); + } + + { + // contiguous_range + std::ranges::concat_view v(ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}); + auto it = v.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v); + } + + { + // non random_access_range + std::ranges::concat_view v(BidiCommonView{buffer1}); + auto iter = v.begin(); + const auto canSubscript = [](auto&& it) { return requires { it[0]; }; }; + static_assert(!canSubscript(iter)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp new file mode 100644 index 0000000000000..8503cff876e01 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/size.pass.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include "test_iterators.h" +#include "types.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; +struct View : std::ranges::view_base { + std::size_t size_ = 0; + constexpr View(std::size_t s) : size_(s) {} + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + size_; } +}; + +struct SizedNonConst : std::ranges::view_base { + using iterator = forward_iterator; + std::size_t size_ = 0; + constexpr SizedNonConst(std::size_t s) : size_(s) {} + constexpr auto begin() const { return iterator{buffer}; } + constexpr auto end() const { return iterator{buffer + size_}; } + constexpr std::size_t size() { return size_; } +}; + +struct StrangeSizeView : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } + + constexpr auto size() { return 5; } + constexpr auto size() const { return 6; } +}; + +struct NoSizeView : std::ranges::view_base { + constexpr auto begin() const { return buffer; } + constexpr auto end() const { return buffer + 8; } +}; + +constexpr bool test() { + { + // single range + std::ranges::concat_view v(View(8)); + assert(v.size() == 8); + assert(std::as_const(v).size() == 8); + } + + { + // multiple ranges same type + std::ranges::concat_view v(View(2), View(3)); + assert(v.size() == 5); + assert(std::as_const(v).size() == 5); + } + + { + // multiple ranges different types + std::ranges::concat_view v(std::views::iota(0, 5), View(3)); + assert(v.size() == 8); + assert(std::as_const(v).size() == 8); + } + + { + // const-view non-sized range + std::ranges::concat_view v(SizedNonConst(2), View(3)); + assert(v.size() == 5); + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + } + + { + // const/non-const has different sizes + std::ranges::concat_view v(StrangeSizeView{}); + assert(v.size() == 5); + assert(std::as_const(v).size() == 6); + } + + { + // underlying range not sized + std::ranges::concat_view v(InputCommonView{buffer}); + static_assert(!std::ranges::sized_range); + static_assert(!std::ranges::sized_range); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/types.h b/libcxx/test/std/ranges/range.adaptors/range.concat/types.h new file mode 100644 index 0000000000000..4a25b4d0709d1 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/types.h @@ -0,0 +1,184 @@ +//===----------------------------------------------------------------------===// +// +// 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_CONCAT_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CONCAT_TYPES_H + +#include +#include +#include "test_iterators.h" + +struct TrackInitialization { + constexpr explicit TrackInitialization(bool* moved, bool* copied) : moved_(moved), copied_(copied) {} + constexpr TrackInitialization(TrackInitialization const& other) : moved_(other.moved_), copied_(other.copied_) { + *copied_ = true; + } + constexpr TrackInitialization(TrackInitialization&& other) : moved_(other.moved_), copied_(other.copied_) { + *moved_ = true; + } + TrackInitialization& operator=(TrackInitialization const&) = default; + TrackInitialization& operator=(TrackInitialization&&) = default; + bool* moved_; + bool* copied_; +}; + +template +struct minimal_view : std::ranges::view_base { + constexpr explicit minimal_view(Iter it, Sent sent) : it_(base(std::move(it))), sent_(base(std::move(sent))) {} + + minimal_view(minimal_view&&) = default; + minimal_view& operator=(minimal_view&&) = 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_; +}; + +template +struct BufferView : std::ranges::view_base { + T* buffer_; + std::size_t size_; + + template + constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} +}; + +using IntBufferView = BufferView; + +template +struct Common : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr int* end() + requires(!Simple) + { + return buffer_ + size_; + } + constexpr const int* end() const { return buffer_ + size_; } +}; + +using SimpleCommon = Common; +using NonSimpleCommon = Common; + +using SimpleCommonRandomAccessSized = SimpleCommon; +using NonSimpleCommonRandomAccessSized = NonSimpleCommon; + +template +struct NonCommon : IntBufferView { + using IntBufferView::IntBufferView; + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() + requires(!Simple) + { + return sentinel_wrapper(buffer_ + size_); + } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } +}; + +using SimpleNonCommon = NonCommon; +using NonSimpleNonCommon = NonCommon; + +template +struct NonCommonSized : IntBufferView { + using IntBufferView::IntBufferView; + constexpr int* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() + requires(!Simple) + { + return sentinel_wrapper(buffer_ + size_); + } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } + constexpr std::size_t size() const { return size_; } +}; + +using SimpleNonCommonSized = NonCommonSized; +using SimpleNonCommonRandomAccessSized = SimpleNonCommonSized; +using NonSimpleNonCommonSized = NonCommonSized; +using NonSimpleNonCommonRandomAccessSized = NonSimpleNonCommonSized; + +template +struct NonCommonNonRandom : IntBufferView { + using IntBufferView::IntBufferView; + + using const_iterator = forward_iterator; + using iterator = forward_iterator; + + constexpr iterator begin() + requires(!Simple) + { + return iterator(buffer_); + } + constexpr const_iterator begin() const { return const_iterator(buffer_); } + constexpr sentinel_wrapper end() + requires(!Simple) + { + return sentinel_wrapper(iterator(buffer_ + size_)); + } + constexpr sentinel_wrapper end() const { + return sentinel_wrapper(const_iterator(buffer_ + size_)); + } +}; + +using SimpleNonCommonNonRandom = NonCommonNonRandom; +using NonSimpleNonCommonNonRandom = NonCommonNonRandom; + +template +struct BasicView : IntBufferView { + using IntBufferView::IntBufferView; + + constexpr NonConstIter begin() + requires(!std::is_same_v) + { + return NonConstIter(buffer_); + } + constexpr Iter begin() const { return Iter(buffer_); } + + constexpr NonConstSent end() + requires(!std::is_same_v) + { + if constexpr (std::is_same_v) { + return NonConstIter(buffer_ + size_); + } else { + return NonConstSent(NonConstIter(buffer_ + size_)); + } + } + + constexpr Sent end() const { + if constexpr (std::is_same_v) { + return Iter(buffer_ + size_); + } else { + return Sent(Iter(buffer_ + size_)); + } + } +}; + +using NonSizedRandomAccessView = + BasicView, sentinel_wrapper>>; + +using InputCommonView = BasicView>; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_CONCAT_FILTER_TYPES_H diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index d9317e00e3f4a..a247d34c33967 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1102,7 +1102,6 @@ def add_version_header(tc): "name": "__cpp_lib_ranges_concat", "values": {"c++26": 202403}, # P2542R8: views::concat "headers": ["ranges"], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_contains",