diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 3c635e5e46bbd..fb0e76aff77f4 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -482,7 +482,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 c52bc54f412bd..9e025fc63e087 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 `__) @@ -52,6 +53,7 @@ Implemented Papers - P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github `__) - P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github `__) + Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index d3feecf6513e4..f6eb91a641eeb 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 2eb1921069776..8e0953b148230 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 e386f31386b60..0ddd102224ea2 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -689,6 +689,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..f2f7cfefbe5bb --- /dev/null +++ b/libcxx/include/__ranges/concat_view.h @@ -0,0 +1,637 @@ +// -*- 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 +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 +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<__maybe_const::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<__maybe_const::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 +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_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>; + +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(!__x.__it_.valueless_by_exception() && !__y.__it_.valueless_by_exception(), + "Trying to compare a valueless iterator 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>>> && + ...) && + (__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."); + 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>>> && + ...) && + (__apply_drop_first<_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..c7e671581a9b8 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> @@ -171,6 +172,34 @@ concept __concatable = requires { typename __concat_rvalue_reference_t<_Rs...>; } && __concat_indirectly_readable<_Rs...>; +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); + # endif // _LIBCPP_STD_VER >= 23 } // namespace ranges diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index adf80f2ac9acb..06ad1f0b29da2 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1834,6 +1834,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 2a6321bd2c5d8..7ef88f63682ed 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; @@ -442,6 +452,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 91fe48351e161..31a47117d45a3 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -585,7 +585,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 adabeeb22d551..58690c6f84830 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 4cf5178dd7b8f..59d10100595ad 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 e546719142231..d5f0c67743bef 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 @@ -7509,17 +7509,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..a47c7a64b375b --- /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 ConcatIterator = std::ranges::iterator_t; + ASSERT_SAME_TYPE(ConcatIterator, 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..8cbd5d5a899fb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.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 "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; +}; + +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.default.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.verify.cpp new file mode 100644 index 0000000000000..b848b654916d0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.default.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/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.view.pass.cpp new file mode 100644 index 0000000000000..8273849bf8889 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/ctor.view.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// 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 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..639c0abb245f2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/end.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 "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..349f96224b4c0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/compare.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// 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)); + }; + + { + 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..fcee9deac9e58 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/decrement.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../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..e58ba032af5c5 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/deref.pass.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 "test_iterators.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..3edaae03fddaf --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/increment.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 +#include "test_iterators.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/iterator/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/minus.pass.cpp new file mode 100644 index 0000000000000..41a5c93733e2a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/minus.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 void 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); + } +} + +int main(int, char**) { + test(); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/random_acess.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/random_acess.pass.cpp new file mode 100644 index 0000000000000..443ecf96d0cc7 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/iterator/random_acess.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: has-unix-headers, std-at-least-c++26 + +#include +#include +#include + +constexpr bool test() { + std::vector v1 = {1, 2, 3}; + std::vector v2 = {4, 5, 6}; + auto cv = std::views::concat(v1, v2); + static_assert(std::random_access_iterator); + assert(cv[0] == 1); + assert(cv[2] == 3); + assert(cv[3] == 4); + assert(cv[5] == 6); + 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..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 edd7b124a1fb3..20bd14a7bf3f7 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1094,7 +1094,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",