diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 72f624fa746b41..2b382db2e5b4f3 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -362,7 +362,7 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_iota`` *unimplemented* ---------------------------------------------------------- ----------------- - ``__cpp_lib_ranges_join_with`` *unimplemented* + ``__cpp_lib_ranges_join_with`` ``202202L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_repeat`` ``202207L`` ---------------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst index 71de10abb6eaa8..81aaa54b5c2310 100644 --- a/libcxx/docs/ReleaseNotes/19.rst +++ b/libcxx/docs/ReleaseNotes/19.rst @@ -53,6 +53,9 @@ Implemented Papers - P2713R1 - Escaping improvements in ``std::format`` - P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant`` - P0019R8 - ``std::atomic_ref`` +- P2441R2 - ``views::join_with`` +- P2711R1 - Making multi-param constructors of ``views`` ``explicit`` +- P2770R0 - Stashing stashing ``iterators`` for proper flattening Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst index 23d30c8128d71e..a10f4ef9bf0719 100644 --- a/libcxx/docs/Status/Cxx23.rst +++ b/libcxx/docs/Status/Cxx23.rst @@ -44,8 +44,6 @@ Paper Status .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations. .. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well. - .. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet. - .. [#note-P2770R0] P2770R0: ``join_with_view`` hasn't been done yet since this type isn't implemented yet. .. [#note-P2693R1] P2693R1: The formatter for ``std::thread::id`` is implemented. The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is not implemented yet. @@ -63,5 +61,5 @@ Library Working Group Issues Status .. note:: .. [#note-LWG3750] LWG3750 Only ``__cpp_lib_format_ranges`` is fully implemented. - .. [#note-LWG3798] LWG3798: ``join_with_view``, ``zip_transform_view``, and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet. + .. [#note-LWG3798] LWG3798: ``zip_transform_view`` and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet. .. [#note-LWG3036] LWG3036: This issue was reverted by P2875R4 diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 36d7f78285aa9c..761cfd1313daac 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -47,7 +47,7 @@ "`P2273R3 `__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0" "`P2387R3 `__","LWG","Pipe support for user-defined range adaptors","February 2022","|Complete|","19.0","|ranges|" "`P2440R1 `__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" -"`P2441R2 `__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|" +"`P2441R2 `__","LWG","``views::join_with``","February 2022","|Complete|","19.0","|ranges|" "`P2442R1 `__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|" "`P2443R1 `__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|" "","","","","","","" @@ -104,9 +104,9 @@ "`P2708R1 `__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","","" "","","","","","","" "`P0290R4 `__","LWG", "``apply()`` for ``synchronized_value``","February 2023","","","|concurrency TS|" -"`P2770R0 `__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Partial| [#note-P2770R0]_","","|ranges|" +"`P2770R0 `__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Complete|","19.0","|ranges|" "`P2164R9 `__","LWG", "``views::enumerate``","February 2023","","","|ranges|" -"`P2711R1 `__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|In Progress| [#note-P2711R1]_","","|ranges|" +"`P2711R1 `__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|Complete|","19.0","|ranges|" "`P2609R3 `__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|" "`P2713R1 `__","LWG", "Escaping improvements in ``std::format``","February 2023","|Complete|","19.0","|format|" "`P2675R1 `__","LWG", "``format``'s width estimation is too approximate and not forward compatible","February 2023","|Complete|","17.0","|format|" diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv index d00fbce9edf489..be67d2d94bf97b 100644 --- a/libcxx/docs/Status/RangesMajorFeatures.csv +++ b/libcxx/docs/Status/RangesMajorFeatures.csv @@ -2,4 +2,4 @@ Standard,Name,Assignee,CL,Status C++23,`ranges::to `_,Konstantin Varlamov,`D142335 `_,Complete C++23,`Pipe support for user-defined range adaptors `_,"Louis Dionne, Jakub Mazurkiewicz, and Xiaoyang Liu",Various,Complete C++23,`Formatting Ranges `_,Mark de Wever,Various,Complete -C++20,`Stashing stashing iterators for proper flattening `_,Jakub Mazurkiewicz,Various,In progress +C++20,`Stashing stashing iterators for proper flattening `_,Jakub Mazurkiewicz,Various,Complete diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv index f141656eb131a2..fac950134ee88f 100644 --- a/libcxx/docs/Status/RangesViews.csv +++ b/libcxx/docs/Status/RangesViews.csv @@ -28,7 +28,7 @@ C++23,`zip `_,Hui Xie,`D122806 `_,Hui Xie,No patch yet,Not started C++23,`adjacent `_,Hui Xie,No patch yet,Not started C++23,`adjacent_transform `_,Hui Xie,No patch yet,Not started -C++23,`join_with `_,Jakub Mazurkiewicz,`65536 `_,In progress +C++23,`join_with `_,Jakub Mazurkiewicz,`65536 `_,✅ C++23,`slide `_,Will Hawkins,`67146 `_,In Progress C++23,`chunk `_,Unassigned,No patch yet,Not started C++23,`chunk_by `_,Jakub Mazurkiewicz,`D144767 `_,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 8d0ffd6ed725bd..67a22550701f2a 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -651,6 +651,7 @@ set(files __ranges/iota_view.h __ranges/istream_view.h __ranges/join_view.h + __ranges/join_with_view.h __ranges/lazy_split_view.h __ranges/movable_box.h __ranges/non_propagating_cache.h diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h new file mode 100644 index 00000000000000..3918b64ebaf1ad --- /dev/null +++ b/libcxx/include/__ranges/join_with_view.h @@ -0,0 +1,456 @@ +// -*- 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_JOIN_WITH_VIEW_H +#define _LIBCPP___RANGES_JOIN_WITH_VIEW_H + +#include <__concepts/common_reference_with.h> +#include <__concepts/common_with.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/derived_from.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/single_view.h> +#include <__ranges/view_interface.h> +#include <__type_traits/conditional.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_reference.h> +#include <__type_traits/maybe_const.h> +#include <__utility/as_const.h> +#include <__utility/as_lvalue.h> +#include <__utility/empty.h> +#include <__utility/forward.h> +#include <__utility/move.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { +template +concept __compatible_joinable_ranges = + common_with, range_value_t<_Pattern>> && + common_reference_with, range_reference_t<_Pattern>> && + common_reference_with, range_rvalue_reference_t<_Pattern>>; + +template +concept __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>; + +template + requires view<_View> && input_range> && view<_Pattern> && + __compatible_joinable_ranges, _Pattern> +class join_with_view : public view_interface> { + using _InnerRng = range_reference_t<_View>; + + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + + static constexpr bool _UseOuterItCache = !forward_range<_View>; + using _OuterItCache = _If<_UseOuterItCache, __non_propagating_cache>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _OuterItCache __outer_it_; + + static constexpr bool _UseInnerCache = !is_reference_v<_InnerRng>; + using _InnerCache = _If<_UseInnerCache, __non_propagating_cache>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _InnerCache __inner_; + + _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern(); + + template + struct __iterator; + + template + struct __sentinel; + +public: + _LIBCPP_HIDE_FROM_ABI join_with_view() + requires default_initializable<_View> && default_initializable<_Pattern> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_View __base, _Pattern __pattern) + : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {} + + template + requires constructible_from<_View, views::all_t<_Range>> && + constructible_from<_Pattern, single_view>> + _LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_Range&& __r, range_value_t<_InnerRng> __e) + : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {} + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { + if constexpr (forward_range<_View>) { + constexpr bool __use_const = __simple_view<_View> && is_reference_v<_InnerRng> && __simple_view<_Pattern>; + return __iterator<__use_const>{*this, ranges::begin(__base_)}; + } else { + __outer_it_.__emplace(ranges::begin(__base_)); + return __iterator{*this}; + } + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires forward_range && forward_range && + is_reference_v> && input_range> + { + return __iterator{*this, ranges::begin(__base_)}; + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto end() { + constexpr bool __use_const = __simple_view<_View> && __simple_view<_Pattern>; + if constexpr (forward_range<_View> && is_reference_v<_InnerRng> && forward_range<_InnerRng> && + common_range<_View> && common_range<_InnerRng>) + return __iterator<__use_const>{*this, ranges::end(__base_)}; + else + return __sentinel<__use_const>{*this}; + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires forward_range && forward_range && + is_reference_v> && input_range> + { + using _InnerConstRng = range_reference_t; + if constexpr (forward_range<_InnerConstRng> && common_range && common_range<_InnerConstRng>) + return __iterator{*this, ranges::end(__base_)}; + else + return __sentinel{*this}; + } +}; + +template +join_with_view(_Range&&, _Pattern&&) -> join_with_view, views::all_t<_Pattern>>; + +template +join_with_view(_Range&&, range_value_t>) + -> join_with_view, single_view>>>; + +template +struct __join_with_view_iterator_category {}; + +template + requires is_reference_v> && forward_range<_Base> && forward_range> +struct __join_with_view_iterator_category<_Base, _PatternBase> { +private: + static consteval auto __get_iterator_category() noexcept { + using _OuterC = iterator_traits>::iterator_category; + using _InnerC = iterator_traits>>::iterator_category; + using _PatternC = iterator_traits>::iterator_category; + + if constexpr (!is_reference_v>>, + iter_reference_t>>>) + return input_iterator_tag{}; + else if constexpr (derived_from<_OuterC, bidirectional_iterator_tag> && + derived_from<_InnerC, bidirectional_iterator_tag> && + derived_from<_PatternC, bidirectional_iterator_tag> && common_range> && + common_range<_PatternBase>) + return bidirectional_iterator_tag{}; + else if constexpr (derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag> && + derived_from<_PatternC, forward_iterator_tag>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + +public: + using iterator_category = decltype(__get_iterator_category()); +}; + +template + requires view<_View> && input_range> && view<_Pattern> && + __compatible_joinable_ranges, _Pattern> +template +struct join_with_view<_View, _Pattern>::__iterator + : public __join_with_view_iterator_category<__maybe_const<_Const, _View>, __maybe_const<_Const, _Pattern>> { +private: + friend join_with_view; + + using _Parent = __maybe_const<_Const, join_with_view>; + using _Base = __maybe_const<_Const, _View>; + using _InnerBase = range_reference_t<_Base>; + using _PatternBase = __maybe_const<_Const, _Pattern>; + + using _OuterIter = iterator_t<_Base>; + using _InnerIter = iterator_t<_InnerBase>; + using _PatternIter = iterator_t<_PatternBase>; + + static_assert(!_Const || forward_range<_Base>, "Const can only be true when Base models forward_range."); + + static constexpr bool __ref_is_glvalue = is_reference_v<_InnerBase>; + + _Parent* __parent_ = nullptr; + + static constexpr bool _OuterIterPresent = forward_range<_Base>; + using _OuterIterType = _If<_OuterIterPresent, _OuterIter, std::__empty>; + _LIBCPP_NO_UNIQUE_ADDRESS _OuterIterType __outer_it_ = _OuterIterType(); + + variant<_PatternIter, _InnerIter> __inner_it_; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, _OuterIter __outer) + requires forward_range<_Base> + : __parent_(std::addressof(__parent)), __outer_it_(std::move(__outer)) { + if (__get_outer() != ranges::end(__parent_->__base_)) { + __inner_it_.template emplace<1>(ranges::begin(__update_inner())); + __satisfy(); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Parent& __parent) + requires(!forward_range<_Base>) + : __parent_(std::addressof(__parent)) { + if (__get_outer() != ranges::end(__parent_->__base_)) { + __inner_it_.template emplace<1>(ranges::begin(__update_inner())); + __satisfy(); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() { + if constexpr (forward_range<_Base>) + return __outer_it_; + else + return *__parent_->__outer_it_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const { + if constexpr (forward_range<_Base>) + return __outer_it_; + else + return *__parent_->__outer_it_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto& __update_inner() { + if constexpr (__ref_is_glvalue) + return std::__as_lvalue(*__get_outer()); + else + return __parent_->__inner_.__emplace_from([&]() -> decltype(auto) { return *__get_outer(); }); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() { + if constexpr (__ref_is_glvalue) + return std::__as_lvalue(*__get_outer()); + else + return *__parent_->__inner_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void __satisfy() { + while (true) { + if (__inner_it_.index() == 0) { + if (std::get<0>(__inner_it_) != ranges::end(__parent_->__pattern_)) + break; + + __inner_it_.template emplace<1>(ranges::begin(__update_inner())); + } else { + if (std::get<1>(__inner_it_) != ranges::end(__get_inner())) + break; + + if (++__get_outer() == ranges::end(__parent_->__base_)) { + if constexpr (__ref_is_glvalue) + __inner_it_.template emplace<0>(); + + break; + } + + __inner_it_.template emplace<0>(ranges::begin(__parent_->__pattern_)); + } + } + } + + static consteval auto __get_iterator_concept() noexcept { + if constexpr (__ref_is_glvalue && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && + __bidirectional_common<_PatternBase>) + return bidirectional_iterator_tag{}; + else if constexpr (__ref_is_glvalue && forward_range<_Base> && forward_range<_InnerBase>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + +public: + using iterator_concept = decltype(__get_iterator_concept()); + using value_type = common_type_t, iter_value_t<_PatternIter>>; + using difference_type = + common_type_t, iter_difference_t<_InnerIter>, iter_difference_t<_PatternIter>>; + + _LIBCPP_HIDE_FROM_ABI __iterator() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && convertible_to, _OuterIter> && + convertible_to, _InnerIter> && convertible_to, _PatternIter> + : __parent_(__i.__parent_), __outer_it_(std::move(__i.__outer_it_)) { + if (__i.__inner_it_.index() == 0) { + __inner_it_.template emplace<0>(std::get<0>(std::move(__i.__inner_it_))); + } else { + __inner_it_.template emplace<1>(std::get<1>(std::move(__i.__inner_it_))); + } + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { + using __reference = common_reference_t, iter_reference_t<_PatternIter>>; + return std::visit([](auto& __it) -> __reference { return *__it; }, __inner_it_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + std::visit([](auto& __it) { ++__it; }, __inner_it_); + __satisfy(); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires __ref_is_glvalue && forward_iterator<_OuterIter> && forward_iterator<_InnerIter> + { + __iterator __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires __ref_is_glvalue + && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase> + { + if (__outer_it_ == ranges::end(__parent_->__base_)) { + auto&& __inner = *--__outer_it_; + __inner_it_.template emplace<1>(ranges::end(__inner)); + } + + while (true) { + if (__inner_it_.index() == 0) { + auto& __it = std::get<0>(__inner_it_); + if (__it == ranges::begin(__parent_->__pattern_)) { + auto&& __inner = *--__outer_it_; + __inner_it_.template emplace<1>(ranges::end(__inner)); + } else + break; + } else { + auto& __it = std::get<1>(__inner_it_); + auto&& __inner = *__outer_it_; + if (__it == ranges::begin(__inner)) + __inner_it_.template emplace<0>(ranges::end(__parent_->__pattern_)); + else + break; + } + } + + std::visit([](auto& __it) { --__it; }, __inner_it_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires __ref_is_glvalue + && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase> + { + __iterator __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) + requires __ref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter> + { + return __x.__outer_it_ == __y.__outer_it_ && __x.__inner_it_ == __y.__inner_it_; + } + + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto) iter_move(const __iterator& __x) { + using __rvalue_reference = + common_reference_t, iter_rvalue_reference_t<_PatternIter>>; + return std::visit<__rvalue_reference>(ranges::iter_move, __x.__inner_it_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y) + requires indirectly_swappable<_InnerIter, _PatternIter> + { + std::visit(ranges::iter_swap, __x.__inner_it_, __y.__inner_it_); + } +}; + +template + requires view<_View> && input_range> && view<_Pattern> && + __compatible_joinable_ranges, _Pattern> +template +struct join_with_view<_View, _Pattern>::__sentinel { +private: + friend join_with_view; + + using _Parent = __maybe_const<_Const, join_with_view>; + using _Base = __maybe_const<_Const, _View>; + + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {} + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __s) + requires _Const && convertible_to, sentinel_t<_Base>> + : __end_(std::move(__s.__end_)) {} + + template + requires sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__get_outer() == __y.__end_; + } +}; + +namespace views { +namespace __join_with_view { +struct __fn { + template + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const + noexcept(noexcept(/**/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)))) + -> decltype(/*--*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) { + return /*-------------*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)); + } + + template + requires constructible_from, _Pattern> + _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v, _Pattern>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern))); + } +}; +} // namespace __join_with_view + +inline namespace __cpo { +inline constexpr auto join_with = __join_with_view::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_JOIN_WITH_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 9ffccf66ff0948..ddec16d3a26b7c 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1726,6 +1726,10 @@ module std_private_ranges_join_view [system] { export std_private_iterator_iterator_with_data export std_private_iterator_segmented_iterator } +module std_private_ranges_join_with_view [system] { + header "__ranges/join_with_view.h" + export std_variant +} module std_private_ranges_lazy_split_view [system] { header "__ranges/lazy_split_view.h" export std_private_ranges_non_propagating_cache diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 07a525ed8641fd..8a2dbf1d117779 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -285,6 +285,18 @@ namespace std::ranges { requires view && input_range> class join_view; + // [range.join.with], join with view + template + concept compatible-joinable-ranges = see below; // exposition only + + template + requires view && input_range> + && view + && compatible-joinable-ranges, Pattern> + class join_with_view; // since C++23 + + namespace views { inline constexpr unspecified join_with = unspecified; } // since C++23 + // [range.lazy.split], lazy split view template concept tiny-range = see below; // exposition only @@ -401,6 +413,7 @@ namespace std { #include <__ranges/from_range.h> #include <__ranges/iota_view.h> #include <__ranges/join_view.h> +#include <__ranges/join_with_view.h> #include <__ranges/lazy_split_view.h> #include <__ranges/rbegin.h> #include <__ranges/ref_view.h> diff --git a/libcxx/include/version b/libcxx/include/version index cac6eaa3b6e880..4925cf2ae9ed6f 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -477,7 +477,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_ranges_chunk_by 202202L # define __cpp_lib_ranges_contains 202207L // # define __cpp_lib_ranges_iota 202202L -// # define __cpp_lib_ranges_join_with 202202L +# define __cpp_lib_ranges_join_with 202202L # define __cpp_lib_ranges_repeat 202207L // # define __cpp_lib_ranges_slide 202202L # define __cpp_lib_ranges_starts_ends_with 202106L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index f71efe948ede10..eebb2a450c15d1 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -223,13 +223,16 @@ export namespace std { namespace views { using std::ranges::views::join; } // namespace views -#if 0 + +#if _LIBCPP_STD_VER >= 23 + // [range.join.with] using std::ranges::join_with_view; namespace views { using std::ranges::views::join_with; } // namespace views -#endif +#endif // _LIBCPP_STD_VER >= 23 + using std::ranges::lazy_split_view; // [range.split], split view diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp new file mode 100644 index 00000000000000..58abf717d7bbeb --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::iterator::operator* is marked as [[nodiscard]]. + +#include +#include + +void test() { + char range[3][2] = {{'x', 'x'}, {'y', 'y'}, {'z', 'z'}}; + char pattern[2] = {',', ' '}; + + std::ranges::join_with_view view(range, pattern); + + // clang-format off + *view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + *std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp new file mode 100644 index 00000000000000..78b6332f778e13 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::iterator::operator== is marked as [[nodiscard]]. + +#include +#include + +void test() { + char16_t range[3][1] = {{u'x'}, {u'y'}, {u'z'}}; + char16_t pattern[1] = {u'-'}; + + std::ranges::join_with_view view(range, pattern); + + // clang-format off + (view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp new file mode 100644 index 00000000000000..1e4a92f827572f --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::iterator::iter_move is marked as [[nodiscard]]. + +#include +#include + +void test() { + long range[2][1] = {{0L}, {2L}}; + long pattern[1] = {1L}; + + std::ranges::join_with_view view(range, pattern); + + // clang-format off + iter_move(view.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + iter_move(std::as_const(view).begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp new file mode 100644 index 00000000000000..05074cbb313a0b --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/no_unique_address.compile.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// XFAIL: msvc && clang-17 + +// + +// This test ensures that we use `[[no_unique_address]]` in `join_with_view::iterator`. + +#include + +#include "test_iterators.h" +#include "test_range.h" + +struct InputView : std::ranges::view_base { + using Inner = test_range; + + cpp20_input_iterator begin(); + sentinel_wrapper> end(); +}; + +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); + +struct Pattern : std::ranges::view_base { + int* begin(); + int* end(); +}; + +static_assert(alignof(void*) == alignof(std::variant)); // alignof(__parent_) == alignof(__inner_it_) +static_assert(sizeof(std::ranges::iterator_t>) == + sizeof(void*) + sizeof(std::variant)); // sizeof(__parent_) + sizeof(__inner_it_) diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp new file mode 100644 index 00000000000000..bd2972f938648c --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::views::join_with is marked as [[nodiscard]]. + +#include + +void test() { + int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + int pattern_base[2] = {-1, -1}; + auto pattern = std::views::all(pattern_base); + + // clang-format off + std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::join_with(range, pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + range | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::reverse | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::join_with(range, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + range | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::reverse | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp new file mode 100644 index 00000000000000..8cd234b85ad1d0 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::sentinel::operator== is marked as [[nodiscard]]. + +#include +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +void test() { + std::array, 0> range; + std::array pattern; + + std::ranges::join_with_view view(range, pattern); + static_assert(!std::ranges::common_range); + + // clang-format off + (view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp new file mode 100644 index 00000000000000..b08b9f9b8bc0da --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::base is marked as [[nodiscard]]. + +#include +#include + +void test() { + int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + int pattern[2] = {-1, -1}; + + std::ranges::join_with_view view(range, pattern); + + // clang-format off + view.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(std::as_const(view)).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp new file mode 100644 index 00000000000000..6d930d46be77ac --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::begin is marked as [[nodiscard]]. + +#include +#include + +void test() { + int range[3][2] = {{1, 3}, {4, 6}, {7, 9}}; + int pattern[1] = {-2}; + + std::ranges::join_with_view view(range, pattern); + + // clang-format off + view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp new file mode 100644 index 00000000000000..1d5f0e394d67f0 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::ranges::join_with_view::end is marked as [[nodiscard]]. + +#include +#include + +void test() { + int range[3][2] = {{1, 2}, {4, 5}, {7, 8}}; + int pattern[1] = {-3}; + + std::ranges::join_with_view view(range, pattern); + + // clang-format off + view.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(view).end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // clang-format on +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp new file mode 100644 index 00000000000000..495a453a898089 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/no_unique_address.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: msvc + +// + +// This test ensures that we use `[[no_unique_address]]` in `join_with_view`. + +#include +#include + +struct ForwardView : std::ranges::view_base { + std::string* begin() const; + std::string* end() const; +}; + +static_assert(std::ranges::forward_range); +static_assert(std::is_reference_v>); + +struct Pattern : std::ranges::view_base { + char* begin() const; + char* end() const; +}; + +template +struct Test { + [[no_unique_address]] View view; + char c; +}; + +static_assert(sizeof(Test>) == + sizeof(std::ranges::join_with_view)); diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv index c720a0c9a05ec7..78f0859321cec1 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -666,6 +666,7 @@ ranges optional ranges span ranges tuple ranges type_traits +ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv index b00436a1be7f7c..0f290762a96680 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -671,6 +671,7 @@ ranges optional ranges span ranges tuple ranges type_traits +ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv index 4b8c12929de0d6..5aebe38c2d481c 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -674,6 +674,7 @@ ranges optional ranges span ranges tuple ranges type_traits +ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv index a51a7e2de1d380..2a8a67ce5028a6 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -675,6 +675,7 @@ ranges optional ranges span ranges tuple ranges type_traits +ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv index 5280d75cbf2e1f..c4cf997c69208e 100644 --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -682,6 +682,7 @@ ranges optional ranges span ranges tuple ranges type_traits +ranges variant ranges version ratio climits ratio cstdint 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 30feacd796d8e1..0704a87bb9ad07 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 @@ -293,17 +293,11 @@ # error "__cpp_lib_ranges_concat should not be defined before c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should be defined in c++23" -# endif -# if __cpp_lib_ranges_join_with != 202202L -# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_join_with +# error "__cpp_lib_ranges_join_with should be defined in c++23" +# endif +# if __cpp_lib_ranges_join_with != 202202L +# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23" # endif # ifndef __cpp_lib_ranges_repeat @@ -421,17 +415,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should be defined in c++26" -# endif -# if __cpp_lib_ranges_join_with != 202202L -# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_join_with +# error "__cpp_lib_ranges_join_with should be defined in c++26" +# endif +# if __cpp_lib_ranges_join_with != 202202L +# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26" # endif # ifndef __cpp_lib_ranges_repeat 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 32ed30e21cbbe3..8ba7ca5e6027c7 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 @@ -5576,17 +5576,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should be defined in c++23" -# endif -# if __cpp_lib_ranges_join_with != 202202L -# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_join_with +# error "__cpp_lib_ranges_join_with should be defined in c++23" +# endif +# if __cpp_lib_ranges_join_with != 202202L +# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23" # endif # ifndef __cpp_lib_ranges_repeat @@ -7378,17 +7372,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should be defined in c++26" -# endif -# if __cpp_lib_ranges_join_with != 202202L -# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_join_with -# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_join_with +# error "__cpp_lib_ranges_join_with should be defined in c++26" +# endif +# if __cpp_lib_ranges_join_with != 202202L +# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26" # endif # ifndef __cpp_lib_ranges_repeat diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp new file mode 100644 index 00000000000000..dfd1d9ce82f0c2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// iterator() = default; + +#include + +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_comparisons.h" +#include "test_iterators.h" + +constexpr bool test() { + { // `V` and `Pattern` model forward range + using Inner = BasicVectorView; + using V = BasicVectorView; + using Pattern = Inner; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using ConstIter = std::ranges::iterator_t; + + Iter iter; + assert(testEquality(iter, Iter{}, true)); + + ConstIter citer; + assert(testEquality(citer, ConstIter{}, true)); + assert(testEquality(iter, citer, true)); + + std::ranges::join_with_view jwv(V{Inner{1, 2}, Inner{2, 1}}, Pattern{3, 3}); + Iter jwv_iter = jwv.begin(); + ConstIter jwv_citer = std::as_const(jwv).begin(); + assert(testEquality(jwv_iter, jwv_citer, true)); + + assert(testEquality(jwv_iter, iter, false)); + assert(testEquality(jwv_iter, citer, false)); + assert(testEquality(jwv_citer, iter, false)); + assert(testEquality(jwv_citer, citer, false)); + } + + { // `InnerIter` is not default constructible (does not model forward iterator, JWV cannot be const-accessed) + using Inner = BasicVectorView; + using V = BasicVectorView; + using Pattern = BasicVectorView; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + + Iter iter; + assert(testEquality(iter, Iter{}, true)); + + std::ranges::join_with_view jwv(V{Inner{'a', 'b'}, Inner{'c', 'd'}}, Pattern{',', ' '}); + Iter jwv_iter = jwv.begin(); + assert(testEquality(jwv_iter, iter, false)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp new file mode 100644 index 00000000000000..16d31618c93dc8 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr iterator(iterator i) +// requires Const && convertible_to, OuterIter> && +// convertible_to, InnerIter> && +// convertible_to, PatternIter>; + +#include + +#include +#include + +#include "../types.h" + +constexpr bool test() { + { // Regular conversion from `!Const` to `Const` iterator + std::vector> vec = {{1, 2}, {3, 4}, {5, 6}}; + int pattern = 0; + std::ranges::join_with_view jwv(vec, pattern); + auto it = jwv.begin(); + assert(*it == 1); + + using CIter = std::ranges::iterator_t; + const CIter cit1 = it; // `cit1` points to element of `V` + assert(*cit1 == 1); + assert(cit1 == it); + + std::ranges::advance(it, 2); + assert(*it == 0); + CIter cit2 = it; // `cit2` points to element of `Pattern` + assert(*cit2 == 0); + assert(cit2 == it); + + ++it; + assert(*it == 3); + CIter cit3 = it; + assert(*cit3 == 3); + assert(cit3 == it); + + --cit3; + assert(cit2 == cit3); + } + + { // Test conversion from `Const` to `!Const` (should be invalid) + using V = std::vector>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view, Pattern>; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!std::convertible_to); + static_assert(!std::constructible_from); + } + + { // When `convertible_to, OuterIter>` is not modeled + using Inner = std::vector; + using V = ConstOppositeView; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!std::convertible_to); + static_assert(!std::constructible_from); + } + + { // When `convertible_to, InnerIter>` is not modeled + using Inner = ConstOppositeView; + using V = std::vector; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view, Pattern>; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!std::convertible_to); + static_assert(!std::constructible_from); + } + + { // When `convertible_to, PatternIter>` is not modeled + using V = std::vector>; + using Pattern = ConstOppositeView; + using JWV = std::ranges::join_with_view, Pattern>; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!std::convertible_to); + static_assert(!std::constructible_from); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp new file mode 100644 index 00000000000000..8bb6d71917421d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp @@ -0,0 +1,283 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr iterator& operator--() +// requires ref-is-glvalue && bidirectional_range && +// bidirectional-common && bidirectional-common; +// constexpr iterator operator--(int) +// requires ref-is-glvalue && bidirectional_range && +// bidirectional-common && bidirectional-common; + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" + +template +concept CanPreDecrement = requires(I& i) { + { --i } -> std::same_as; +}; + +template +concept CanPostDecrement = requires(I& i) { + { i-- } -> std::same_as; +}; + +template +concept CanDecrement = CanPreDecrement && CanPostDecrement; + +constexpr bool test() { + { // `V` and `Pattern` are not empty. Test return type too. + using V = std::ranges::owning_view>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(CanDecrement); + static_assert(CanDecrement); + + JWV jwv(V{{"01", "23", "45"}}, Pattern{'_'}); + + { + auto it = jwv.end(); + std::same_as decltype(auto) it_ref = --it; + assert(it_ref == it); + assert(*it == '5'); + std::same_as decltype(auto) it_copy = it--; + assert(--it_copy == it); + --it; + assert(*it == '_'); + it--; + assert(*it == '3'); + --it; + it--; + assert(*it == '_'); + } + + { + auto cit = std::as_const(jwv).end(); + std::same_as decltype(auto) cit_ref = --cit; + assert(cit_ref == cit); + assert(*cit == '5'); + std::same_as decltype(auto) cit_copy = cit--; + assert(--cit_copy == cit); + --cit; + assert(*cit == '_'); + cit--; + assert(*cit == '3'); + --cit; + cit--; + assert(*cit == '_'); + } + + assert(std::ranges::equal(std::views::reverse(std::move(jwv)), std::string_view{"54_32_10"})); + } + + { // `Pattern` is empty, `V` is not. + using Inner = std::array; + using V = std::ranges::owning_view>; + using Pattern = std::ranges::owning_view>; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{{Inner{-9}, Inner{-99}, Inner{-999}}}, Pattern{}); + + { + auto it = jwv.end(); + --it; + assert(*it == -999); + it--; + assert(*it == -99); + --it; + assert(*it == -9); + assert(it == jwv.begin()); + } + + { + auto cit = std::as_const(jwv).end(); + --cit; + assert(*cit == -999); + cit--; + assert(*cit == -99); + --cit; + assert(*cit == -9); + assert(cit == std::as_const(jwv).begin()); + } + } + +#if !defined(TEST_COMPILER_GCC) // GCC c++/101777 + { // `V` has empty subrange in the middle, `Pattern` is not empty. Try to go back and forth. + using V = std::array, 3>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view, Pattern>; + + JWV jwv(V{{{5}, {}, {125}}}, Pattern{1}); + + { + auto it = jwv.end(); + --it; + assert(*it == 125); + it--; + assert(*it == 1); + --it; + assert(*it == 1); + it--; + assert(*it == 5); + ++it; + assert(*it == 1); + --it; + assert(*it == 5); + std::ranges::advance(it, 4); + it--; + assert(*it == 125); + } + + { + auto cit = std::as_const(jwv).end(); + --cit; + assert(*cit == 125); + cit--; + assert(*cit == 1); + --cit; + assert(*cit == 1); + cit--; + assert(*cit == 5); + ++cit; + assert(*cit == 1); + --cit; + assert(*cit == 5); + std::ranges::advance(cit, 4); + cit--; + assert(*cit == 125); + } + } + + { // Only first element of `V` is not empty. `Pattern` is empty. Try to go back and forth. + using Inner = std::vector; + using V = std::ranges::owning_view>; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{{Inner{999}, {}, {}}}, Pattern{}); + + { + auto it = jwv.end(); + --it; + assert(*it == 999); + ++it; + assert(it == jwv.end()); + it--; + assert(*it == 999); + } + + { + auto cit = std::as_const(jwv).end(); + --cit; + assert(*cit == 999); + ++cit; + assert(cit == std::as_const(jwv).end()); + cit--; + assert(*cit == 999); + } + } +#endif // !defined(TEST_COMPILER_GCC) + + { // `ref-is-glvalue` is false + using V = RvalueVector>; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + using Iter = std::ranges::iterator_t; + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + + { // `Base` does not model bidirectional range + using V = std::ranges::owning_view>>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + + { // InnerBase does not model bidirectional-common + { // InnerBase does not model bidirectional range + using V = std::ranges::owning_view>>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + + { // InnerBase does not model common range + using InnerBase = BasicVectorView; + using V = std::ranges::owning_view>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + } + + { // PatternBase does not model bidirectional-common + { // PatternBase does not model bidirectional range + using V = std::ranges::owning_view>>; + using Pattern = std::ranges::owning_view>; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + + { // PatternBase does not model common range + using V = std::ranges::owning_view>>; + using Pattern = BasicVectorView; + using JWV = std::ranges::join_with_view; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp new file mode 100644 index 00000000000000..2491e8bf75f701 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr decltype(auto) operator*() const; + +#include + +#include +#include +#include +#include +#include +#include + +#include "../types.h" + +constexpr bool test() { + { // Result of `operator*` is (maybe const) lvalue reference + using V = std::ranges::owning_view>; + using Pattern = std::ranges::owning_view; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{{"ab", "cd", "ef"}}, Pattern{"><"}); + + { + auto it = jwv.begin(); + std::same_as decltype(auto) v_ref = *std::as_const(it); + assert(v_ref == 'a'); + std::ranges::advance(it, 2); + std::same_as decltype(auto) pattern_ref = *it; + assert(pattern_ref == '>'); + } + + { + auto cit = std::as_const(jwv).begin(); + std::same_as decltype(auto) cv_ref = *cit; + assert(cv_ref == 'a'); + std::ranges::advance(cit, 3); + std::same_as decltype(auto) cpattern_ref = *std::as_const(cit); + assert(cpattern_ref == '<'); + } + } + + { // Result of `operator*` is const lvalue reference + using V = std::ranges::owning_view>; + using Pattern = std::string_view; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{{"123", "456", "789"}}, Pattern{"._."}); + + { + auto it = jwv.begin(); + std::same_as decltype(auto) v_ref = *it; + assert(v_ref == '1'); + std::ranges::advance(it, 3); + std::same_as decltype(auto) pattern_ref = *std::as_const(it); + assert(pattern_ref == '.'); + } + + { + auto cit = std::as_const(jwv).begin(); + std::same_as decltype(auto) cv_ref = *std::as_const(cit); + assert(cv_ref == '1'); + std::ranges::advance(cit, 4); + std::same_as decltype(auto) cpattern_ref = *cit; + assert(cpattern_ref == '_'); + } + } + + { // Result of `operator*` is prvalue + using V = std::vector; + using Pattern = RvalueVector; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + JWV jwv(V{"x^2", "y^2", "z^2"}, Pattern{{' ', '+', ' '}}); + + { + auto it = jwv.begin(); + std::same_as decltype(auto) v_ref = *std::as_const(it); + assert(v_ref == 'x'); + std::ranges::advance(it, 3); + std::same_as decltype(auto) pattern_ref = *it; + assert(pattern_ref == ' '); + } + + { + auto cit = std::as_const(jwv).begin(); + std::same_as decltype(auto) cv_ref = *cit; + assert(cv_ref == 'x'); + std::ranges::advance(cit, 4); + std::same_as decltype(auto) cpattern_ref = *std::as_const(cit); + assert(cpattern_ref == '+'); + } + } + + { // Result of `operator*` is (maybe const) rvalue reference + using Inner = std::ranges::as_rvalue_view>; + using V = std::ranges::owning_view>; + using Pattern = std::ranges::as_rvalue_view>>; + using JWV = std::ranges::join_with_view; + + std::vector vec; + vec.emplace_back(Inner{{"x*y"}}); + vec.emplace_back(Inner{{"y*z"}}); + vec.emplace_back(Inner{{"z*x"}}); + JWV jwv(V(std::move(vec)), Pattern(std::array{',', ' '})); + + { + auto it = jwv.begin(); + std::same_as decltype(auto) v_ref = *it; + assert(v_ref == 'x'); + std::ranges::advance(it, 3); + std::same_as decltype(auto) pattern_ref = *std::as_const(it); + assert(pattern_ref == ','); + } + + { + auto cit = std::as_const(jwv).begin(); + std::same_as decltype(auto) cv_ref = *std::as_const(cit); + assert(cv_ref == 'x'); + std::ranges::advance(cit, 4); + std::same_as decltype(auto) cpattern_ref = *cit; + assert(cpattern_ref == ' '); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp new file mode 100644 index 00000000000000..f02e0c9169fd16 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp @@ -0,0 +1,256 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// friend constexpr bool operator==(const iterator& x, const iterator& y) +// requires ref-is-glvalue && forward_range && +// equality_comparable; + +#include + +#include +#include +#include + +#include "../types.h" +#include "test_comparisons.h" + +template +concept CanEq = requires(const I1& i1, const I2& i2) { + { i1 == i2 } -> std::same_as; + { i2 == i1 } -> std::same_as; + { i1 != i2 } -> std::same_as; + { i2 != i1 } -> std::same_as; +}; + +constexpr bool test() { + { // `V` and `Pattern` are not empty. Test return types too. + using V = std::array, 3>; + using Pattern = std::array; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(CanEq); + static_assert(CanEq); + static_assert(CanEq); + + JWV jwv(V{{{9, 8}, {7, 6}, {5, 4}}}, Pattern{0L}); + + Iter it1 = jwv.begin(); + assert(*it1 == 9); + assert(testEquality(it1, it1, true)); + + Iter it2 = std::ranges::prev(jwv.end()); + assert(*it2 == 4); + assert(testEquality(it2, it2, true)); + assert(testEquality(it1, it2, false)); + + CIter cit1 = std::as_const(jwv).begin(); + assert(*cit1 == 9); + assert(testEquality(cit1, cit1, true)); + assert(testEquality(it1, cit1, true)); + assert(testEquality(it2, cit1, false)); + + CIter cit2 = std::ranges::prev(std::as_const(jwv).end()); + assert(*cit2 == 4); + assert(testEquality(cit2, cit2, true)); + assert(testEquality(cit1, cit2, false)); + assert(testEquality(it1, cit2, false)); + assert(testEquality(it2, cit2, true)); + + // `it1.inner_it_` and `it2.inner_it_` are equal, but `it1.outer_it_` and `it2.outer_it_` are not. + std::ranges::advance(it1, 2); + assert(*it1 == 0); + std::ranges::advance(it2, -2); + assert(*it2 == 0); + assert(testEquality(it1, it2, false)); + + // `cit1.inner_it_` and `cit2.inner_it_` are equal, but `cit1.outer_it_` and `cit2.outer_it_` are not. + std::ranges::advance(cit1, 2); + assert(*cit1 == 0); + assert(testEquality(it1, cit1, true)); + std::ranges::advance(cit2, -2); + assert(*cit2 == 0); + assert(testEquality(it2, cit2, true)); + assert(testEquality(cit1, cit2, false)); + + // `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too. + // `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 1. + ++it1; + assert(*it1 == 7); + std::ranges::advance(it2, -2); + assert(*it2 == 7); + assert(testEquality(it1, it2, true)); + + // `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too. + // `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 1. + ++cit1; + assert(*cit1 == 7); + assert(testEquality(it1, cit1, true)); + std::ranges::advance(cit2, -2); + assert(*cit2 == 7); + assert(testEquality(it2, cit2, true)); + assert(testEquality(cit1, cit2, true)); + + // `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too. + // `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 0. + --it1; + assert(*it1 == 0); + --it2; + assert(*it2 == 0); + assert(testEquality(it1, it2, true)); + + // `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too. + // `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 0. + --cit1; + assert(*cit1 == 0); + assert(testEquality(it1, cit1, true)); + --cit2; + assert(*cit2 == 0); + assert(testEquality(it2, cit2, true)); + assert(testEquality(cit2, cit2, true)); + } + + { // `InnerIter` models input iterator and equality comparable. `Pattern` is empty. + using Inner = BasicVectorView; + using V = std::vector; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(CanEq); + static_assert(CanEq); + static_assert(!CanEq); + + JWV jwv(V{Inner{1, 2}, Inner{5, 6}, Inner{9, 0}}, Pattern{}); + + { + Iter it1 = jwv.begin(); + assert(*it1 == 1); + Iter it2 = std::ranges::next(jwv.begin(), 2); + assert(*it2 == 5); + assert(testEquality(it1, it2, false)); + ++it1; + ++it1; + assert(testEquality(it1, it2, true)); + ++it1; + assert(testEquality(it1, it2, false)); + } + + { + CIter cit1 = std::as_const(jwv).begin(); + assert(*cit1 == 1); + CIter cit2 = std::ranges::next(std::as_const(jwv).begin(), 2); + assert(*cit2 == 5); + assert(testEquality(cit1, cit2, false)); + ++cit1; + ++cit1; + assert(testEquality(cit1, cit2, true)); + ++cit1; + assert(testEquality(cit1, cit2, false)); + } + } + + { // `Pattern` is not empty. Some elements of `V` are. + using Inner = BasicVectorView; + using V = BasicVectorView; + using Pattern = BasicVectorView; + using JWV = std::ranges::join_with_view; + + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(CanEq); + static_assert(CanEq); + static_assert(!CanEq); + + JWV jwv(V{Inner{1}, Inner{}, Inner{27}}, Pattern{0}); + + { + Iter it1 = jwv.begin(); + assert(*it1 == 1); + ++it1; + assert(*it1 == 0); + Iter it2 = jwv.begin(); + assert(testEquality(it1, it2, false)); + ++it2; + assert(testEquality(it1, it2, true)); + + ++it2; + assert(*it1 == *it2); + assert(testEquality(it1, it2, false)); + + std::ranges::advance(it1, 2); + ++it2; + assert(*it1 == *it2); + assert(testEquality(it1, it2, true)); + } + + { + CIter cit1 = std::as_const(jwv).begin(); + assert(*cit1 == 1); + ++cit1; + assert(*cit1 == 0); + CIter cit2 = std::as_const(jwv).begin(); + assert(testEquality(cit1, cit2, false)); + ++cit2; + assert(testEquality(cit1, cit2, true)); + + ++cit2; + assert(*cit1 == *cit2); + assert(testEquality(cit1, cit2, false)); + + std::ranges::advance(cit1, 2); + ++cit2; + assert(*cit1 == *cit2); + assert(testEquality(cit1, cit2, true)); + } + } + + { // `ref-is-glvalue` is false + using Inner = std::vector; + using V = RvalueVector; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + using Iter = std::ranges::iterator_t; + static_assert(!CanEq); + } + + { // `Base` does not model forward range + using Inner = std::vector; + using V = BasicVectorView; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + using Iter = std::ranges::iterator_t; + static_assert(!CanEq); + } + + { // `InnerIter` does not model equality comparable + using Inner = BasicVectorView; + using V = std::vector; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!CanEq); + static_assert(!CanEq); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp new file mode 100644 index 00000000000000..c100d73aff34b7 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp @@ -0,0 +1,320 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int) +// requires ref-is-glvalue && forward_iterator && +// forward_iterator; + +#include + +#include +#include +#include +#include + +#include "../types.h" + +template +concept CanPreIncrement = requires(I& i) { ++i; }; + +template +concept CanPostIncrement = requires(I& i) { i++; }; + +constexpr void test_pre_increment() { + { // `V` and `Pattern` are not empty. Test return type too. + using V = std::array, 3>; + using Pattern = std::array; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(CanPreIncrement); + static_assert(!CanPreIncrement); + static_assert(CanPreIncrement); + static_assert(!CanPreIncrement); + + JWV jwv(V{{{1, 1}, {2, 2}, {3, 3}}}, Pattern{0, 0}); + + { + auto it = jwv.begin(); + assert(*it == 1); + std::same_as decltype(auto) it_ref = ++it; + assert(it_ref == it); + ++it; + assert(*it == 0); + ++it_ref; + ++it_ref; + assert(*it_ref == 2); + ++it; + ++it_ref; + assert(*it == 0); + } + + { + auto cit = std::as_const(jwv).begin(); + assert(*cit == 1); + std::same_as decltype(auto) cit_ref = ++cit; + assert(cit_ref == cit); + ++cit; + assert(*cit == 0); + ++cit_ref; + ++cit_ref; + assert(*cit_ref == 2); + ++cit; + ++cit_ref; + assert(*cit == 0); + } + } + +#if !defined(TEST_COMPILER_GCC) // GCC c++/101777 + + { // `Pattern` is empty, `V` is not. + using V = std::array, 3>; + using Pattern = std::vector; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + JWV jwv(V{{{-1}, {-2}, {-3}}}, Pattern{}); + + { + auto it = jwv.begin(); + assert(*it == -1); + ++it; + assert(*it == -2); + ++it; + assert(*it == -3); + ++it; + assert(it == jwv.end()); + } + + { + auto cit = std::as_const(jwv).begin(); + assert(*cit == -1); + ++cit; + assert(*cit == -2); + ++cit; + assert(*cit == -3); + ++cit; + assert(cit == std::as_const(jwv).end()); + } + } + + { // `V` has empty subrange in the middle, `Pattern` is not empty. + using V = std::array, 3>; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view, Pattern>; + + JWV jwv(V{{{1}, {}, {3}}}, Pattern{0}); + + { + auto it = jwv.begin(); + assert(*it == 1); + ++it; + assert(*it == 0); + ++it; + assert(*it == 0); + ++it; + assert(*it == 3); + } + + { + auto cit = std::as_const(jwv).begin(); + assert(*cit == 1); + ++cit; + assert(*cit == 0); + ++cit; + assert(*cit == 0); + ++cit; + assert(*cit == 3); + } + } + + { // Only first element of `V` is not empty. `Pattern` is empty. + using V = std::array, 3>; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, Pattern>; + + JWV jwv(V{{{777}, {}, {}}}, Pattern{}); + + { + auto it = jwv.begin(); + assert(*it == 777); + ++it; + assert(it == jwv.end()); + } + + { + auto cit = std::as_const(jwv).begin(); + assert(*cit == 777); + ++cit; + assert(cit == std::as_const(jwv).end()); + } + } + +#endif // !defined(TEST_COMPILER_GCC) + + { // Only last element of `V` is not empty. `Pattern` is empty. `V` models input range and `ref-is-glvalue` is false. + using Inner = RvalueVector; + using V = BasicView; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{{}, {}, {'a'}}, Pattern{}); + + auto it = jwv.begin(); + assert(*it == 'a'); + ++it; + assert(it == jwv.end()); + } + + { // Only first element of `V` is not empty. `Pattern` is not empty. + // `V` models input range and `ref-is-glvalue` is false. + using Inner = RvalueVector; + using V = BasicView; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{{'b'}, {}, {}}, Pattern{'.'}); + + auto it = jwv.begin(); + assert(*it == 'b'); + ++it; + assert(*it == '.'); + ++it; + assert(*it == '.'); + ++it; + assert(it == jwv.end()); + } +} + +constexpr void test_post_increment() { + { // `V` and `Pattern` are not empty. Test return type too. + using V = std::array, 2>; + using Pattern = std::array; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(CanPostIncrement); + static_assert(!CanPostIncrement); + static_assert(CanPostIncrement); + static_assert(!CanPostIncrement); + + JWV jwv(V{{{6, 5, 4}, {3, 2, 1}}}, Pattern{-5}); + + { + auto it = jwv.begin(); + assert(*it == 6); + std::same_as decltype(auto) it_copy = it++; + assert(++it_copy == it); + it++; + it++; + assert(*it == -5); + it_copy++; + it_copy++; + assert(*it_copy == -5); + it++; + it_copy++; + assert(*it == 3); + assert(*it_copy == 3); + } + + { + auto cit = std::as_const(jwv).begin(); + assert(*cit == 6); + std::same_as decltype(auto) cit_copy = cit++; + assert(++cit_copy == cit); + cit++; + cit++; + assert(*cit == -5); + cit_copy++; + cit_copy++; + assert(*cit_copy == -5); + cit++; + cit_copy++; + assert(*cit == 3); + assert(*cit_copy == 3); + } + } + + { // `Pattern` is empty, `V` is not. `ref-is-glvalue` is false. + using Inner = std::vector; + using V = RvalueVector; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + JWV jwv(V{Inner{-3}, Inner{-2}, Inner{-1}}, Pattern{}); + + auto it = jwv.begin(); + assert(*it == -3); + it++; + assert(*it == -2); + it++; + assert(*it == -1); + it++; + assert(it == jwv.end()); + static_assert(std::is_void_v); + } + + { // `V` has empty subrange in the middle, `Pattern` is not empty. OuterIter does not model forward iterator. + using Inner = std::vector; + using V = BasicVectorView; + using Pattern = std::ranges::single_view; + using JWV = std::ranges::join_with_view; + + JWV jwv(V{Inner{7}, {}, Inner{9}}, Pattern{8}); + + auto it = jwv.begin(); + assert(*it == 7); + it++; + assert(*it == 8); + it++; + assert(*it == 8); + it++; + assert(*it == 9); + it++; + assert(it == jwv.end()); + static_assert(std::is_void_v); + } + +#if !defined(TEST_COMPILER_GCC) // GCC c++/101777 + { // Only first element of `V` is not empty. `Pattern` is empty. InnerIter does not model forward iterator. + using Inner = BasicVectorView; + using V = std::array; + using Pattern = std::ranges::empty_view; + using JWV = std::ranges::join_with_view, Pattern>; + + JWV jwv(V{Inner{U'?'}, Inner{}, Inner{}}, Pattern{}); + + auto it = jwv.begin(); + assert(*it == U'?'); + it++; + assert(it == jwv.end()); + static_assert(std::is_void_v); + } +#endif // !defined(TEST_COMPILER_GCC) +} + +constexpr bool test() { + test_pre_increment(); + test_post_increment(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp new file mode 100644 index 00000000000000..147626f1a7ec34 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp @@ -0,0 +1,340 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// friend constexpr decltype(auto) iter_move(const iterator& x) { +// using rvalue_reference = common_reference_t< +// iter_rvalue_reference_t, +// iter_rvalue_reference_t>; +// return visit(ranges::iter_move, x.inner_it_); +// } + +#include + +#include +#include +#include +#include +#include +#include + +#include "../types.h" + +class MoveOnlyInt { +public: + enum Status { constructed, move_constructed, moved_from_this }; + + MoveOnlyInt() = default; + constexpr MoveOnlyInt(int val) : val_(val) {} + + constexpr MoveOnlyInt(MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) { + other.val_ = -1; + other.status_ = moved_from_this; + } + + constexpr MoveOnlyInt(const MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) { + other.val_ = -1; + other.status_ = moved_from_this; + } + + MoveOnlyInt(const MoveOnlyInt&) { assert(false); } // Should never be called in this test. + + MoveOnlyInt& operator=(MoveOnlyInt&&) { // Should never be called in this test. + assert(false); + return *this; + } + + constexpr Status get_status() const { return status_; } + + friend constexpr bool operator==(const MoveOnlyInt& left, int right) { return left.val_ == right; } + friend constexpr bool operator==(const MoveOnlyInt& left, const MoveOnlyInt& right) { + return left.val_ == right.val_; + } + +private: + mutable int val_ = -1; + mutable Status status_ = constructed; +}; + +static_assert(std::movable); + +template +class ProxyRvalue { + T val_; + +public: + constexpr ProxyRvalue(T val) : val_(std::move(val)) {} + + ProxyRvalue(ProxyRvalue&&) = default; + ProxyRvalue& operator=(ProxyRvalue&&) = default; + + constexpr explicit operator T&&() noexcept { return std::move(val_); } +}; + +static_assert(std::common_reference_with, int>); +static_assert(std::common_reference_with, MoveOnlyInt>); + +template +class ProxyOnIterMoveIter { + It it_ = It(); + +public: + using value_type = std::iter_value_t; + using difference_type = std::iter_difference_t; + + ProxyOnIterMoveIter() = default; + constexpr ProxyOnIterMoveIter(It it) : it_(std::move(it)) {} + + constexpr decltype(auto) operator*() const { return *it_; } + + constexpr ProxyOnIterMoveIter& operator++() { + ++it_; + return *this; + } + + constexpr ProxyOnIterMoveIter operator++(int) { + ProxyOnIterMoveIter copy = *this; + ++it_; + return copy; + } + + constexpr ProxyOnIterMoveIter& operator--() { + --it_; + return *this; + } + + constexpr ProxyOnIterMoveIter operator--(int) { + ProxyOnIterMoveIter copy = *this; + --it_; + return copy; + } + + friend bool operator==(const ProxyOnIterMoveIter&, const ProxyOnIterMoveIter&) = default; + + friend constexpr ProxyRvalue iter_move(const ProxyOnIterMoveIter iter) { + return ProxyRvalue{std::ranges::iter_move(iter.it_)}; + } +}; + +template +ProxyOnIterMoveIter(It) -> ProxyOnIterMoveIter; + +static_assert(std::bidirectional_iterator>); + +constexpr bool test() { + { // Test `iter_move` when result is true rvalue reference. Test return types. + using V = std::array, 2>; + using Pattern = std::array; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + JWV jwv(V{{{'0'}, {'1'}}}, Pattern{','}); + + { + auto it = jwv.begin(); + std::same_as decltype(auto) v_rref1 = iter_move(it); + std::same_as decltype(auto) v_rref2 = iter_move(std::as_const(it)); + std::same_as decltype(auto) v_rref3 = std::ranges::iter_move(it); + std::same_as decltype(auto) v_rref4 = std::ranges::iter_move(std::as_const(it)); + assert(std::ranges::equal(std::array{v_rref1, v_rref2, v_rref3, v_rref4}, std::views::repeat('0', 4))); + + ++it; // `it` points to element of `Pattern` from here + std::same_as decltype(auto) pattern_rref1 = iter_move(it); + std::same_as decltype(auto) pattern_rref2 = iter_move(std::as_const(it)); + std::same_as decltype(auto) pattern_rref3 = std::ranges::iter_move(it); + std::same_as decltype(auto) pattern_rref4 = std::ranges::iter_move(std::as_const(it)); + assert(std::ranges::equal( + std::array{pattern_rref1, pattern_rref2, pattern_rref3, pattern_rref4}, std::views::repeat(',', 4))); + } + + { + auto cit = std::prev(std::as_const(jwv).end()); + std::same_as decltype(auto) cv_rref1 = iter_move(cit); + std::same_as decltype(auto) cv_rref2 = iter_move(std::as_const(cit)); + std::same_as decltype(auto) cv_rref3 = std::ranges::iter_move(cit); + std::same_as decltype(auto) cv_rref4 = std::ranges::iter_move(std::as_const(cit)); + assert(std::ranges::equal(std::array{cv_rref1, cv_rref2, cv_rref3, cv_rref4}, std::views::repeat('1', 4))); + + cit--; // `cit` points to element of `Pattern` from here + std::same_as decltype(auto) cpattern_rref1 = iter_move(cit); + std::same_as decltype(auto) cpattern_rref2 = iter_move(std::as_const(cit)); + std::same_as decltype(auto) cpattern_rref3 = std::ranges::iter_move(cit); + std::same_as decltype(auto) cpattern_rref4 = std::ranges::iter_move(std::as_const(cit)); + assert(std::ranges::equal( + std::array{cpattern_rref1, cpattern_rref2, cpattern_rref3, cpattern_rref4}, std::views::repeat(',', 4))); + } + } + + { // Test `iter_move` when result is true rvalue reference. Test moving. + using Inner = std::vector; + using V = std::vector; + using Pattern = std::vector; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + V v; + v.reserve(2); + v.emplace_back(std::ranges::to(std::views::iota(0, 4))); + v.emplace_back(std::ranges::to(std::views::iota(12, 16))); + JWV jwv(std::move(v), std::ranges::to(std::views::iota(4, 12))); + assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; })); + + { + using enum MoveOnlyInt::Status; + std::vector values; + values.reserve(8); + + auto it = jwv.begin(); + values.emplace_back(iter_move(it)); + ++it; + values.emplace_back(iter_move(std::as_const(it))); + it++; + values.emplace_back(std::ranges::iter_move(it)); + ++it; + values.emplace_back(std::ranges::iter_move(std::as_const(it))); + it++; // `it` points to element of `Pattern` from here + values.emplace_back(iter_move(it)); + ++it; + values.emplace_back(iter_move(std::as_const(it))); + it++; + values.emplace_back(std::ranges::iter_move(it)); + ++it; + values.emplace_back(std::ranges::iter_move(std::as_const(it))); + + assert(std::ranges::equal(values, std::views::iota(0, 8))); + assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; })); + } + + { + using enum MoveOnlyInt::Status; + std::vector values; + values.reserve(8); + + auto cit = std::prev(std::as_const(jwv).end()); + values.emplace_back(iter_move(cit)); + cit--; + values.emplace_back(iter_move(std::as_const(cit))); + --cit; + values.emplace_back(std::ranges::iter_move(cit)); + cit--; + values.emplace_back(std::ranges::iter_move(std::as_const(cit))); + --cit; // `it` points to element of `Pattern` from here + values.emplace_back(iter_move(cit)); + cit--; + values.emplace_back(iter_move(std::as_const(cit))); + --cit; + values.emplace_back(std::ranges::iter_move(cit)); + cit--; + values.emplace_back(std::ranges::iter_move(std::as_const(cit))); + + assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16))); + assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; })); + } + + assert( + std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; })); + } + + { // Test `iter_move` when result is proxy rvalue reference. Test return types and moving. + using Inner = std::vector; + using V = std::vector; + using Pattern = BasicVectorView; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + + using RRef = ProxyRvalue; + static_assert(std::same_as>); + + V v; + v.reserve(2); + v.emplace_back(std::ranges::to(std::views::iota(0, 4))); + v.emplace_back(std::ranges::to(std::views::iota(12, 16))); + JWV jwv(std::move(v), Pattern{std::ranges::to>(std::views::iota(4, 12))}); + assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; })); + + { + using enum MoveOnlyInt::Status; + std::vector values; + values.reserve(8); + + auto it = jwv.begin(); + std::same_as decltype(auto) rref1 = iter_move(it); + values.emplace_back(std::move(rref1)); + ++it; + std::same_as decltype(auto) rref2 = iter_move(std::as_const(it)); + values.emplace_back(rref2); + it++; + std::same_as decltype(auto) rref3 = std::ranges::iter_move(it); + values.emplace_back(rref3); + ++it; + std::same_as decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it)); + values.emplace_back(rref4); + it++; // `it` points to element of `Pattern` from here + std::same_as decltype(auto) rref5 = iter_move(it); + values.emplace_back(rref5); + ++it; + std::same_as decltype(auto) rref6 = iter_move(std::as_const(it)); + values.emplace_back(rref6); + it++; + std::same_as decltype(auto) rref7 = std::ranges::iter_move(it); + values.emplace_back(rref7); + ++it; + std::same_as decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it)); + values.emplace_back(rref8); + + assert(std::ranges::equal(values, std::views::iota(0, 8))); + assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; })); + } + + { + using enum MoveOnlyInt::Status; + std::vector values; + values.reserve(8); + + auto cit = std::prev(std::as_const(jwv).end()); + std::same_as decltype(auto) rref1 = iter_move(cit); + values.emplace_back(rref1); + cit--; + std::same_as decltype(auto) rref2 = iter_move(std::as_const(cit)); + values.emplace_back(rref2); + --cit; + std::same_as decltype(auto) rref3 = std::ranges::iter_move(cit); + values.emplace_back(rref3); + cit--; + std::same_as decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit)); + values.emplace_back(rref4); + --cit; // `it` points to element of `Pattern` from here + std::same_as decltype(auto) rref5 = iter_move(cit); + values.emplace_back(rref5); + cit--; + std::same_as decltype(auto) rref6 = iter_move(std::as_const(cit)); + values.emplace_back(rref6); + --cit; + std::same_as decltype(auto) rref7 = std::ranges::iter_move(cit); + values.emplace_back(rref7); + cit--; + std::same_as decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit)); + values.emplace_back(rref8); + + assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16))); + assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; })); + } + + assert( + std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; })); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp new file mode 100644 index 00000000000000..b4e66b92b38833 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// friend constexpr void iter_swap(const iterator& x, const iterator& y) +// requires indirectly_swappable { +// visit(ranges::iter_swap, x.inner_it_, y.inner_it_); +// } + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +template +concept CanIterSwap = requires(I i) { iter_swap(i); }; + +constexpr bool test() { + { // Test common usage + using V = std::vector; + using Pattern = std::string; + using JWV = std::ranges::join_with_view, std::ranges::owning_view>; + using namespace std::string_view_literals; + + JWV jwv(V{"std", "ranges", "views", "join_with_view"}, Pattern{":: "}); + assert(std::ranges::equal(jwv, "std:: ranges:: views:: join_with_view"sv)); + + auto it = jwv.begin(); + iter_swap(it, std::ranges::next(it, 2)); // Swap elements of the same inner range. + assert(std::ranges::equal(jwv, "dts:: ranges:: views:: join_with_view"sv)); + + std::ranges::advance(it, 3); + iter_swap(std::as_const(it), std::ranges::next(it, 2)); // Swap elements of the pattern. + assert(std::ranges::equal(jwv, "dts ::ranges ::views ::join_with_view"sv)); + + std::ranges::advance(it, 3); + const auto it2 = jwv.begin(); + iter_swap(std::as_const(it), it2); // Swap elements of different inner ranges. + assert(std::ranges::equal(jwv, "rts ::danges ::views ::join_with_view"sv)); + + std::ranges::advance(it, 6); + iter_swap(std::as_const(it), it2); // Swap element from inner range with element from the pattern. + assert(std::ranges::equal(jwv, " tsr::dangesr::viewsr::join_with_view"sv)); + + static_assert(std::is_void_v); + static_assert(std::is_void_v); + static_assert(!CanIterSwap>); + static_assert(!CanIterSwap>); + } + + { // InnerIter and PatternIter don't model indirectly swappable + using JWV = std::ranges::join_with_view, std::string_view>; + static_assert(!CanIterSwap>); + static_assert(!CanIterSwap>); + static_assert(!CanIterSwap>); + static_assert(!CanIterSwap>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp new file mode 100644 index 00000000000000..ab34310211a958 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp @@ -0,0 +1,456 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// using iterator_concept = see below; +// using iterator_category = see below; // not always present +// using value_type = see below; +// using difference_type = see below; + +#include + +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +namespace test_iterator_concept { +template