diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index a89d4038785cd..ab887487387d8 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -376,7 +376,7 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_iota`` ``202202L`` ---------------------------------------------------------- ----------------- - ``__cpp_lib_ranges_join_with`` *unimplemented* + ``__cpp_lib_ranges_join_with`` ``202202L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_repeat`` ``202207L`` ---------------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index 6cbc0baf29487..7032771757411 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -47,6 +47,9 @@ Implemented Papers - P1222R4: A Standard ``flat_set`` (`Github `__) - P2897R7: ``aligned_accessor``: An mdspan accessor expressing pointer over-alignment (`Github `__) - P3247R2: Deprecate the notion of trivial types (`Github `__) +- P2441R2: ``views::join_with`` (`Github `__) +- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github `__) +- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github `__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index c26363bcda796..574675175a4cf 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -47,7 +47,7 @@ "`P2273R3 `__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","" "`P2387R3 `__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","" "`P2440R1 `__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","Only ``ranges::iota`` is implemented." -"`P2441R2 `__","``views::join_with``","2022-02 (Virtual)","|In Progress|","","" +"`P2441R2 `__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","" "`P2442R1 `__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","" "`P2443R1 `__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","" "","","","","","" @@ -103,9 +103,9 @@ "`P2708R1 `__","No Further Fundamentals TSes","2022-11 (Kona)","|Nothing To Do|","","" "","","","","","" "`P0290R4 `__","``apply()`` for ``synchronized_value``","2023-02 (Issaquah)","","","" -"`P2770R0 `__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Partial|","","``join_with_view`` hasn't been done yet since this type isn't implemented yet" +"`P2770R0 `__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Complete|","21","" "`P2164R9 `__","``views::enumerate``","2023-02 (Issaquah)","","","" -"`P2711R1 `__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|In Progress|","","``join_with_view`` hasn't been done yet since this type isn't implemented yet" +"`P2711R1 `__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|Complete|","21","" "`P2609R3 `__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","Implemented as a DR in C++20. Other implementations will do the same." "`P2713R1 `__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19","" "`P2675R1 `__","``format``'s width estimation is too approximate and not forward compatible","2023-02 (Issaquah)","|Complete|","17","" diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index fdf381862d87b..d3feecf6513e4 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -66,7 +66,7 @@ "`LWG4060 `__","``submdspan`` preconditions do not forbid creating invalid pointer","2024-06 (St. Louis)","","","" "`LWG4061 `__","Should ``std::basic_format_context`` be default-constructible/copyable/movable?","2024-06 (St. Louis)","|Complete|","19","" "`LWG4071 `__","``reference_wrapper`` comparisons are not SFINAE-friendly","2024-06 (St. Louis)","|Complete|","19","" -"`LWG4074 `__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","","","" +"`LWG4074 `__","``compatible-joinable-ranges`` is underconstrained","2024-06 (St. Louis)","|Complete|","21","" "`LWG4076 `__","``concat_view`` should be freestanding","2024-06 (St. Louis)","","","" "`LWG4079 `__","Missing Preconditions in ``concat_view::iterator``\`s conversion constructor","2024-06 (St. Louis)","","","" "`LWG4082 `__","``views::concat(r)`` is well-formed when ``r`` is an ``output_range``","2024-06 (St. Louis)","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 52611e43968bc..1632a8f6e10fe 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -706,6 +706,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/concepts.h b/libcxx/include/__ranges/concepts.h index 674a3f359ff99..bf75fe8a6fef4 100644 --- a/libcxx/include/__ranges/concepts.h +++ b/libcxx/include/__ranges/concepts.h @@ -10,7 +10,9 @@ #ifndef _LIBCPP___RANGES_CONCEPTS_H #define _LIBCPP___RANGES_CONCEPTS_H +#include <__concepts/common_reference_with.h> #include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> #include <__concepts/movable.h> #include <__concepts/same_as.h> #include <__config> @@ -25,6 +27,8 @@ #include <__ranges/enable_view.h> #include <__ranges/size.h> #include <__type_traits/add_pointer.h> +#include <__type_traits/common_reference.h> +#include <__type_traits/common_type.h> #include <__type_traits/is_reference.h> #include <__type_traits/remove_cvref.h> #include <__type_traits/remove_reference.h> @@ -133,6 +137,42 @@ concept viewable_range = (is_lvalue_reference_v<_Tp> || (movable> && !__is_std_initializer_list>)))); +# if _LIBCPP_STD_VER >= 23 + +template +using __concat_reference_t _LIBCPP_NODEBUG = common_reference_t...>; + +template +using __concat_value_t _LIBCPP_NODEBUG = common_type_t...>; + +template +using __concat_rvalue_reference_t _LIBCPP_NODEBUG = common_reference_t...>; + +template +concept __concat_indirectly_readable_impl = requires(const _It __it) { + { *__it } -> convertible_to<_Ref>; + { ranges::iter_move(__it) } -> convertible_to<_RRef>; +}; + +template +concept __concat_indirectly_readable = + common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&> && + common_reference_with<__concat_reference_t<_Rs...>&&, __concat_rvalue_reference_t<_Rs...>&&> && + common_reference_with<__concat_rvalue_reference_t<_Rs...>&&, const __concat_value_t<_Rs...>&> && + (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>, + __concat_rvalue_reference_t<_Rs...>, + iterator_t<_Rs>> && + ...); + +template +concept __concatable = requires { + typename __concat_reference_t<_Rs...>; + typename __concat_value_t<_Rs...>; + typename __concat_rvalue_reference_t<_Rs...>; +} && __concat_indirectly_readable<_Rs...>; + +# endif // _LIBCPP_STD_VER >= 23 + } // namespace ranges #endif // _LIBCPP_STD_VER >= 20 diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h new file mode 100644 index 0000000000000..8ed989a664687 --- /dev/null +++ b/libcxx/include/__ranges/join_with_view.h @@ -0,0 +1,460 @@ +// -*- 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 <__concepts/equality_comparable.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 __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>; + +template + requires view<_View> && input_range> && view<_Pattern> && + __concatable, _Pattern> +class join_with_view : public view_interface> { + using _InnerRng _LIBCPP_NODEBUG = range_reference_t<_View>; + + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + + static constexpr bool _UseOuterItCache = !forward_range<_View>; + using _OuterItCache _LIBCPP_NODEBUG = + _If<_UseOuterItCache, __non_propagating_cache>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _OuterItCache __outer_it_; + + static constexpr bool _UseInnerCache = !is_reference_v<_InnerRng>; + using _InnerCache _LIBCPP_NODEBUG = + _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))) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr 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}; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires forward_range && forward_range && + is_reference_v> && input_range> && + __concatable, const _Pattern> + { + return __iterator{*this, ranges::begin(__base_)}; + } + + [[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}; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires forward_range && forward_range && + is_reference_v> && input_range> && + __concatable, const _Pattern> + { + 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<_InnerBase> && forward_range<_Base> && forward_range<_InnerBase> +struct __join_with_view_iterator_category<_Base, _PatternBase, _InnerBase> { +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<_InnerBase> && + 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> && + __concatable, _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 _LIBCPP_NODEBUG = __maybe_const<_Const, join_with_view>; + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + using _InnerBase _LIBCPP_NODEBUG = range_reference_t<_Base>; + using _PatternBase _LIBCPP_NODEBUG = __maybe_const<_Const, _Pattern>; + + using _OuterIter _LIBCPP_NODEBUG = iterator_t<_Base>; + using _InnerIter _LIBCPP_NODEBUG = iterator_t<_InnerBase>; + using _PatternIter _LIBCPP_NODEBUG = 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 _LIBCPP_NODEBUG = _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(); + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() { + if constexpr (forward_range<_Base>) + return __outer_it_; + else + return *__parent_->__outer_it_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const { + if constexpr (forward_range<_Base>) + return __outer_it_; + else + return *__parent_->__outer_it_; + } + + [[nodiscard]] _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([this]() -> decltype(auto) { return *__get_outer(); }); + } + + [[nodiscard]] _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_)); + } + } + } + + [[nodiscard]] 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_))); + } + } + + [[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; + } + + [[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_; + } + + [[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> && + __concatable, _Pattern> +template +struct join_with_view<_View, _Pattern>::__sentinel { +private: + friend join_with_view; + + using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, join_with_view>; + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + + _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {} + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto& __get_outer_of(const __iterator<_OtherConst>& __x) { + return __x.__get_outer(); + } + +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>>> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __get_outer_of(__x) == __y.__end_; + } +}; + +namespace views { +namespace __join_with_view { +struct __fn { + template + [[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> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v, _Pattern>) { + return __pipeable(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.in b/libcxx/include/module.modulemap.in index 7f625cefed1c2..a59c687529e0d 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1872,6 +1872,7 @@ module std [system] { module iota_view { header "__ranges/iota_view.h" } module istream_view { header "__ranges/istream_view.h" } module join_view { header "__ranges/join_view.h" } + module join_with_view { header "__ranges/join_with_view.h" } module lazy_split_view { header "__ranges/lazy_split_view.h" export std.functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 49fea7c3f84ec..2a6321bd2c5d8 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -285,6 +285,15 @@ namespace std::ranges { requires view && input_range> class join_view; + // [range.join.with], join with view + template + requires view && input_range> + && view + && concatable, 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 @@ -427,6 +436,7 @@ namespace std { # include <__ranges/as_rvalue_view.h> # include <__ranges/chunk_by_view.h> # include <__ranges/from_range.h> +# include <__ranges/join_with_view.h> # include <__ranges/repeat_view.h> # include <__ranges/to.h> # include <__ranges/zip_view.h> diff --git a/libcxx/include/version b/libcxx/include/version index 65fae111dc8ed..9b010591e92f2 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -517,7 +517,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_ranges_contains 202207L # define __cpp_lib_ranges_find_last 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 a5e2a2b4583c1..adabeeb22d551 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 0000000000000..97133613bf58a --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..823d8def98085 --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..9e046ef43fda3 --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..6b2abb5c80539 --- /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,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// This test ensures that we use `[[no_unique_address]]` in `join_with_view::iterator`. + +#include +#include +#include + +struct IntRange : std::ranges::view_base { + int* begin(); + int* end(); +}; + +class Iter { +public: + using value_type = IntRange; + using difference_type = ptrdiff_t; + + Iter& operator++(); + void operator++(int); + value_type& operator*() const; + bool operator==(std::default_sentinel_t) const; + +private: + int* ptr_; +}; + +static_assert(std::input_iterator); +static_assert(!std::forward_iterator); + +struct View : std::ranges::view_base { + Iter begin(); + std::default_sentinel_t end(); +}; + +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); + +using JWV = std::ranges::join_with_view; + +// Expected JWV::iterator layout: +// _Parent* __parent_; // offset: 0 +// [[no_unique_address]] __empty __outer_it; // 0 +// variant<_PatternIter, _InnerIter> __pattern_; // sizeof(pointer) +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 0000000000000..3efe77a3765d5 --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..e7e4d262eedb9 --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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.sentinel/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/no_unique_address.compile.pass.cpp new file mode 100644 index 0000000000000..3badac162ce7c --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/no_unique_address.compile.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// XFAIL: msvc + +// + +// This test ensures that we use `[[no_unique_address]]` in `join_with_view::sentinel`. + +#include +#include +#include + +template +struct Iter { + using value_type = std::string_view; + using difference_type = std::ptrdiff_t; + + Iter& operator++(); + Iter operator++(int); + value_type& operator*() const; + bool operator==(const Iter&) const; + bool operator==(std::default_sentinel_t) const; +}; + +struct View : std::ranges::view_base { + Iter begin(); + Iter begin() const; + std::default_sentinel_t end() const; +}; + +using JWV = std::ranges::join_with_view; + +template +struct Test { + [[no_unique_address]] std::ranges::sentinel_t se; + unsigned char pad; +}; + +static_assert(sizeof(Test) == 1); +static_assert(sizeof(Test) == 1); 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 0000000000000..ddf1ebdc5e46c --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..858490a82c75d --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..e57e0ee3f0d06 --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..aa6eeafe4be13 --- /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,47 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// 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; + unsigned char pad; +}; + +using JWV = std::ranges::join_with_view; + +// Expected JWV layout: +// [[no_unique_address]] _View __base_ // offset: 0 +// [[no_unique_address]] __empty_cache __outer_it; // 0 +// [[no_unique_address]] __empty_cache __inner_; // 1 +// [[no_unique_address]] _Patter __pattern_ // 0 +static_assert(sizeof(JWV) == 2); +static_assert(sizeof(Test) == 2); diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index c7c8112e123cd..4cf5178dd7b8f 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 @@ -278,17 +278,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 -# 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 @@ -406,17 +400,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 -# 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 b1cc4afd30696..1301252d7f6a6 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 @@ -5593,17 +5593,11 @@ # error "__cpp_lib_ranges_iota should have the value 202202L in c++23" # 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 -# 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 @@ -7495,17 +7489,11 @@ # error "__cpp_lib_ranges_iota should have the value 202202L in c++26" # 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 -# 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 0000000000000..0d9df493305da --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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; + + // Default constructor of iterator should not be explicit + Iter iter = {}; + assert(testEquality(iter, Iter{}, true)); + + // Default constructor of iterator should not be explicit + 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 0000000000000..0ca31392ed8fa --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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); + + using JWV = decltype(jwv); + using Iter = std::ranges::iterator_t; + using CIter = std::ranges::iterator_t; + static_assert(!std::same_as); + static_assert(std::convertible_to); + static_assert(std::constructible_from); + + Iter it = jwv.begin(); + assert(*it == 1); + + const CIter cit1 = it; // `cit1` points to element of `V`; this constructor should not be explicit + 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 0000000000000..207d60a7296fc --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// 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 0000000000000..b2eeddd2941d6 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp @@ -0,0 +1,225 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// constexpr decltype(auto) operator*() const; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" + +struct ProxyRef { + int& val; +}; + +class CommonProxyRef { +public: + constexpr CommonProxyRef(ProxyRef i) : val(i.val) {} + constexpr CommonProxyRef(int i) : val(i) {} + + constexpr int get() const { return val; } + +private: + int val; +}; + +template