diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 9b57b7c8eeb52..bf289f1a3b63e 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -476,7 +476,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 c571dd6f08fe9..9e34c9a9d912e 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 `__) - P2562R1: ``constexpr`` Stable Sorting (`Github `__) - P0472R3: Put std::monostate in (`Github `__) @@ -48,6 +49,7 @@ Implemented Papers - P2897R7: ``aligned_accessor``: An mdspan accessor expressing pointer over-alignment (`Github `__) - P3247R2: Deprecate the notion of trivial types (`Github `__) + Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index 9bf31c417f3c9..4340fead9a0db 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)","","","" "`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 3809446a57896..83e0f78f0c546 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 8cb05857ea8d7..e1b9d6852e47a 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -688,6 +688,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..4c8efb5dbfdfb --- /dev/null +++ b/libcxx/include/__ranges/concat_view.h @@ -0,0 +1,672 @@ +// -*- 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 <__algorithm/ranges_find_if.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 <__functional/bind_back.h> +#include <__functional/invoke.h> +#include <__functional/reference_wrapper.h> +#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 <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/movable_box.h> +#include <__ranges/non_propagating_cache.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/is_object.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 +constexpr bool __derived_from_pack = + __derived_from_pack<_Tp, __extract_last<_Tail...>> && __derived_from_pack<_Tail...>; + +template +constexpr bool __derived_from_pack<_Tp, _IterCategory> = derived_from<_Tp, _IterCategory>; + +template +struct __last_view : __last_view<_Views...> {}; + +template +struct __last_view<_View> { + using type _LIBCPP_NODEBUG = _View; +}; + +template +concept __concat_indirectly_readable_impl = requires(const _It __it) { + { *__it } -> convertible_to<_Ref>; + { ranges::iter_move(__it) } -> convertible_to<_RRef>; +}; + +template +using __concat_reference_t _LIBCPP_NODEBUG = common_reference_t...>; + +template +using __concat_value_t _LIBCPP_NODEBUG = common_type_t...>; + +template +using __concat_rvalue_reference_t _LIBCPP_NODEBUG = common_reference_t...>; + +template +concept __concat_indirectly_readable = + common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&> && + common_reference_with<__concat_reference_t<_Rs...>&&, __concat_rvalue_reference_t<_Rs...>&&> && + common_reference_with<__concat_rvalue_reference_t<_Rs...>&&, __concat_value_t<_Rs...> const&> && + (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>, + __concat_rvalue_reference_t<_Rs...>, + iterator_t<_Rs>> && + ...); + +template +concept __concatable = requires { + typename __concat_reference_t<_Rs...>; + typename __concat_value_t<_Rs...>; + typename __concat_rvalue_reference_t<_Rs...>; +} && __concat_indirectly_readable<_Rs...>; + +template +concept __concat_is_random_access = + (random_access_range<__maybe_const<_Const, _Rs>> && ...) && (sized_range<__maybe_const<_Const, _Rs>> && ...); + +template +concept __concat_is_bidirectional = + ((bidirectional_range<__maybe_const<_Const, _Rs>> && ...) && (common_range<__maybe_const<_Const, _Rs>> && ...)); + +template +concept __all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...); + +template +struct __apply_drop_first; + +template +struct __apply_drop_first<_Const, _Head, _Tail...> { + static constexpr bool value = (sized_range<__maybe_const<_Const, _Tail>> && ...); +}; + +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::type>) { + 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::type>) { + 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 + requires(view<_Views> && ...) && (sizeof...(_Views) > 0) && __concatable<_Views...> +template +class concat_view<_Views...>::__iterator { +public: + constexpr static bool derive_pack_random_iterator = + __derived_from_pack>>::iterator_category..., + random_access_iterator_tag>; + constexpr static bool derive_pack_bidirectional_iterator = + __derived_from_pack>>::iterator_category..., + bidirectional_iterator_tag>; + constexpr static bool derive_pack_forward_iterator = + __derived_from_pack>>::iterator_category..., + forward_iterator_tag>; + using iterator_category = + _If...>>, + input_iterator_tag, + _If > > >; + 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>...>; + 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_))) { + using __prev_view = __maybe_const<_Const, tuple_element_t<_Idx - 1, tuple<_Views...>>>; + if constexpr (common_range<__prev_view>) { + __it_.template emplace<_Idx - 1>(ranges::end(std::get<_Idx - 1>(__parent_->__views_))); + } else { + __it_.template emplace<_Idx - 1>(ranges::__next(ranges::begin(std::get<_Idx - 1>(__parent_->__views_)), + ranges::size(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::begin(std::get<_Idx - 1>(__parent_->__views_)) + __prev_size); + __advance_bwd<_Idx - 1>(__prev_size, __steps - __offset); + } + } + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __apply_at_index(size_t __index, _Func&& __func, index_sequence<_Is...>) { + ((__index == _Is ? (__func(integral_constant{}), 0) : 0), ...); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr void __apply_at_index(size_t __index, _Func&& __func) { + __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) {} + +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(forward_range<__maybe_const<_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(random_access_range<__maybe_const<_Const, _Views>> && ...) + { + return __x.__it_ < __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires(random_access_range<__maybe_const<_Const, _Views>> && ...) + { + return __x.__it_ > __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires(random_access_range<__maybe_const<_Const, _Views>> && ...) + { + return __x.__it_ <= __y.__it_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires(random_access_range<__maybe_const<_Const, _Views>> && ...) + { + 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."); + size_t __ix = __x.__it_.index(); + size_t __iy = __y.__it_.index(); + + if (__ix > __iy) { + __variant_detail::__visitation::__variant::__visit_value( + [&](auto& __it_x, auto& __it_y) { + __it_x.template __apply_at_index__views_)>>( + __ix, [&](auto __index_constant_x) { + constexpr size_t __index_x = __index_constant_x.value; + auto __dx = ranges::distance(ranges::begin(std::get<__index_x>(__x.__parent_->__views_)), __it_x); + + __it_y.template __apply_at_index__views_)>>( + __iy, [&](auto __index_constant_y) { + constexpr size_t __index_y = __index_constant_y.value; + auto __dy = + ranges::distance(ranges::begin(std::get<__index_y>(__y.__parent_->__views_)), __it_y); + difference_type __s = 0; + for (size_t __idx = __index_y + 1; __idx < __index_x; __idx++) { + __s += ranges::size(std::get<__idx>(__x.__parent_->__views_)); + } + return __dy + __s + __dx; + }); + }); + }, + __x.__it_, + __y.__it_); + } else if (__ix < __iy) { + return -(__y - __x); + } else { + __variant_detail::__visitation::__variant::__visit_value( + [&](const auto& __it1, const auto& __it2) { return __it1 - __it2; }, __x.__it_, __y.__it_); + } + } + + _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 difference_type operator-(const __iterator& __x, default_sentinel_t) + requires(sized_sentinel_for>, iterator_t<__maybe_const<_Const, _Views>>> && + ...) && + (__apply_drop_first<_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."); + size_t __ix = __x.__it_.index(); + __variant_detail::__visitation::__variant::__visit_value( + [&](auto& __it_x) { + __it_x.template __apply_at_index__views_)>>( + __ix, [&](auto __index_constant) { + constexpr size_t __index_x = __index_constant.value; + auto __dx = ranges::distance(ranges::begin(std::get<__index_x>(__x.__parent_->__views_)), __it_x); + + difference_type __s = 0; + for (size_t __idx = 0; __idx < __index_x; __idx++) { + __s += ranges::size(std::get<__idx>(__x.__parent_->__views_)); + } + + return -(__dx + __s); + }); + }, + __x.__it_); + } + + _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>>> && + ...) && + (__apply_drop_first<_Const, _Views...>::value) + { + -(__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/module.modulemap.in b/libcxx/include/module.modulemap.in index b9cb43ebd999a..3de67055eb93d 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1837,6 +1837,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 49fea7c3f84ec..15fdf2955231d 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; @@ -432,6 +442,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 77d97b93adc6c..0bfaa8e48c44f 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -579,7 +579,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 a5e2a2b4583c1..ba14d12c198d7 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..048b8458db044 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/iterator.valueless_by_exception.pass.cpp @@ -0,0 +1,264 @@ +//===----------------------------------------------------------------------===// +// +// 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) = default; + 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_; } +}; + +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; + + 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-- + 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 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 c7c8112e123cd..affe070203129 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 @@ -393,17 +393,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 # if !defined(_LIBCPP_VERSION) 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 aa33a2788f1eb..e054ed8ffad3a 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 @@ -7434,17 +7434,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..8b4e2716d6337 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/adaptor.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#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..b4270093c3961 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/begin.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include +#include + +#include +#include "test_iterators.h" + +constexpr void general_tests() { + std::vector v1 = {1, 2, 3, 4, 5, 6, 7, 8}; + std::vector v2 = {1, 2, 3, 4, 5, 6, 7, 8}; + // Check the return type of `.begin()` + { + std::ranges::concat_view view(v1, v2); + using FilterIterator = std::ranges::iterator_t; + ASSERT_SAME_TYPE(FilterIterator, decltype(view.begin())); + } +} + +constexpr bool test() { + general_tests(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + 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..b5fde5bfc0257 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctad.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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>>); + } + + 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..51984908d7b03 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.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 + +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; +}; + +constexpr bool test() { + { + 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); + } + + // 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**) { + test(); + static_assert(test()); + + 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..bb916e11fb962 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.view.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// 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_convertible.h" +#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 TrackingRange : TrackInitialization, std::ranges::view_base { + using TrackInitialization::TrackInitialization; + int* begin() const; + int* end() const; +}; + +constexpr bool test() { + int buff[] = {1, 2, 3, 4}; + + // Test explicit syntax + { + Range range(buff, buff + 4); + std::ranges::concat_view view(range); + 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 + { + bool moved = false, copied = false; + TrackingRange range(&moved, &copied); + [[maybe_unused]] std::ranges::concat_view view(std::move(range)); + assert(moved); + assert(!copied); + } + + 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..65df03140427d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/end.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +#include + +#include +#include +#include +#include +#include "test_iterators.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 CommonRange : std::ranges::view_base { + using Iterator = forward_iterator; + constexpr explicit CommonRange(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Iterator end() const { return Iterator(end_); } + +private: + int* begin_; + int* end_; +}; + +struct NotCommonRange : std::ranges::view_base { + constexpr explicit NotCommonRange() {} + char* begin(); + bool end(); +}; + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Check the return type of `.end()` + { + Range range(buff, buff + 1); + std::ranges::concat_view view(range); + using ConcatSentinel = std::ranges::sentinel_t; + ASSERT_SAME_TYPE(ConcatSentinel, decltype(view.end())); + } + + // Check a not a common range + { + Range range(buff, buff + 1); + std::ranges::concat_view view(range); + using ConcatSentinel = std::default_sentinel_t; + ASSERT_SAME_TYPE(ConcatSentinel, decltype(view.end())); + } + + // end() on an empty range + { + Range range(buff, buff); + std::ranges::concat_view view(range); + auto begin = view.begin(); + auto end = view.end(); + assert(begin == end); + } + + // end() on a common_range + { + CommonRange range(buff, buff + 1); + CommonRange range_2(buff + 2, buff + 3); + std::ranges::concat_view view(range, range_2); + auto it = view.begin(); + it++; + it++; + auto end = view.end(); + assert(it == end); + static_assert(std::is_same_v); + } + + return true; +} + +int main(int, char**) { + 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..4ad690aebfabd --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// 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 <__type_traits/maybe_const.h> +#include "test_iterators.h" +#include "test_macros.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)); + }; + + { + 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)); + } + + { + 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(); + 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..4693c1586dc37 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/ctor.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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..81ea5fb88565d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.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 +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +constexpr void test() { + // Test with a single satisfied value + { + constexpr static std::array array{0, 1, 2, 3, 4}; + constexpr static std::ranges::concat_view view(std::views::all(array)); + 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 == &it); + assert(result == view.begin() + 4); + } + + // Test with more than one satisfied value + { + constexpr static std::array array{0, 1, 2, 3, 4}; + constexpr static std::ranges::concat_view view(std::views::all(array)); + auto it = std::ranges::next(view.begin(), view.end()); + assert(it == view.end()); // test the test + + auto& result = --it; + assert(&result == &it); + + --it; + assert(it == view.begin() + 3); + } + + // Test going forward and then backward on the same iterator + { + constexpr static std::array array{0, 1, 2, 3, 4}; + constexpr static std::ranges::concat_view view(std::views::all(array)); + auto it = view.begin(); + ++it; + --it; + assert(*it == array[0]); + ++it; + ++it; + --it; + assert(*it == array[1]); + ++it; + ++it; + --it; + assert(*it == array[2]); + ++it; + ++it; + --it; + assert(*it == array[3]); + } + + // Test post-decrement + { + std::array array{0, 1, 2, 3, 4}; + std::ranges::concat_view view(std::views::all(array)); + 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)); + } +} + +int main(int, char**) { + 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..e5cfbcb3377cc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.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 +#include +#include +#include +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +template > +constexpr void test() { + 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()); +} + +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..7102561922a73 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +template +constexpr void test() { + using Sentinel = sentinel_wrapper; + 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{Iterator(begin), Sentinel(Iterator(end))}; + return ConcatView(std::move(view)); + }; + + // Increment an iterator when it won't find another satisfied value after begin() + { + std::array array{0, 1, 2, 3, 4}; + ConcatView view = make_concat_view(array.data(), array.data() + array.size()); + + auto it = view.begin(); + auto& result = ++it; + ASSERT_SAME_TYPE(ConcatIterator&, decltype(++it)); + assert(&result == &it); + assert(*result == 1); + } + + // Increment an iterator multiple times + { + std::array array{0, 1, 2, 3, 4}; + ConcatView view = make_concat_view(array.data(), array.data() + array.size()); + + ConcatIterator it = view.begin(); + assert(*it == array[0]); + + ++it; + assert(*it == array[1]); + ++it; + assert(*it == array[2]); + ++it; + assert(*it == array[3]); + ++it; + assert(*it == array[4]); + } +} + +constexpr bool tests() { + test >(); + test >(); + test >(); + test >(); + test >(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + 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..203ba350430f3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_move.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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_; +}; + +template +constexpr bool test() { + using Sentinel = sentinel_wrapper; + 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{Iterator(begin), Sentinel(Iterator(end))}; + return ConcatView(std::move(view)); + }; + + // noexcept in case of a forward iterator + { + int buff[] = {0, 1, 2, 3}; + ConcatView view = make_concat_view(buff, buff + 4); + ConcatIterator it = view.begin(); + int&& result = iter_move(it); + static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove); + assert(&result == buff); + } + + 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..6ae4a53652aa4 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/iter_swap.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 +#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); }; + +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)); + }; + + { + std::array array{0, 1, 2, 3, 4}; + ConcatView view = make_concat_view(array.data(), array.data() + array.size()); + std::array another_array{5, 6, 7, 8, 9}; + ConcatView another_view = make_concat_view(another_array.data(), another_array.data() + another_array.size()); + auto it1 = view.begin(); + auto it2 = another_view.begin(); + + static_assert(std::is_same_v); + static_assert(noexcept(iter_swap(it1, it2)) == IsNoexcept); + + assert(*it1 == 0 && *it2 == 5); // test the test + iter_swap(it1, it2); + assert(*it1 == 5); + assert(*it2 == 0); + } +} + +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/types.h b/libcxx/test/std/ranges/range.adaptors/range.concat/types.h new file mode 100644 index 0000000000000..5686dc7c6b095 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/types.h @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CONCAT_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CONCAT_TYPES_H + +#include +#include + +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_; +}; + +#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 cb92dc17ba707..1f5eecf845705 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1075,7 +1075,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",