diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -52,6 +52,7 @@ - P1169R4 - ``static operator()`` - P0415R1 - ``constexpr`` for ``std::complex`` - P1208R6 - ``std::source_location`` +- P1035R7 - Input Range Adaptors Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -281,7 +281,7 @@ "`3373 <https://wg21.link/LWG3373>`__","``{to,from}_chars_result``\ and ``format_to_n_result``\ need the ""we really mean what we say"" wording","Prague","|Complete|","14.0","|format|" "`3374 <https://wg21.link/LWG3374>`__","P0653 + P1006 should have made the other ``std::to_address``\ overload ``constexpr``\ ","Prague","|Complete|","12.0" "`3375 <https://wg21.link/LWG3375>`__","``decay``\ in ``viewable_range``\ should be ``remove_cvref``\ ","Prague","|Complete|","15.0","|ranges|" -"`3377 <https://wg21.link/LWG3377>`__","``elements_view::iterator``\ befriends a specialization of itself","Prague","","","|ranges|" +"`3377 <https://wg21.link/LWG3377>`__","``elements_view::iterator``\ befriends a specialization of itself","Prague","|Nothing To Do|","","|ranges|" "`3379 <https://wg21.link/LWG3379>`__","""``safe``\ "" in several library names is misleading","Prague","|Complete|","15.0","|ranges|" "`3380 <https://wg21.link/LWG3380>`__","``common_type``\ and comparison categories","Prague","|Complete|","15.0","|spaceship|" "`3381 <https://wg21.link/LWG3381>`__","``begin``\ and ``data``\ must agree for ``contiguous_range``\ ","Prague","|Nothing To Do|","","|ranges|" diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -108,7 +108,7 @@ "`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0" "`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0" "`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0" -"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors (mostly implemented, TODO: elements_view)","Cologne","|In Progress|","","|ranges|" +"`P1035R7 <https://wg21.link/P1035R7>`__","LWG","Input Range Adaptors","Cologne","|Complete|","16.0","|ranges|" "`P1065R2 <https://wg21.link/P1065R2>`__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0" "`P1135R6 <https://wg21.link/P1135R6>`__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0" "`P1207R4 <https://wg21.link/P1207R4>`__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0","|ranges|" @@ -182,7 +182,7 @@ "`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *","" "`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","* *","" "`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|" -"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","* *","","|ranges|" +"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","Complete","16.0","|ranges|" "`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *","" "`P2045R1 <https://wg21.link/P2045R1>`__","LWG","Missing Mandates for the standard library","Prague","* *","" "`P2085R0 <https://wg21.link/P2085R0>`__","CWG","Consistent defaulted comparisons","Prague","* *","" diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -22,7 +22,7 @@ "`3403 <https://wg21.link/LWG3403>`__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","November 2020","","","|ranges|" "`3404 <https://wg21.link/LWG3404>`__","Finish removing subrange's conversions from pair-like","November 2020","","","|ranges|" "`3405 <https://wg21.link/LWG3405>`__","``common_view``'s converting constructor is bad, too","November 2020","|Complete|","14.0","|ranges|" -"`3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","November 2020","","","|ranges|" +"`3406 <https://wg21.link/LWG3406>`__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","November 2020","|Complete|","16.0","|ranges|" "`3419 <https://wg21.link/LWG3419>`__","[algorithms.requirements]/15 doesn't reserve as many rights as it intends to","November 2020","|Nothing To Do|","" "`3420 <https://wg21.link/LWG3420>`__","cpp17-iterator should check that the type looks like an iterator first","November 2020","|Complete|","14.0","|ranges|" "`3421 <https://wg21.link/LWG3421>`__","Imperfect ADL emulation for boolean-testable","November 2020","|Nothing To Do|","","|ranges|" @@ -53,11 +53,11 @@ "`3391 <https://wg21.link/LWG3391>`__","Problems with ``counted_iterator``/``move_iterator::base() const &``","February 2021","","","|ranges|" "`3433 <https://wg21.link/LWG3433>`__","``subrange::advance(n)`` has UB when ``n < 0``","February 2021","|Complete|","14.0","|ranges|" "`3490 <https://wg21.link/LWG3490>`__","``ranges::drop_while_view::begin()`` is missing a precondition","February 2021","|Nothing To Do|","","|ranges|" -"`3492 <https://wg21.link/LWG3492>`__","Minimal improvements to ``elements_view::iterator``","February 2021","","","|ranges|" +"`3492 <https://wg21.link/LWG3492>`__","Minimal improvements to ``elements_view::iterator``","February 2021","|Complete|","16.0","|ranges|" "`3494 <https://wg21.link/LWG3494>`__","Allow ranges to be conditionally borrowed","February 2021","Superseded by `P2017R1 <https://wg21.link/P2017R1>`__","","|ranges|" "`3495 <https://wg21.link/LWG3495>`__","``constexpr launder`` makes pointers to inactive members of unions usable","February 2021","|Nothing To Do|","" "`3500 <https://wg21.link/LWG3500>`__","``join_view::iterator::operator->()`` is bogus","February 2021","|Complete|","14.0","|ranges|" -"`3502 <https://wg21.link/LWG3502>`__","``elements_view`` should not be allowed to return dangling reference","February 2021","","","|ranges|" +"`3502 <https://wg21.link/LWG3502>`__","``elements_view`` should not be allowed to return dangling reference","February 2021","|Complete|","16.0","|ranges|" "`3505 <https://wg21.link/LWG3505>`__","``split_view::outer-iterator::operator++`` misspecified","February 2021","","","|ranges|" "","","","","","" `2774 <https://wg21.link/LWG2774>`__,"``std::function`` construction vs assignment","June 2021","","" diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -51,7 +51,7 @@ | `[counted.iter.cmp] <https://wg21.link/counted.iter.cmp>`_,| counted_iterator,None,Unassigned,|Not Started| | `[range.iota.iterator] <https://wg21.link/range.iota.iterator>`_,| `ranges::iota_view::iterator <https://reviews.llvm.org/D110774>`_,[concepts.cmp],Arthur O'Dwyer,|Complete| | `[range.transform.iterator] <https://wg21.link/range.transform.iterator>`_,| `ranges::transform_view::iterator <https://reviews.llvm.org/D110774>`_,[concepts.cmp],Arthur O'Dwyer,|Complete| -| `[range.elements.iterator] <https://wg21.link/range.elements.iterator>`_,| ranges::elements_view::iterator,[concepts.cmp],Unassigned,|Not Started| +| `[range.elements.iterator] <https://wg21.link/range.elements.iterator>`_,| ranges::elements_view::iterator,[concepts.cmp],Hui Xie,|Complete| | `[time.duration.comparisons] <https://wg21.link/time.duration.comparisons>`_, "chrono::duration", None, Mark de Wever, |Not Started| | `[time.point.comparisons] <https://wg21.link/time.point.comparisons>`_, "chrono::time_point", None, Mark de Wever, |Not Started| "| `[time.cal.day.nonmembers] <https://wg21.link/time.cal.day.nonmembers>`_ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -363,6 +363,7 @@ __fwd/span.h __fwd/string.h __fwd/string_view.h + __fwd/subrange.h __fwd/tuple.h __hash_table __ios/fpos.h @@ -502,6 +503,7 @@ __ranges/data.h __ranges/drop_view.h __ranges/drop_while_view.h + __ranges/elements_view.h __ranges/empty.h __ranges/empty_view.h __ranges/enable_borrowed_range.h @@ -553,10 +555,12 @@ __tree __tuple_dir/apply_cv.h __tuple_dir/make_tuple_types.h + __tuple_dir/pair_like.h __tuple_dir/sfinae_helpers.h __tuple_dir/tuple_element.h __tuple_dir/tuple_indices.h __tuple_dir/tuple_like.h + __tuple_dir/tuple_like_ext.h __tuple_dir/tuple_size.h __tuple_dir/tuple_types.h __type_traits/add_const.h diff --git a/libcxx/include/__fwd/get.h b/libcxx/include/__fwd/get.h --- a/libcxx/include/__fwd/get.h +++ b/libcxx/include/__fwd/get.h @@ -9,9 +9,11 @@ #ifndef _LIBCPP___FWD_GET_H #define _LIBCPP___FWD_GET_H +#include <__concepts/copyable.h> #include <__config> #include <__fwd/array.h> #include <__fwd/pair.h> +#include <__fwd/subrange.h> #include <__fwd/tuple.h> #include <__tuple_dir/tuple_element.h> #include <cstddef> @@ -90,6 +92,24 @@ get(const array<_Tp, _Size>&&) _NOEXCEPT; #endif +#if _LIBCPP_STD_VER >= 20 + +namespace ranges { + +template <size_t _Index, class _Iter, class _Sent, subrange_kind _Kind> + requires((_Index == 0 && copyable<_Iter>) || _Index == 1) +_LIBCPP_HIDE_FROM_ABI constexpr auto get(const subrange<_Iter, _Sent, _Kind>& __subrange); + +template <size_t _Index, class _Iter, class _Sent, subrange_kind _Kind> + requires(_Index < 2) +_LIBCPP_HIDE_FROM_ABI constexpr auto get(subrange<_Iter, _Sent, _Kind>&& __subrange); + +} // namespace ranges + +using ranges::get; + +#endif // _LIBCPP_STD_VER >= 20 + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___FWD_GET_H diff --git a/libcxx/include/__fwd/subrange.h b/libcxx/include/__fwd/subrange.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__fwd/subrange.h @@ -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 +// +//===---------------------------------------------------------------------===// + +#ifndef _LIBCPP___FWD_SUBRANGE_H +#define _LIBCPP___FWD_SUBRANGE_H + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 20 + +#include <__iterator/concepts.h> + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { + +enum class _LIBCPP_ENUM_VIS subrange_kind : bool { unsized, sized }; + +template <input_or_output_iterator _Iter, sentinel_for<_Iter> _Sent, subrange_kind _Kind> + requires(_Kind == subrange_kind::sized || !sized_sentinel_for<_Sent, _Iter>) +class _LIBCPP_TEMPLATE_VIS subrange; + +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 20 + +#endif // _LIBCPP___FWD_SUBRANGE_H diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/elements_view.h @@ -0,0 +1,423 @@ +// -*- 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_ELEMENTS_VIEW_H +#define _LIBCPP___RANGES_ELEMENTS_VIEW_H + +#include <__compare/three_way_comparable.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/derived_from.h> +#include <__concepts/equality_comparable.h> +#include <__config> +#include <__fwd/get.h> +#include <__iterator/concepts.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__tuple_dir/tuple_element.h> +#include <__tuple_dir/tuple_like.h> +#include <__tuple_dir/tuple_size.h> +#include <__type_traits/is_reference.h> +#include <__type_traits/maybe_const.h> +#include <__type_traits/remove_cv.h> +#include <__type_traits/remove_cvref.h> +#include <__type_traits/remove_reference.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/move.h> +#include <cstddef> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +namespace ranges { + +template <class _View, size_t _Np, bool _Const> +class __elements_view_iterator; + +template <class _View, size_t _Np, bool _Const> +class __elements_view_sentinel; + +template <class _Tp, size_t _Np> +concept __has_tuple_element = __tuple_like<_Tp> && _Np < tuple_size<_Tp>::value; + +template <class _Tp, size_t _Np> +concept __returnable_element = is_reference_v<_Tp> || move_constructible<tuple_element_t<_Np, _Tp>>; + +template <input_range _View, size_t _Np> + requires view<_View> && __has_tuple_element<range_value_t<_View>, _Np> && + __has_tuple_element<remove_reference_t<range_reference_t<_View>>, _Np> && + __returnable_element<range_reference_t<_View>, _Np> +class elements_view : public view_interface<elements_view<_View, _Np>> { +public: + _LIBCPP_HIDE_FROM_ABI elements_view() + requires default_initializable<_View> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit elements_view(_View __base) : __base_(std::move(__base)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator</*_Const=*/false>(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range<const _View> + { + return __iterator</*_Const=*/true>(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View> && !common_range<_View>) + { + return __sentinel</*_Const=*/false>{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View> && common_range<_View>) + { + return __iterator</*_Const=*/false>{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range<const _View> + { + return __sentinel</*_Const=*/true>{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires common_range<const _View> + { + return __iterator</*_Const=*/true>{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range<const _View> + { + return ranges::size(__base_); + } + +private: + template <bool _Const> + using __iterator = __elements_view_iterator<_View, _Np, _Const>; + + template <bool _Const> + using __sentinel = __elements_view_sentinel<_View, _Np, _Const>; + + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); +}; + +template <class, size_t> +struct __elements_view_iterator_category_base {}; + +template <forward_range _Base, size_t _Np> +struct __elements_view_iterator_category_base<_Base, _Np> { + static consteval auto __get_iterator_category() { + using _Result = decltype(std::get<_Np>(*std::declval<iterator_t<_Base>>())); + using _Cat = typename iterator_traits<iterator_t<_Base>>::iterator_category; + + if constexpr (!is_lvalue_reference_v<_Result>) { + return input_iterator_tag{}; + } else if constexpr (derived_from<_Cat, random_access_iterator_tag>) { + return random_access_iterator_tag{}; + } else { + return _Cat{}; + } + } + + using iterator_category = decltype(__get_iterator_category()); +}; + +template <class _View, size_t _Np, bool _Const> +class __elements_view_iterator : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> { + template <class, size_t, bool > + friend class __elements_view_iterator; + + template <class, size_t, bool > + friend class __elements_view_sentinel; + + using _Base = __maybe_const<_Const, _View>; + + iterator_t<_Base> __current_ = iterator_t<_Base>(); + + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_element(const iterator_t<_Base>& __i) { + if constexpr (is_reference_v<range_reference_t<_Base>>) { + return std::get<_Np>(*__i); + } else { + using _Element = remove_cv_t<tuple_element_t<_Np, range_reference_t<_Base>>>; + return static_cast<_Element>(std::get<_Np>(*__i)); + } + } + + static consteval auto __get_iterator_concept() { + if constexpr (random_access_range<_Base>) { + return random_access_iterator_tag{}; + } else if constexpr (bidirectional_range<_Base>) { + return bidirectional_iterator_tag{}; + } else if constexpr (forward_range<_Base>) { + return forward_iterator_tag{}; + } else { + return input_iterator_tag{}; + } + } + +public: + using iterator_concept = decltype(__get_iterator_concept()); + using value_type = remove_cvref_t<tuple_element_t<_Np, range_value_t<_Base>>>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI __elements_view_iterator() + requires default_initializable<iterator_t<_Base>> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __elements_view_iterator(iterator_t<_Base> __current) + : __current_(std::move(__current)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator(__elements_view_iterator<_View, _Np, !_Const> __i) + requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> + : __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); } + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return __get_element(__current_); } + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator++() { + ++__current_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++__current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator operator++(int) + requires forward_range<_Base> + { + auto temp = *this; + ++__current_; + return temp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator--() + requires bidirectional_range<_Base> + { + --__current_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator operator--(int) + requires bidirectional_range<_Base> + { + auto temp = *this; + --__current_; + return temp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator+=(difference_type __n) + requires random_access_range<_Base> + { + __current_ += __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_iterator& operator-=(difference_type __n) + requires random_access_range<_Base> + { + __current_ -= __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const + requires random_access_range<_Base> + { + return __get_element(__current_ + __n); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires equality_comparable<iterator_t<_Base>> + { + return __x.__current_ == __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator<(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires random_access_range<_Base> + { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator>(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator<=(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator>=(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto + operator<=>(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>> + { + return __x.__current_ <=> __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __elements_view_iterator + operator+(const __elements_view_iterator& __x, difference_type __y) + requires random_access_range<_Base> + { + return __elements_view_iterator{__x} += __y; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __elements_view_iterator + operator+(difference_type __x, const __elements_view_iterator& __y) + requires random_access_range<_Base> + { + return __y + __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __elements_view_iterator + operator-(const __elements_view_iterator& __x, difference_type __y) + requires random_access_range<_Base> + { + return __elements_view_iterator{__x} -= __y; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(const __elements_view_iterator& __x, const __elements_view_iterator& __y) + requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>> + { + return __x.__current_ - __y.__current_; + } +}; + +template <class _View, size_t _Np, bool _Const> +class __elements_view_sentinel { +private: + using _Base = __maybe_const<_Const, _View>; + _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + template <class, size_t, bool > + friend class __elements_view_sentinel; + + template <bool _AnyConst> + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) + __get_current(const __elements_view_iterator<_View, _Np, _AnyConst>& __iter) { + return (__iter.__current_); + } + +public: + _LIBCPP_HIDE_FROM_ABI __elements_view_sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __elements_view_sentinel(sentinel_t<_Base> __end) + : __end_(std::move(__end)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __elements_view_sentinel(__elements_view_sentinel<_View, _Np, !_Const> __other) + requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>> + : __end_(std::move(__other.__end_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; } + + template <bool _OtherConst> + requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __elements_view_iterator<_View, _Np, _OtherConst>& __x, const __elements_view_sentinel& __y) { + return __get_current(__x) == __y.__end_; + } + + template <bool _OtherConst> + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __elements_view_iterator<_View, _Np, _OtherConst>& __x, const __elements_view_sentinel& __y) { + return __get_current(__x) - __y.__end_; + } + + template <bool _OtherConst> + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __elements_view_sentinel& __x, const __elements_view_iterator<_View, _Np, _OtherConst>& __y) { + return __x.__end_ - __get_current(__y); + } +}; + +template <class _Tp, size_t _Np> +inline constexpr bool enable_borrowed_range<elements_view<_Tp, _Np>> = enable_borrowed_range<_Tp>; + +template <class _Tp> +using keys_view = elements_view<_Tp, 0>; +template <class _Tp> +using values_view = elements_view<_Tp, 1>; + +namespace views { +namespace __elements { + +template <size_t _Np> +struct __fn : __range_adaptor_closure<__fn<_Np>> { + template <class _Range> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + /**/ noexcept(noexcept(elements_view<all_t<_Range&&>, _Np>(std::forward<_Range>(__range)))) + /*------*/ -> decltype(elements_view<all_t<_Range&&>, _Np>(std::forward<_Range>(__range))) { + /*-------------*/ return elements_view<all_t<_Range&&>, _Np>(std::forward<_Range>(__range)); + } +}; +} // namespace __elements + +inline namespace __cpo { +template <size_t _Np> +inline constexpr auto elements = __elements::__fn<_Np>{}; +inline constexpr auto keys = elements<0>; +inline constexpr auto values = elements<1>; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_ELEMENTS_VIEW_H diff --git a/libcxx/include/__ranges/subrange.h b/libcxx/include/__ranges/subrange.h --- a/libcxx/include/__ranges/subrange.h +++ b/libcxx/include/__ranges/subrange.h @@ -18,6 +18,7 @@ #include <__concepts/different_from.h> #include <__config> #include <__fwd/get.h> +#include <__fwd/subrange.h> #include <__iterator/advance.h> #include <__iterator/concepts.h> #include <__iterator/incrementable_traits.h> @@ -28,6 +29,7 @@ #include <__ranges/enable_borrowed_range.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> +#include <__tuple_dir/pair_like.h> #include <__tuple_dir/tuple_element.h> #include <__tuple_dir/tuple_size.h> #include <__type_traits/conditional.h> @@ -59,17 +61,6 @@ convertible_to<_From, _To> && !__uses_nonqualification_pointer_conversion<decay_t<_From>, decay_t<_To>>; - template<class _Tp> - concept __pair_like = - !is_reference_v<_Tp> && requires(_Tp __t) { - typename tuple_size<_Tp>::type; // Ensures `tuple_size<T>` is complete. - requires derived_from<tuple_size<_Tp>, integral_constant<size_t, 2>>; - typename tuple_element_t<0, remove_const_t<_Tp>>; - typename tuple_element_t<1, remove_const_t<_Tp>>; - { std::get<0>(__t) } -> convertible_to<const tuple_element_t<0, _Tp>&>; - { std::get<1>(__t) } -> convertible_to<const tuple_element_t<1, _Tp>&>; - }; - template<class _Pair, class _Iter, class _Sent> concept __pair_like_convertible_from = !range<_Pair> && __pair_like<_Pair> && @@ -77,8 +68,6 @@ __convertible_to_non_slicing<_Iter, tuple_element_t<0, _Pair>> && convertible_to<_Sent, tuple_element_t<1, _Pair>>; - enum class _LIBCPP_ENUM_VIS subrange_kind : bool { unsized, sized }; - template<input_or_output_iterator _Iter, sentinel_for<_Iter> _Sent = _Iter, subrange_kind _Kind = sized_sentinel_for<_Sent, _Iter> ? subrange_kind::sized diff --git a/libcxx/include/__tuple_dir/pair_like.h b/libcxx/include/__tuple_dir/pair_like.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__tuple_dir/pair_like.h @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___TUPLE_PAIR_LIKE_H +#define _LIBCPP___TUPLE_PAIR_LIKE_H + +#include <__config> +#include <__tuple_dir/tuple_like.h> +#include <__tuple_dir/tuple_size.h> +#include <__type_traits/remove_cvref.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template <class _Tp> +concept __pair_like = __tuple_like<_Tp> && tuple_size<remove_cvref_t<_Tp>>::value == 2; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TUPLE_PAIR_LIKE_H diff --git a/libcxx/include/__tuple_dir/sfinae_helpers.h b/libcxx/include/__tuple_dir/sfinae_helpers.h --- a/libcxx/include/__tuple_dir/sfinae_helpers.h +++ b/libcxx/include/__tuple_dir/sfinae_helpers.h @@ -13,7 +13,7 @@ #include <__fwd/tuple.h> #include <__tuple_dir/make_tuple_types.h> #include <__tuple_dir/tuple_element.h> -#include <__tuple_dir/tuple_like.h> +#include <__tuple_dir/tuple_like_ext.h> #include <__tuple_dir/tuple_size.h> #include <__tuple_dir/tuple_types.h> #include <__type_traits/enable_if.h> @@ -58,8 +58,8 @@ // __tuple_convertible -template <class _Tp, class _Up, bool = __tuple_like<__libcpp_remove_reference_t<_Tp> >::value, - bool = __tuple_like<_Up>::value> +template <class _Tp, class _Up, bool = __tuple_like_ext<__libcpp_remove_reference_t<_Tp> >::value, + bool = __tuple_like_ext<_Up>::value> struct __tuple_convertible : public false_type {}; @@ -73,8 +73,8 @@ // __tuple_constructible -template <class _Tp, class _Up, bool = __tuple_like<__libcpp_remove_reference_t<_Tp> >::value, - bool = __tuple_like<_Up>::value> +template <class _Tp, class _Up, bool = __tuple_like_ext<__libcpp_remove_reference_t<_Tp> >::value, + bool = __tuple_like_ext<_Up>::value> struct __tuple_constructible : public false_type {}; @@ -88,8 +88,8 @@ // __tuple_assignable -template <class _Tp, class _Up, bool = __tuple_like<__libcpp_remove_reference_t<_Tp> >::value, - bool = __tuple_like<_Up>::value> +template <class _Tp, class _Up, bool = __tuple_like_ext<__libcpp_remove_reference_t<_Tp> >::value, + bool = __tuple_like_ext<_Up>::value> struct __tuple_assignable : public false_type {}; @@ -117,7 +117,7 @@ template <class _Tuple, size_t _ExpectedSize, class _RawTuple = __libcpp_remove_reference_t<_Tuple> > using __tuple_like_with_size _LIBCPP_NODEBUG = __tuple_like_with_size_imp< - __tuple_like<_RawTuple>::value, + __tuple_like_ext<_RawTuple>::value, tuple_size<_RawTuple>, _ExpectedSize >; diff --git a/libcxx/include/__tuple_dir/tuple_like.h b/libcxx/include/__tuple_dir/tuple_like.h --- a/libcxx/include/__tuple_dir/tuple_like.h +++ b/libcxx/include/__tuple_dir/tuple_like.h @@ -12,9 +12,10 @@ #include <__config> #include <__fwd/array.h> #include <__fwd/pair.h> +#include <__fwd/subrange.h> #include <__fwd/tuple.h> -#include <__tuple_dir/tuple_types.h> #include <__type_traits/integral_constant.h> +#include <__type_traits/remove_cvref.h> #include <cstddef> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -23,21 +24,27 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template <class _Tp> struct __tuple_like : false_type {}; +#if _LIBCPP_STD_VER >= 20 -template <class _Tp> struct __tuple_like<const _Tp> : public __tuple_like<_Tp> {}; -template <class _Tp> struct __tuple_like<volatile _Tp> : public __tuple_like<_Tp> {}; -template <class _Tp> struct __tuple_like<const volatile _Tp> : public __tuple_like<_Tp> {}; +template <class _Tp> +struct __tuple_like_impl : false_type {}; -#ifndef _LIBCPP_CXX03_LANG -template <class... _Tp> struct __tuple_like<tuple<_Tp...> > : true_type {}; -#endif +template <class... _Tp> +struct __tuple_like_impl<tuple<_Tp...> > : true_type {}; + +template <class _T1, class _T2> +struct __tuple_like_impl<pair<_T1, _T2> > : true_type {}; + +template <class _Tp, size_t _Size> +struct __tuple_like_impl<array<_Tp, _Size> > : true_type {}; -template <class _T1, class _T2> struct __tuple_like<pair<_T1, _T2> > : true_type {}; +template <class _Ip, class _Sp, ranges::subrange_kind _Kp> +struct __tuple_like_impl<ranges::subrange<_Ip, _Sp, _Kp> > : true_type {}; -template <class _Tp, size_t _Size> struct __tuple_like<array<_Tp, _Size> > : true_type {}; +template <class _Tp> +concept __tuple_like = __tuple_like_impl<remove_cvref_t<_Tp>>::value; -template <class... _Tp> struct __tuple_like<__tuple_types<_Tp...> > : true_type {}; +#endif // _LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__tuple_dir/tuple_like_ext.h b/libcxx/include/__tuple_dir/tuple_like_ext.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__tuple_dir/tuple_like_ext.h @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H +#define _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H + +#include <__config> +#include <__fwd/array.h> +#include <__fwd/pair.h> +#include <__fwd/tuple.h> +#include <__tuple_dir/tuple_types.h> +#include <__type_traits/integral_constant.h> +#include <cstddef> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template <class _Tp> struct __tuple_like_ext : false_type {}; + +template <class _Tp> struct __tuple_like_ext<const _Tp> : public __tuple_like_ext<_Tp> {}; +template <class _Tp> struct __tuple_like_ext<volatile _Tp> : public __tuple_like_ext<_Tp> {}; +template <class _Tp> struct __tuple_like_ext<const volatile _Tp> : public __tuple_like_ext<_Tp> {}; + +#ifndef _LIBCPP_CXX03_LANG +template <class... _Tp> struct __tuple_like_ext<tuple<_Tp...> > : true_type {}; +#endif + +template <class _T1, class _T2> struct __tuple_like_ext<pair<_T1, _T2> > : true_type {}; + +template <class _Tp, size_t _Size> struct __tuple_like_ext<array<_Tp, _Size> > : true_type {}; + +template <class... _Tp> struct __tuple_like_ext<__tuple_types<_Tp...> > : true_type {}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TUPLE_TUPLE_LIKE_EXT_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1228,6 +1228,7 @@ module data { private header "__ranges/data.h" } module drop_view { private header "__ranges/drop_view.h" } module drop_while_view { private header "__ranges/drop_while_view.h" } + module elements_view { private header "__ranges/elements_view.h" } module empty { private header "__ranges/empty.h" } module empty_view { private header "__ranges/empty_view.h" } module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" } @@ -1249,7 +1250,11 @@ module reverse_view { private header "__ranges/reverse_view.h" } module single_view { private header "__ranges/single_view.h" } module size { private header "__ranges/size.h" } - module subrange { private header "__ranges/subrange.h" } + module subrange { + private header "__ranges/subrange.h" + + module subrange_fwd { private header "__fwd/subrange.h" } + } module take_view { private header "__ranges/take_view.h" } module take_while_view { private header "__ranges/take_while_view.h" } module transform_view { @@ -1364,11 +1369,13 @@ module apply_cv { private header "__tuple_dir/apply_cv.h" } module get_fwd { private header "__fwd/get.h" } module make_tuple_types { private header "__tuple_dir/make_tuple_types.h" } + module pair_like { private header "__tuple_dir/pair_like.h" } module sfinae_helpers { private header "__tuple_dir/sfinae_helpers.h" } module tuple_element { private header "__tuple_dir/tuple_element.h" } module tuple_fwd { private header "__fwd/tuple.h" } module tuple_indices { private header "__tuple_dir/tuple_indices.h" } module tuple_like { private header "__tuple_dir/tuple_like.h" } + module tuple_like_ext { private header "__tuple_dir/tuple_like_ext.h" } module tuple_size { private header "__tuple_dir/tuple_size.h" } module tuple_types { private header "__tuple_dir/tuple_types.h" } } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -115,6 +115,27 @@ template<range R> using borrowed_subrange_t = see below; + // [range.elements], elements view + template<input_range V, size_t N> + requires see below + class elements_view; + + template<class T, size_t N> + inline constexpr bool enable_borrowed_range<elements_view<T, N>> = + enable_borrowed_range<T>; + + template<class R> + using keys_view = elements_view<R, 0>; + template<class R> + using values_view = elements_view<R, 1>; + + namespace views { + template<size_t N> + inline constexpr unspecified elements = unspecified; + inline constexpr auto keys = elements<0>; + inline constexpr auto values = elements<1>; + } + // [range.empty], empty view template<class T> requires is_object_v<T> @@ -316,6 +337,7 @@ #include <__ranges/data.h> #include <__ranges/drop_view.h> #include <__ranges/drop_while_view.h> +#include <__ranges/elements_view.h> #include <__ranges/empty.h> #include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> diff --git a/libcxx/include/tuple b/libcxx/include/tuple --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -1677,7 +1677,7 @@ tuple<_Types...>, typename __make_tuple_types<__remove_cvref_t<_Tuple0> >::type >::type, - __tuple_like<__libcpp_remove_reference_t<_Tuple1> >::value, + __tuple_like_ext<__libcpp_remove_reference_t<_Tuple1> >::value, _Tuple1, _Tuples...> { }; @@ -1687,7 +1687,7 @@ template <class _Tuple0, class ..._Tuples> struct __tuple_cat_return<_Tuple0, _Tuples...> : public __tuple_cat_return_1<tuple<>, - __tuple_like<__libcpp_remove_reference_t<_Tuple0> >::value, _Tuple0, + __tuple_like_ext<__libcpp_remove_reference_t<_Tuple0> >::value, _Tuple0, _Tuples...> { }; diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -395,6 +395,7 @@ #include <__fwd/span.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/span.h'}} #include <__fwd/string.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string.h'}} #include <__fwd/string_view.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string_view.h'}} +#include <__fwd/subrange.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/subrange.h'}} #include <__fwd/tuple.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/tuple.h'}} #include <__ios/fpos.h> // expected-error@*:* {{use of private header from outside its module: '__ios/fpos.h'}} #include <__iterator/access.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/access.h'}} @@ -533,6 +534,7 @@ #include <__ranges/data.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/data.h'}} #include <__ranges/drop_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_view.h'}} #include <__ranges/drop_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/drop_while_view.h'}} +#include <__ranges/elements_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/elements_view.h'}} #include <__ranges/empty.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty.h'}} #include <__ranges/empty_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/empty_view.h'}} #include <__ranges/enable_borrowed_range.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/enable_borrowed_range.h'}} @@ -566,10 +568,12 @@ #include <__thread/timed_backoff_policy.h> // expected-error@*:* {{use of private header from outside its module: '__thread/timed_backoff_policy.h'}} #include <__tuple_dir/apply_cv.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/apply_cv.h'}} #include <__tuple_dir/make_tuple_types.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/make_tuple_types.h'}} +#include <__tuple_dir/pair_like.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/pair_like.h'}} #include <__tuple_dir/sfinae_helpers.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/sfinae_helpers.h'}} #include <__tuple_dir/tuple_element.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_element.h'}} #include <__tuple_dir/tuple_indices.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_indices.h'}} #include <__tuple_dir/tuple_like.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_like.h'}} +#include <__tuple_dir/tuple_like_ext.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_like_ext.h'}} #include <__tuple_dir/tuple_size.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_size.h'}} #include <__tuple_dir/tuple_types.h> // expected-error@*:* {{use of private header from outside its module: '__tuple_dir/tuple_types.h'}} #include <__type_traits/add_const.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/add_const.h'}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/elements_view.no_unique_address.compile.pass.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 + +// clang-cl and cl currently don't support [[no_unique_address]] +// XFAIL: msvc + +// Test the libc++ extension that the base view stored in `std::ranges::elements_view` +// has been marked as _LIBCPP_NO_UNIQUE_ADDRESS + +#include <ranges> +#include <tuple> + + +struct EmptyView : std::ranges::view_base { + std::tuple<int>* begin() const; + std::tuple<int>* end() const; +}; + +using ElementsView = std::ranges::elements_view<EmptyView, 0>; + +struct TestClass { + [[no_unique_address]] ElementsView view; + int i; +}; + +static_assert(sizeof(TestClass) == sizeof(int)); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/sentinel.no_unique_address.compile.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// clang-cl and cl currently don't support [[no_unique_address]] +// XFAIL: msvc + +// Test the libc++ extension that the sentinel stored in `std::ranges::elements_view::__sentinel` +// has been marked as _LIBCPP_NO_UNIQUE_ADDRESS + +#include <ranges> +#include <tuple> + +struct EmptySentinel { + friend bool operator==(std::tuple<int>* iter, EmptySentinel) { return iter; } +}; + +struct Range : std::ranges::view_base { + std::tuple<int>* begin() const; + EmptySentinel end() const; +}; + +using ElementsView = std::ranges::elements_view<Range, 0>; +using ElementsSent = std::ranges::sentinel_t<ElementsView>; + +struct TestClass { + [[no_unique_address]] ElementsSent s; + int i; +}; + +static_assert(sizeof(TestClass) == sizeof(int)); diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/adaptor.pass.cpp @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::elements<N> +// std::views::keys +// std::views::values + +#include <algorithm> +#include <cassert> +#include <ranges> +#include <tuple> +#include <type_traits> +#include <utility> + +template <class T> +struct View : std::ranges::view_base { + T* begin() const; + T* end() const; +}; + +static_assert(!std::is_invocable_v<decltype((std::views::elements<0>))>); +static_assert(!std::is_invocable_v<decltype((std::views::elements<0>)), View<int>>); +static_assert(std::is_invocable_v<decltype((std::views::elements<0>)), View<std::pair<int, int>>>); +static_assert(std::is_invocable_v<decltype((std::views::elements<0>)), View<std::tuple<int>>>); +static_assert(!std::is_invocable_v<decltype((std::views::elements<5>)), View<std::tuple<int>>>); + +static_assert(!std::is_invocable_v<decltype((std::views::keys))>); +static_assert(!std::is_invocable_v<decltype((std::views::keys)), View<int>>); +static_assert(std::is_invocable_v<decltype((std::views::keys)), View<std::pair<int, int>>>); +static_assert(std::is_invocable_v<decltype((std::views::keys)), View<std::tuple<int>>>); + +static_assert(!std::is_invocable_v<decltype((std::views::values))>); +static_assert(!std::is_invocable_v<decltype((std::views::values)), View<int>>); +static_assert(std::is_invocable_v<decltype((std::views::values)), View<std::pair<int, int>>>); +static_assert(!std::is_invocable_v<decltype((std::views::values)), View<std::tuple<int>>>); + +template <class View, class T> +concept CanBePiped = + requires(View&& view, T&& t) { + { std::forward<View>(view) | std::forward<T>(t) }; + }; + +static_assert(!CanBePiped<View<int>, decltype((std::views::elements<0>))>); +static_assert(CanBePiped<View<std::pair<int, int>>, decltype((std::views::elements<0>))>); +static_assert(CanBePiped<View<std::tuple<int>>, decltype((std::views::elements<0>))>); +static_assert(!CanBePiped<View<std::tuple<int>>, decltype((std::views::elements<5>))>); + +static_assert(!CanBePiped<View<int>, decltype((std::views::keys))>); +static_assert(CanBePiped<View<std::pair<int, int>>, decltype((std::views::keys))>); +static_assert(CanBePiped<View<std::tuple<int>>, decltype((std::views::keys))>); + +static_assert(!CanBePiped<View<int>, decltype((std::views::values))>); +static_assert(CanBePiped<View<std::pair<int, int>>, decltype((std::views::values))>); +static_assert(!CanBePiped<View<std::tuple<int>>, decltype((std::views::values))>); + +constexpr bool test() { + std::pair<int, int> buff[] = {{1, 2}, {3, 4}, {5, 6}}; + + // Test `views::elements<N>(v)` + { + using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 0>; + std::same_as<Result> decltype(auto) result = std::views::elements<0>(buff); + auto expected = {1, 3, 5}; + assert(std::ranges::equal(result, expected)); + } + + // Test `views::keys(v)` + { + using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 0>; + std::same_as<Result> decltype(auto) result = std::views::keys(buff); + auto expected = {1, 3, 5}; + assert(std::ranges::equal(result, expected)); + } + + // Test `views::values(v)` + { + using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 1>; + std::same_as<Result> decltype(auto) result = std::views::values(buff); + auto expected = {2, 4, 6}; + assert(std::ranges::equal(result, expected)); + } + + // Test `v | views::elements<N>` + { + using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 1>; + std::same_as<Result> decltype(auto) result = buff | std::views::elements<1>; + auto expected = {2, 4, 6}; + assert(std::ranges::equal(result, expected)); + } + + // Test `v | views::keys` + { + using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 0>; + std::same_as<Result> decltype(auto) result = buff | std::views::keys; + auto expected = {1, 3, 5}; + assert(std::ranges::equal(result, expected)); + } + + // Test `v | views::values` + { + using Result = std::ranges::elements_view<std::ranges::ref_view<std::pair<int, int>[3]>, 1>; + std::same_as<Result> decltype(auto) result = buff | std::views::values; + auto expected = {2, 4, 6}; + assert(std::ranges::equal(result, expected)); + } + + // Test views::elements<0> | views::elements<0> + { + std::pair<std::tuple<int>, std::tuple<int>> nested[] = {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int>>[3]>, 0>, + 0>; + auto const partial = std::views::elements<0> | std::views::elements<0>; + std::same_as<Result> decltype(auto) result = nested | partial; + auto expected = {1, 3, 5}; + assert(std::ranges::equal(result, expected)); + } + + // Test views::keys | views::keys + { + std::pair<std::tuple<int>, std::tuple<int>> nested[] = {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int>>[3]>, 0>, + 0>; + auto const partial = std::views::keys | std::views::keys; + std::same_as<Result> decltype(auto) result = nested | partial; + auto expected = {1, 3, 5}; + assert(std::ranges::equal(result, expected)); + } + + // Test views::values | views::values + { + std::pair<std::tuple<int>, std::tuple<int, int>> nested[] = {{{1}, {2, 3}}, {{4}, {5, 6}}, {{7}, {8, 9}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int, int>>[3]>, 1>, + 1>; + auto const partial = std::views::values | std::views::values; + std::same_as<Result> decltype(auto) result = nested | partial; + auto expected = {3, 6, 9}; + assert(std::ranges::equal(result, expected)); + } + + // Test views::keys | views::values + { + std::pair<std::tuple<int, int>, std::tuple<int>> nested[] = {{{1, 2}, {3}}, {{4, 5}, {6}}, {{7, 8}, {9}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int, int>, std::tuple<int>>[3]>, 0>, + 1>; + auto const partial = std::views::keys | std::views::values; + std::same_as<Result> decltype(auto) result = nested | partial; + auto expected = {2, 5, 8}; + assert(std::ranges::equal(result, expected)); + } + + // Test views::values | views::keys + { + std::pair<std::tuple<int>, std::tuple<int, int>> nested[] = {{{1}, {2, 3}}, {{4}, {5, 6}}, {{7}, {8, 9}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view<std::ranges::ref_view<std::pair<std::tuple<int>, std::tuple<int, int>>[3]>, 1>, + 0>; + auto const partial = std::views::values | std::views::keys; + std::same_as<Result> decltype(auto) result = nested | partial; + auto expected = {2, 5, 8}; + assert(std::ranges::equal(result, expected)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/base.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr V base() const & requires copy_constructible<V> { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include <cassert> +#include <ranges> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + int i; + std::tuple<int>* begin() const; + std::tuple<int>* end() const; +}; + +struct MoveOnlyView : View { + MoveOnly mo; +}; + +template <class T> +concept HasBase = requires(T&& t) { std::forward<T>(t).base(); }; + +static_assert(HasBase<std::ranges::elements_view<View, 0> const&>); +static_assert(HasBase<std::ranges::elements_view<View, 0>&&>); + +static_assert(!HasBase<std::ranges::elements_view<MoveOnlyView, 0> const&>); +static_assert(HasBase<std::ranges::elements_view<MoveOnlyView, 0>&&>); + +constexpr bool test() { + // const & + { + const std::ranges::elements_view<View, 0> ev{View{{}, 5}}; + std::same_as<View> decltype(auto) v = ev.base(); + assert(v.i == 5); + } + + // & + { + std::ranges::elements_view<View, 0> ev{View{{}, 5}}; + std::same_as<View> decltype(auto) v = ev.base(); + assert(v.i == 5); + } + + // && + { + std::ranges::elements_view<View, 0> ev{View{{}, 5}}; + std::same_as<View> decltype(auto) v = std::move(ev).base(); + assert(v.i == 5); + } + + // const && + { + const std::ranges::elements_view<View, 0> ev{View{{}, 5}}; + std::same_as<View> decltype(auto) v = std::move(ev).base(); + assert(v.i == 5); + } + + // move only + { + std::ranges::elements_view<MoveOnlyView, 0> ev{MoveOnlyView{{}, 5}}; + std::same_as<MoveOnlyView> decltype(auto) v = std::move(ev).base(); + assert(v.mo.get() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/begin.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto begin() requires (!simple-view<V>) +// constexpr auto begin() const requires range<const V> + +#include <cassert> +#include <concepts> +#include <ranges> +#include <tuple> +#include <utility> + +#include "types.h" + +template <class T> +concept HasConstBegin = requires(const T ct) { ct.begin(); }; + +template <class T> +concept HasBegin = requires(T t) { t.begin(); }; + +template <class T> +concept HasConstAndNonConstBegin = + HasConstBegin<T> && + // because const begin and non-const begin returns different types (iterator<true>, iterator<false>) + requires(T t, const T ct) { requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>; }; + +template <class T> +concept HasOnlyNonConstBegin = HasBegin<T> && !HasConstBegin<T>; + +template <class T> +concept HasOnlyConstBegin = HasConstBegin<T> && !HasConstAndNonConstBegin<T>; + +struct NoConstBeginView : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr std::tuple<int>* begin() { return buffer_; } + constexpr std::tuple<int>* end() { return buffer_ + size_; } +}; + +// simple-view<V> +static_assert(HasOnlyConstBegin<std::ranges::elements_view<SimpleCommon, 0>>); + +// !simple-view<V> && range<const V> +static_assert(HasConstAndNonConstBegin<std::ranges::elements_view<NonSimpleCommon, 0>>); + +// !range<const V> +static_assert(HasOnlyNonConstBegin<std::ranges::elements_view<NoConstBeginView, 0>>); + +constexpr bool test() { + std::tuple<int> buffer[] = {{1}, {2}}; + { + // underlying iterator should be pointing to the first element + auto ev = std::views::elements<0>(buffer); + auto iter = ev.begin(); + assert(&(*iter) == &std::get<0>(buffer[0])); + } + + { + // underlying range models simple-view + auto v = std::views::elements<0>(SimpleCommon{buffer}); + static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>); + assert(v.begin() == std::as_const(v).begin()); + auto&& r = *std::as_const(v).begin(); + assert(&r == &std::get<0>(buffer[0])); + } + + { + // underlying const R is not a range + auto v = std::views::elements<0>(NoConstBeginView{buffer}); + auto&& r = *v.begin(); + assert(&r == &std::get<0>(buffer[0])); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/borrowed.compile.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// +// template<class T, size_t N> +// inline constexpr bool enable_borrowed_range<elements_view<T, N>> = +// enable_borrowed_range<T>; + +#include <ranges> +#include <tuple> + +struct NonBorrowed : std::ranges::view_base { + std::tuple<int>* begin(); + std::tuple<int>* end(); +}; + +struct Borrowed : std::ranges::view_base { + std::tuple<int>* begin(); + std::tuple<int>* end(); +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range<Borrowed> = true; + +static_assert(!std::ranges::borrowed_range<std::ranges::elements_view<NonBorrowed, 0>>); +static_assert(std::ranges::borrowed_range<std::ranges::elements_view<Borrowed, 0>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.default.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// elements_view() requires default_initializable<V> = default; + +#include <cassert> +#include <ranges> +#include <tuple> +#include <type_traits> + +template <bool DefaultInitializable> +struct View : std::ranges::view_base { + int i = 42; + constexpr explicit View() + requires DefaultInitializable + = default; + std::tuple<int>* begin() const; + std::tuple<int>* end() const; +}; + + +// clang-format off +static_assert( std::is_default_constructible_v<std::ranges::elements_view<View<true >, 0>>); +static_assert(!std::is_default_constructible_v<std::ranges::elements_view<View<false>, 0>>); +// clang-format on + +constexpr bool test() { + { + std::ranges::elements_view<View<true>, 0> ev = {}; + assert(ev.base().i == 42); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/ctor.view.pass.cpp @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr explicit elements_view(V base); + +#include <cassert> +#include <ranges> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + MoveOnly mo; + std::tuple<int>* begin() const; + std::tuple<int>* end() const; +}; + +// Test Explicit +static_assert(std::is_constructible_v<std::ranges::elements_view<View, 0>, View>); +static_assert(!std::is_convertible_v<View, std::ranges::elements_view<View, 0>>); + +constexpr bool test() { + { + std::ranges::elements_view<View, 0> ev{View{{}, MoveOnly{5}}}; + assert(std::move(ev).base().mo.get() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/end.pass.cpp @@ -0,0 +1,142 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto end() requires (!simple-view<V> && !common_range<V>) +// constexpr auto end() requires (!simple-view<V> && common_range<V>) +// constexpr auto end() const requires range<const V> +// constexpr auto end() const requires common_range<const V> + +#include <cassert> +#include <iterator> +#include <ranges> +#include <type_traits> +#include <utility> + +#include "types.h" + +// | simple | common | v.end() | as_const(v) +// | | | | .end() +// |--------|--------|------------------|--------------- +// | Y | Y | iterator<true> | iterator<true> +// | Y | N | sentinel<true> | sentinel<true> +// | N | Y | iterator<false> | iterator<true> +// | N | N | sentinel<false> | sentinel<true> + +// !range<const V> +template <class T> +concept HasEnd = requires(T t) { t.end(); }; + +template <class T> +concept HasConstEnd = requires(const T ct) { ct.end(); }; + +struct NoConstEndView : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr std::tuple<int>* begin() { return buffer_; } + constexpr std::tuple<int>* end() { return buffer_ + size_; } +}; + +static_assert(HasEnd<std::ranges::elements_view<NoConstEndView, 0>>); +static_assert(!HasConstEnd<std::ranges::elements_view<NoConstEndView, 0>>); + +constexpr bool test() { + std::tuple<int> buffer[] = {{1}, {2}, {3}}; + + // simple-view && common_view + { + SimpleCommon v{buffer}; + auto ev = std::views::elements<0>(v); + + auto it = ev.begin(); + decltype(auto) st = ev.end(); + assert(st == it + 3); + + auto const_it = std::as_const(ev).begin(); + decltype(auto) const_st = std::as_const(ev).end(); + assert(const_st == const_it + 3); + + // Both iterator<true> + static_assert(std::same_as<decltype(st), decltype(const_st)>); + static_assert(std::same_as<decltype(st), decltype(it)>); + static_assert(std::same_as<decltype(const_st), decltype(const_it)>); + } + + // simple-view && !common_view + { + SimpleNonCommon v{buffer}; + auto ev = std::views::elements<0>(v); + + auto it = ev.begin(); + decltype(auto) st = ev.end(); + assert(st == it + 3); + + auto const_it = std::as_const(ev).begin(); + decltype(auto) const_st = std::as_const(ev).end(); + assert(const_st == const_it + 3); + + // Both iterator<true> + static_assert(std::same_as<decltype(st), decltype(const_st)>); + static_assert(!std::same_as<decltype(st), decltype(it)>); + static_assert(!std::same_as<decltype(const_st), decltype(const_it)>); + } + + // !simple-view && common_view + { + NonSimpleCommon v{buffer}; + auto ev = std::views::elements<0>(v); + + auto it = ev.begin(); + decltype(auto) st = ev.end(); + assert(st == it + 3); + + auto const_it = std::as_const(ev).begin(); + decltype(auto) const_st = std::as_const(ev).end(); + assert(const_st == const_it + 3); + + // iterator<false> and iterator<true> + static_assert(!std::same_as<decltype(st), decltype(const_st)>); + static_assert(std::same_as<decltype(st), decltype(it)>); + static_assert(std::same_as<decltype(const_st), decltype(const_it)>); + } + + // !simple-view && !common_view + { + NonSimpleNonCommon v{buffer}; + auto ev = std::views::elements<0>(v); + + auto it = ev.begin(); + decltype(auto) st = ev.end(); + assert(st == it + 3); + + auto const_it = std::as_const(ev).begin(); + decltype(auto) const_st = std::as_const(ev).end(); + assert(const_st == const_it + 3); + + // sentinel<false> and sentinel<true> + static_assert(!std::same_as<decltype(st), decltype(const_st)>); + static_assert(!std::same_as<decltype(st), decltype(it)>); + static_assert(!std::same_as<decltype(const_st), decltype(const_it)>); + } + + // LWG 3406 elements_view::begin() and elements_view::end() have incompatible constraints + { + std::tuple<int, int> x[] = {{0, 0}}; + std::ranges::subrange r = {std::counted_iterator(x, 1), std::default_sentinel}; + auto v = r | std::views::elements<0>; + assert(v.begin() != v.end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/general.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Some basic examples of how elements_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include <algorithm> +#include <array> +#include <cassert> +#include <map> +#include <ranges> +#include <string_view> +#include <tuple> +#include <utility> + +#include "test_iterators.h" + +int main(int, char**) { + using namespace std::string_view_literals; + auto historicalFigures = + std::map{std::pair{"Lovelace"sv, 1815}, {"Turing"sv, 1912}, {"Babbage"sv, 1791}, {"Hamilton"sv, 1936}}; + auto expectedYears = {1791, 1936, 1815, 1912}; + + // views::elements<N> + { + auto names = historicalFigures | std::views::elements<0>; + auto expectedNames = {"Babbage"sv, "Hamilton"sv, "Lovelace"sv, "Turing"sv}; + assert(std::ranges::equal(names, expectedNames)); + + auto birth_years = historicalFigures | std::views::elements<1>; + assert(std::ranges::equal(birth_years, expectedYears)); + } + + // views::keys + { + auto names = historicalFigures | std::views::keys; + auto expectedNames = {"Babbage"sv, "Hamilton"sv, "Lovelace"sv, "Turing"sv}; + assert(std::ranges::equal(names, expectedNames)); + } + + // views::values + { + auto is_even = [](const auto x) { return x % 2 == 0; }; + assert(std::ranges::count_if(historicalFigures | std::views::values, is_even) == 2); + } + + // array + { + std::array<int, 3> arrs[] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + auto ev = arrs | std::views::elements<2>; + auto expected = {3, 6, 9}; + assert(std::ranges::equal(ev, expected)); + } + + // pair + { + std::pair<double, int> ps[] = {{1.0, 2}, {3.0, 4}, {5.0, 6}}; + auto ev = ps | std::views::elements<1>; + auto expected = {2, 4, 6}; + assert(std::ranges::equal(ev, expected)); + } + + // tuple + { + std::tuple<short> tps[] = {{1}, {2}, {3}}; + auto ev = tps | std::views::elements<0>; + auto expected = {1, 2, 3}; + assert(std::ranges::equal(ev, expected)); + } + + // subrange + { + int is[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + using Iter = forward_iterator<int*>; + using Sent = sentinel_wrapper<Iter>; + using SubRange = std::ranges::subrange<Iter, Sent>; + SubRange sr[] = { + {Iter{is}, Sent{Iter{is + 1}}}, + {Iter{is + 2}, Sent{Iter{is + 5}}}, + {Iter{is + 6}, Sent{Iter{is + 8}}}, + }; + + auto iters = sr | std::views::elements<0>; + static_assert(std::is_same_v<Iter, std::ranges::range_reference_t<decltype(iters)>>); + auto expectedIters = {is, is + 2, is + 6}; + assert(std::ranges::equal(iters | std::views::transform([](auto&& iter) { return base(iter); }), expectedIters)); + + auto sentinels = sr | std::views::elements<1>; + static_assert(std::is_same_v<Sent, std::ranges::range_reference_t<decltype(sentinels)>>); + auto expectedSentinels = {is + 1, is + 5, is + 8}; + assert(std::ranges::equal( + sentinels | std::views::transform([](auto&& st) { return base(base(st)); }), expectedSentinels)); + } + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/arithmetic.pass.cpp @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator& operator+=(difference_type n) +// requires random_access_range<Base>; +// +// constexpr iterator& operator-=(difference_type n) +// requires random_access_range<Base>; +// +// friend constexpr iterator operator+(const iterator& x, difference_type y) +// requires random_access_range<Base>; +// +// friend constexpr iterator operator+(difference_type x, const iterator& y) +// requires random_access_range<Base>; +// +// friend constexpr iterator operator-(const iterator& x, difference_type y) +// requires random_access_range<Base>; +// +// friend constexpr difference_type operator-(const iterator& x, const iterator& y) +// requires sized_sentinel_for<iterator_t<Base>, iterator_t<Base>>; + +#include <ranges> + +#include <tuple> + +#include "test_iterators.h" + +template <class T, class U> +concept CanPlus = requires(T t, U u) { t + u; }; + +template <class T, class U> +concept CanPlusEqual = requires(T t, U u) { t += u; }; + +template <class T, class U> +concept CanMinus = requires(T t, U u) { t - u; }; + +template <class T, class U> +concept CanMinusEqual = requires(T t, U u) { t -= u; }; + +template <class BaseRange> +using ElemIter = std::ranges::iterator_t<std::ranges::elements_view<BaseRange, 0>>; + +using RandomAccessRange = std::ranges::subrange<std::tuple<int>*>; +static_assert(std::ranges::random_access_range<RandomAccessRange>); +static_assert(std::sized_sentinel_for<std::ranges::iterator_t<RandomAccessRange>, // + std::ranges::iterator_t<RandomAccessRange>>); + +static_assert(CanPlus<ElemIter<RandomAccessRange>, int>); +static_assert(CanPlus<int, ElemIter<RandomAccessRange>>); +static_assert(CanPlusEqual<ElemIter<RandomAccessRange>, int>); +static_assert(CanMinus<ElemIter<RandomAccessRange>, int>); +static_assert(CanMinus<ElemIter<RandomAccessRange>, ElemIter<RandomAccessRange>>); +static_assert(CanMinusEqual<ElemIter<RandomAccessRange>, int>); + +using BidiRange = std::ranges::subrange<bidirectional_iterator<std::tuple<int>*>>; +static_assert(!std::ranges::random_access_range<BidiRange>); +static_assert(!std::sized_sentinel_for<std::ranges::iterator_t<BidiRange>, // + std::ranges::iterator_t<BidiRange>>); + +static_assert(!CanPlus<ElemIter<BidiRange>, int>); +static_assert(!CanPlus<int, ElemIter<BidiRange>>); +static_assert(!CanPlusEqual<ElemIter<BidiRange>, int>); +static_assert(!CanMinus<ElemIter<BidiRange>, int>); +static_assert(!CanMinus<ElemIter<BidiRange>, ElemIter<BidiRange>>); +static_assert(!CanMinusEqual<ElemIter<BidiRange>, int>); + +constexpr bool test() { + std::tuple<int> ts[] = {{1}, {2}, {3}, {4}}; + + RandomAccessRange r{&ts[0], &ts[0] + 4}; + auto ev = r | std::views::elements<0>; + { + // operator+(x, n) operator+(n,x) and operator+= + auto it1 = ev.begin(); + + auto it2 = it1 + 3; + assert(it2.base() == &ts[3]); + + auto it3 = 3 + it1; + assert(it3.base() == &ts[3]); + + it1 += 3; + assert(it1 == it2); + assert(it1.base() == &ts[3]); + } + + { + // operator-(x, n) and operator-= + auto it1 = ev.end(); + + auto it2 = it1 - 3; + assert(it2.base() == &ts[1]); + + it1 -= 3; + assert(it1 == it2); + assert(it1.base() == &ts[1]); + } + + { + // operator-(x, y) + assert((ev.end() - ev.begin()) == 4); + + auto it1 = ev.begin() + 2; + auto it2 = ev.end() - 1; + assert((it1 - it2) == -1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/base.pass.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr const iterator_t<Base>& base() const & noexcept; +// constexpr iterator_t<Base> base() &&; + +#include <cassert> +#include <ranges> +#include <tuple> +#include <type_traits> +#include <utility> + +#include "MoveOnly.h" +#include "../types.h" + +// Test Noexcept +template <class T> +concept IsBaseNoexcept = + requires { + { std::declval<T>().base() } noexcept; + }; + +using BaseView = std::ranges::subrange<std::tuple<int>*>; +using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<BaseView, 0>>; + +static_assert(IsBaseNoexcept<const ElementsIter&>); +static_assert(IsBaseNoexcept<ElementsIter&>); +static_assert(IsBaseNoexcept<const ElementsIter&&>); +static_assert(!IsBaseNoexcept<ElementsIter&&>); + +constexpr bool test() { + std::tuple<int> t{5}; + + // const & + { + const ElementsIter it{&t}; + decltype(auto) base = it.base(); + static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>); + assert(base == &t); + } + + // & + { + ElementsIter it{&t}; + decltype(auto) base = it.base(); + static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>); + assert(base == &t); + } + + // && + { + ElementsIter it{&t}; + decltype(auto) base = std::move(it).base(); + static_assert(std::is_same_v<decltype(base), std::tuple<int>*>); + assert(base == &t); + } + + // const && + { + const ElementsIter it{&t}; + decltype(auto) base = std::move(it).base(); + static_assert(std::is_same_v<decltype(base), std::tuple<int>* const&>); + assert(base == &t); + } + + // move only + { + struct MoveOnlyIter : IterBase<MoveOnlyIter> { + MoveOnly mo; + }; + struct Sent { + constexpr bool operator==(const MoveOnlyIter&) const { return true; } + }; + + using MoveOnlyElemIter = + std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<MoveOnlyIter, Sent>, 0>>; + + MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}}; + decltype(auto) base = std::move(it).base(); + static_assert(std::is_same_v<decltype(base), MoveOnlyIter>); + assert(base.mo.get() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/compare.pass.cpp @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// friend constexpr bool operator==(const iterator& x, const iterator& y) +// requires equality_comparable<iterator_t<Base>>; +// friend constexpr bool operator<(const iterator& x, const iterator& y) +// requires random_access_range<Base>; +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// requires random_access_range<Base>; +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// requires random_access_range<Base>; +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// requires random_access_range<Base>; +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires random_access_range<Base> && three_way_comparable<iterator_t<Base>>; + +#include <compare> +#include <functional> +#include <ranges> +#include <tuple> + +#include "test_iterators.h" + +constexpr void compareOperatorTest(const auto& iter1, const auto& iter2) { + assert(!(iter1 < iter1)); + assert(iter1 < iter2); + assert(!(iter2 < iter1)); + + assert(iter1 <= iter1); + assert(iter1 <= iter2); + assert(!(iter2 <= iter1)); + + assert(!(iter1 > iter1)); + assert(!(iter1 > iter2)); + assert(iter2 > iter1); + + assert(iter1 >= iter1); + assert(!(iter1 >= iter2)); + assert(iter2 >= iter1); + + assert(iter1 == iter1); + assert(!(iter1 == iter2)); + assert(iter2 == iter2); + + assert(!(iter1 != iter1)); + assert(iter1 != iter2); + assert(!(iter2 != iter2)); +} + +constexpr void inequalityOperatorsDoNotExistTest(const auto& iter1, const auto& iter2) { + using Iter1 = decltype(iter1); + using Iter2 = decltype(iter2); + static_assert(!std::is_invocable_v<std::less<>, Iter1, Iter2>); + static_assert(!std::is_invocable_v<std::less_equal<>, Iter1, Iter2>); + static_assert(!std::is_invocable_v<std::greater<>, Iter1, Iter2>); + static_assert(!std::is_invocable_v<std::greater_equal<>, Iter1, Iter2>); +} + +constexpr bool test() { + std::tuple<int> ts[] = {{1}, {2}, {3}}; + + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator<std::tuple<int>*>; + using Subrange = std::ranges::subrange<It>; + static_assert(std::three_way_comparable<It>); + using R = std::ranges::elements_view<Subrange, 0>; + static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>); + + auto ev = Subrange{It{&ts[0]}, It{&ts[0] + 3}} | std::views::elements<0>; + auto iter1 = ev.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + + assert((iter1 <=> iter2) == std::strong_ordering::less); + assert((iter1 <=> iter1) == std::strong_ordering::equal); + assert((iter2 <=> iter1) == std::strong_ordering::greater); + } + + { + // Test an old-school iterator with no operator<=>; the elements view iterator shouldn't have + // operator<=> either. + using It = random_access_iterator<std::tuple<int>*>; + using Subrange = std::ranges::subrange<It>; + static_assert(!std::three_way_comparable<It>); + using R = std::ranges::elements_view<Subrange, 0>; + static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>); + + auto ev = Subrange{It{&ts[0]}, It{&ts[0] + 3}} | std::views::elements<0>; + auto iter1 = ev.begin(); + auto iter2 = iter1 + 1; + + compareOperatorTest(iter1, iter2); + } + + { + // non random_access_range + using It = bidirectional_iterator<std::tuple<int>*>; + using Subrange = std::ranges::subrange<It>; + static_assert(!std::ranges::random_access_range<Subrange>); + using R = std::ranges::elements_view<Subrange, 0>; + static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>); + + auto ev = Subrange{It{&ts[0]}, It{&ts[0] + 1}} | std::views::elements<0>; + + auto it1 = ev.begin(); + auto it2 = ev.end(); + + assert(it1 == it1); + assert(!(it1 != it1)); + assert(it2 == it2); + assert(!(it2 != it2)); + + assert(it1 != it2); + + ++it1; + assert(it1 == it2); + + inequalityOperatorsDoNotExistTest(it1, it2); + } + + { + // underlying iterator does not support == + using Iter = cpp20_input_iterator<std::tuple<int>*>; + using Sent = sentinel_wrapper<Iter>; + using Subrange = std::ranges::subrange<Iter, Sent>; + using R = std::ranges::elements_view<Subrange, 0>; + static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>); + + auto ev = Subrange{Iter{&ts[0]}, Sent{Iter{&ts[0] + 1}}} | std::views::elements<0>; + auto it = ev.begin(); + + using ElemIter = decltype(it); + static_assert(!std::invocable<std::equal_to<>, ElemIter, ElemIter>); + static_assert(!std::invocable<std::not_equal_to<>, ElemIter, ElemIter>); + inequalityOperatorsDoNotExistTest(it, it); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.base.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr explicit iterator(iterator_t<Base> current); + +#include <cassert> +#include <ranges> +#include <tuple> + +#include "../types.h" + +// Test explicit +using BaseIter = std::tuple<int>*; +using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<std::ranges::subrange<BaseIter, BaseIter>, 0>>; + +static_assert(std::is_constructible_v<ElementsIter, BaseIter>); +static_assert(!std::is_convertible_v<BaseIter, ElementsIter>); + +struct TracedMoveIter : IterBase<TracedMoveIter>{ + bool moved = false; + + constexpr TracedMoveIter() = default; + constexpr TracedMoveIter(const TracedMoveIter&) = default; + constexpr TracedMoveIter(TracedMoveIter&&) : moved{true} {} + constexpr TracedMoveIter& operator=(TracedMoveIter&&) = default; + constexpr TracedMoveIter& operator=(const TracedMoveIter&) = default; +}; + +struct TracedMoveView : std::ranges::view_base { + TracedMoveIter begin() const; + TracedMoveIter end() const; +}; + +constexpr bool test() { + using Iter = std::ranges::iterator_t<std::ranges::elements_view<TracedMoveView, 0>>; + Iter iter{TracedMoveIter{}}; + assert(iter.base().moved); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.default.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// iterator() requires default_initializable<iterator_t<Base>> = default; + +#include <ranges> +#include <tuple> + +#include "../types.h" + +struct PODIter : IterBase<PODIter> { + int i; // deliberately uninitialised +}; + +struct IterDefaultCtrView : std::ranges::view_base { + PODIter begin() const; + PODIter end() const; +}; + +struct IterNoDefaultCtrView : std::ranges::view_base { + cpp20_input_iterator<std::tuple<int>*> begin() const; + sentinel_wrapper<cpp20_input_iterator<std::tuple<int>*>> end() const; +}; + +template <class View, size_t N> +using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<View, N>>; + +static_assert(!std::default_initializable<ElementsIter<IterNoDefaultCtrView, 0>>); +static_assert(std::default_initializable<ElementsIter<IterDefaultCtrView, 0>>); + +constexpr bool test() { + using Iter = ElementsIter<IterDefaultCtrView, 0>; + { + Iter iter; + assert(iter.base().i == 0); // PODIter has to be initialised to have value 0 + } + + { + Iter iter = {}; + assert(iter.base().i == 0); // PODIter has to be initialised to have value 0 + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/ctor.other.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr iterator(iterator<!Const> i) +// requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>; + +#include <cassert> +#include <ranges> +#include <tuple> + +#include "../types.h" + +template <bool Const> +struct ConvertibleIter : IterBase<ConvertibleIter<Const>> { + using iterator_category = std::random_access_iterator_tag; + using value_type = std::tuple<int>; + using difference_type = intptr_t; + + bool movedFromOtherConst = false; + int i = 0; + + constexpr ConvertibleIter() = default; + constexpr ConvertibleIter(int ii) : i(ii) {} + template <bool otherConst> + requires(Const != otherConst) + constexpr ConvertibleIter(ConvertibleIter<otherConst> it) : movedFromOtherConst(true), i(it.i) {} +}; + +template <class Iter, class ConstIter> +struct BasicView : std::ranges::view_base { + Iter begin(); + Iter end(); + + ConstIter begin() const; + ConstIter end() const; +}; + +template <class View> +using ElemIter = std::ranges::iterator_t<std::ranges::elements_view<View, 0>>; + +template <class View> +using ConstElemIter = std::ranges::iterator_t<const std::ranges::elements_view<View, 0>>; + +using ConvertibleView = BasicView<ConvertibleIter<false>, ConvertibleIter<true>>; +using NonConvertibleView = BasicView<forward_iterator<std::tuple<int>*>, bidirectional_iterator<std::tuple<int>*>>; + +static_assert(std::is_constructible_v<ConstElemIter<ConvertibleView>, ElemIter<ConvertibleView>>); +static_assert(!std::is_constructible_v<ElemIter<ConvertibleView>, ConstElemIter<ConvertibleView>>); +static_assert(!std::is_constructible_v<ConstElemIter<NonConvertibleView>, ElemIter<NonConvertibleView>>); +static_assert(!std::is_constructible_v<ElemIter<NonConvertibleView>, ConstElemIter<NonConvertibleView>>); + +constexpr bool test() { + ElemIter<ConvertibleView> iter{ConvertibleIter<false>{5}}; + ConstElemIter<ConvertibleView> constIter = iter; // implicit + assert(constIter.base().movedFromOtherConst); + assert(constIter.base().i == 5); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/decrement.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr iterator& operator--() requires bidirectional_range<Base>; +// constexpr iterator operator--(int) requires bidirectional_range<Base>; + +#include <array> +#include <cassert> +#include <ranges> +#include <tuple> + +#include "test_iterators.h" + +template <class Iter> +concept CanPreDecrement = requires(Iter it) { --it; }; + +template <class Iter> +concept CanPostDecrement = requires(Iter it) { it--; }; + +template <class Iter, class Sent = sentinel_wrapper<Iter>> +constexpr void testOne() { + using Range = std::ranges::subrange<Iter, Sent>; + std::tuple<int> ts[] = {{1}, {2}, {3}}; + auto ev = Range{Iter{&ts[0]}, Sent{Iter{&ts[0] + 3}}} | std::views::elements<0>; + + using ElementIter = std::ranges::iterator_t<decltype(ev)>; + + if constexpr (!std::bidirectional_iterator<Iter>) { + auto it = ev.begin(); + static_assert(!CanPreDecrement<decltype(it)>); + static_assert(!CanPostDecrement<decltype(it)>); + } else { + // --i + { + auto it = ev.begin(); + static_assert(CanPreDecrement<decltype(it)>); + + ++it; + assert(base(it.base()) == &ts[1]); + + decltype(auto) result = --it; + + static_assert(std::is_same_v<decltype(result), ElementIter&>); + assert(&result == &it); + + assert(base(it.base()) == &ts[0]); + } + + // i-- + { + auto it = ev.begin(); + static_assert(CanPostDecrement<decltype(it)>); + + ++it; + assert(base(it.base()) == &ts[1]); + + decltype(auto) result = it--; + + static_assert(std::is_same_v<decltype(result), ElementIter>); + + assert(base(it.base()) == &ts[0]); + assert(base(result.base()) == &ts[1]); + } + } +} + +constexpr bool test() { + using Ptr = std::tuple<int>*; + testOne<cpp20_input_iterator<Ptr>>(); + testOne<forward_iterator<Ptr>>(); + testOne<bidirectional_iterator<Ptr>>(); + testOne<random_access_iterator<Ptr>>(); + testOne<contiguous_iterator<Ptr>>(); + testOne<Ptr>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/deref.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr decltype(auto) operator*() const; + +#include <array> +#include <cassert> +#include <ranges> +#include <tuple> +#include <utility> + +template <std::size_t N, class T, std::size_t Size> +constexpr void testReference(T (&ts)[Size]) { + auto ev = ts | std::views::elements<N>; + auto it = ev.begin(); + + decltype(auto) result = *it; + using ExpectedType = decltype(std::get<N>(ts[0])); + static_assert(std::is_same_v<decltype(result), ExpectedType>); + + if constexpr (std::is_reference_v<ExpectedType>) { + // tuple/array/pair + assert(&result == &std::get<N>(ts[0])); + } else { + // subrange + assert(result == std::get<N>(ts[0])); + } +} + +// LWG 3502 elements_view should not be allowed to return dangling references +template <std::size_t N, class T> +constexpr void testValue(T t) { + auto ev = std::views::iota(0, 1) | std::views::transform([&t](int) { return t; }) | std::views::elements<N>; + auto it = ev.begin(); + + decltype(auto) result = *it; + using ExpectedType = std::remove_cvref_t<decltype(std::get<N>(t))>; + static_assert(std::is_same_v<decltype(result), ExpectedType>); + + assert(result == std::get<N>(t)); +} + +constexpr bool test() { + // test tuple + { + std::tuple<int, short, long> ts[] = {{1, 2, 3}, {4, 5, 6}}; + testReference<0>(ts); + testReference<1>(ts); + testReference<2>(ts); + testValue<0>(ts[0]); + testValue<1>(ts[0]); + testValue<2>(ts[0]); + } + + // test pair + { + std::pair<int, short> ps[] = {{1, 2}, {4, 5}}; + testReference<0>(ps); + testReference<1>(ps); + testValue<0>(ps[0]); + testValue<1>(ps[0]); + } + + // test array + { + std::array<int, 3> arrs[] = {{1, 2, 3}, {3, 4, 5}}; + testReference<0>(arrs); + testReference<1>(arrs); + testReference<2>(arrs); + testValue<0>(arrs[0]); + testValue<1>(arrs[0]); + testValue<2>(arrs[0]); + } + + // test subrange + { + int i = 5; + std::ranges::subrange<int*> srs[] = {{&i, &i}, {&i, &i}}; + testReference<0>(srs); + testReference<1>(srs); + testValue<0>(srs[0]); + testValue<1>(srs[0]); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/increment.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int) requires forward_range<Base>; + +#include <array> +#include <cassert> +#include <ranges> +#include <tuple> + +#include "test_iterators.h" + +template <class Iter, class Sent = sentinel_wrapper<Iter>> +constexpr void testOne() { + using Range = std::ranges::subrange<Iter, Sent>; + std::tuple<int> ts[] = {{1}, {2}, {3}}; + auto ev = Range{Iter{&ts[0]}, Sent{Iter{&ts[0] + 3}}} | std::views::elements<0>; + + using ElementIter = std::ranges::iterator_t<decltype(ev)>; + + // ++i + { + auto it = ev.begin(); + decltype(auto) result = ++it; + + static_assert(std::is_same_v<decltype(result), ElementIter&>); + assert(&result == &it); + + assert(base(it.base()) == &ts[1]); + } + + // i++ + { + if constexpr (std::forward_iterator<Iter>) { + auto it = ev.begin(); + decltype(auto) result = it++; + + static_assert(std::is_same_v<decltype(result), ElementIter>); + + assert(base(it.base()) == &ts[1]); + assert(base(result.base()) == &ts[0]); + } else { + auto it = ev.begin(); + it++; + + static_assert(std::is_same_v<decltype(it++), void>); + assert(base(it.base()) == &ts[1]); + } + } +} + +constexpr bool test() { + using Ptr = std::tuple<int>*; + testOne<cpp20_input_iterator<Ptr>>(); + testOne<forward_iterator<Ptr>>(); + testOne<bidirectional_iterator<Ptr>>(); + testOne<random_access_iterator<Ptr>>(); + testOne<contiguous_iterator<Ptr>>(); + testOne<Ptr>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/member_types.compile.pass.cpp @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// Member typedefs in elements_view<V, N>::iterator. + +#include <concepts> +#include <ranges> +#include <tuple> + +#include "test_macros.h" +#include "test_iterators.h" + +template <class Iter> +using Range = std::ranges::subrange<Iter, sentinel_wrapper<Iter>>; + +template <class Range, size_t N = 0> +using ElementsIter = std::ranges::iterator_t<std::ranges::elements_view<Range, N>>; + +// using iterator_concept = see below; +static_assert(std::same_as<ElementsIter<Range<cpp20_input_iterator<std::tuple<int>*>>>::iterator_concept, + std::input_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<forward_iterator<std::tuple<int>*>>>::iterator_concept, // + std::forward_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<bidirectional_iterator<std::tuple<int>*>>>::iterator_concept, + std::bidirectional_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<random_access_iterator<std::tuple<int>*>>>::iterator_concept, + std::random_access_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<contiguous_iterator<std::tuple<int>*>>>::iterator_concept, + std::random_access_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<std::tuple<int>*>>::iterator_concept, // + std::random_access_iterator_tag>); + +// using iterator_category = see below; // not always present +template <class T> +concept HasIterCategory = requires { typename T::iterator_category; }; + +static_assert(!HasIterCategory<ElementsIter<Range<cpp20_input_iterator<std::tuple<int>*>>>>); +static_assert(HasIterCategory<ElementsIter<Range<forward_iterator<std::tuple<int>*>>>>); + +static_assert(std::same_as<ElementsIter<Range<forward_iterator<std::tuple<int>*>>>::iterator_category, + std::forward_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<bidirectional_iterator<std::tuple<int>*>>>::iterator_category, + std::bidirectional_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<random_access_iterator<std::tuple<int>*>>>::iterator_category, + std::random_access_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<contiguous_iterator<std::tuple<int>*>>>::iterator_category, + std::random_access_iterator_tag>); + +static_assert(std::same_as<ElementsIter<Range<std::tuple<int>*>>::iterator_category, // + std::random_access_iterator_tag>); + +using Generator = decltype(std::views::iota(0, 1) | std::views::transform([](int) { + return std::pair<int, short>{1, 1}; + })); +static_assert(std::ranges::random_access_range<Generator>); + +static_assert(std::same_as<ElementsIter<Generator>::iterator_category, // + std::input_iterator_tag>); + +// using value_type = remove_cvref_t<tuple_element_t<N, range_value_t<Base>>>; +static_assert(std::same_as<ElementsIter<Range<std::tuple<int, long>*>, 0>::value_type, int>); + +static_assert(std::same_as<ElementsIter<Range<std::tuple<int, long>*>, 1>::value_type, long>); + +static_assert(std::same_as<ElementsIter<Generator, 0>::value_type, int>); + +static_assert(std::same_as<ElementsIter<Generator, 1>::value_type, short>); + +// using difference_type = range_difference_t<Base>; +static_assert(std::same_as<ElementsIter<Range<std::tuple<int, long>*>>::difference_type, + std::ranges::range_difference_t<Range<std::tuple<int, long>*>>>); + +static_assert(std::same_as<ElementsIter<Generator>::difference_type, // + std::ranges::range_difference_t<Generator>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/iterator/subscript.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr decltype(auto) operator[](difference_type n) const +// requires random_access_range<Base> + +#include <cassert> +#include <ranges> +#include <tuple> + +#include "test_iterators.h" + +template <class T, class U> +concept CanSubscript = requires(T t, U u) { t[u]; }; + +template <class BaseRange> +using ElemIter = std::ranges::iterator_t<std::ranges::elements_view<BaseRange, 0>>; + +using RandomAccessRange = std::ranges::subrange<std::tuple<int>*>; +static_assert(std::ranges::random_access_range<RandomAccessRange>); + +static_assert(CanSubscript<ElemIter<RandomAccessRange>, int>); + +using BidiRange = std::ranges::subrange<bidirectional_iterator<std::tuple<int>*>>; +static_assert(!std::ranges::random_access_range<BidiRange>); + +static_assert(!CanSubscript<ElemIter<BidiRange>, int>); + +constexpr bool test() { + { + // reference + std::tuple<int> ts[] = {{1}, {2}, {3}, {4}}; + auto ev = ts | std::views::elements<0>; + auto it = ev.begin(); + + assert(&it[0] == &*it); + assert(&it[2] == &*(it + 2)); + + static_assert(std::is_same_v<decltype(it[2]), int&>); + } + + { + // value + auto ev = std::views::iota(0, 5) | std::views::transform([](int i) { return std::tuple<int>{i}; }) | + std::views::elements<0>; + auto it = ev.begin(); + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v<decltype(it[2]), int>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/range.concept.compile.pass.cpp @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// concept checking +// +// template<class T, size_t N> +// concept has-tuple-element = +// tuple-like<T> && N < tuple_size_v<T>; +// +// template<class T, size_t N> +// concept returnable-element = +// is_reference_v<T> || move_constructible<tuple_element_t<N, T>>; +// +// template<input_range V, size_t N> +// requires view<V> && has-tuple-element<range_value_t<V>, N> && +// has-tuple-element<remove_reference_t<range_reference_t<V>>, N> && +// returnable-element<range_reference_t<V>, N> +// class elements_view; + +#include <array> +#include <concepts> +#include <tuple> +#include <ranges> +#include <utility> + +#include "test_iterators.h" + +template <class It> +using Range = std::ranges::subrange<It, sentinel_wrapper<It>>; + +template <class V, size_t N> +concept HasElementsView = requires { typename std::ranges::elements_view<V, N>; }; + +static_assert(HasElementsView<Range<std::ranges::subrange<int*>*>, 0>); +static_assert(HasElementsView<Range<std::pair<int, int>*>, 1>); +static_assert(HasElementsView<Range<std::tuple<int, int, int>*>, 2>); +static_assert(HasElementsView<Range<std::array<int, 4>*>, 3>); + +// !view<V> +static_assert(!std::ranges::view<std::array<std::tuple<int>, 1>>); +static_assert(!HasElementsView<std::array<std::tuple<int>, 1>, 0>); + +// !input_range +static_assert(!std::ranges::input_range<Range<cpp20_output_iterator<std::tuple<int>*>>>); +static_assert(!HasElementsView<Range<cpp20_output_iterator<std::tuple<int>*>>, 0>); + +// !tuple-like +LIBCPP_STATIC_ASSERT(!std::__tuple_like<int>); +static_assert(!HasElementsView<Range<int*>, 1>); + +// !(N < tuple_size_v<T>) +static_assert(!(2 < std::tuple_size_v< std::pair<int, int>>)); +static_assert(!HasElementsView<Range<std::pair<int, int>*>, 2>); + +// ! (is_reference_v<T> || move_constructible<tuple_element_t<N, T>>) +struct NonMovable { + NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; +static_assert(!std::move_constructible<NonMovable>); + +using NonMovableGenerator = + decltype(std::views::iota(0, 1) | std::views::transform([](int) { + return std::pair<NonMovable, int>{1, 1}; + })); + +static_assert(!HasElementsView<NonMovableGenerator, 0>); +static_assert(HasElementsView<NonMovableGenerator, 1>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/base.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr sentinel_t<Base> base() const; + +#include <cassert> +#include <ranges> +#include <tuple> +#include <type_traits> +#include <utility> + +struct Sent { + int i; + + friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; } +}; + +constexpr bool test() { + using BaseRange = std::ranges::subrange<std::tuple<int>*, Sent>; + using EleRange = std::ranges::elements_view<BaseRange, 0>; + using EleSent = std::ranges::sentinel_t<EleRange>; + + const EleSent st{Sent{5}}; + std::same_as<Sent> decltype(auto) base = st.base(); + assert(base.i == 5); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.base.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr explicit sentinel(sentinel_t<Base> end); + +#include <cassert> +#include <ranges> +#include <tuple> +#include <utility> + +struct Sent { + int i; + + friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; } +}; + +struct Range : std::ranges::view_base { + std::tuple<int>* begin() const; + Sent end(); +}; + +// Test explicit + +static_assert(std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, Sent>); +static_assert(!std::is_convertible_v<Sent, std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>); + +constexpr bool test() { + // base is init correctly + { + using R = std::ranges::elements_view<Range, 0>; + using Sentinel = std::ranges::sentinel_t<R>; + + Sentinel s1(Sent{5}); + assert(s1.base().i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.convert.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr sentinel(sentinel<!Const> s) +// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>; + +#include <cassert> +#include <ranges> +#include <tuple> + +#include "../types.h" + +struct Sent { + int i; + constexpr Sent() = default; + constexpr Sent(int ii) : i(ii) {} + friend constexpr bool operator==(std::tuple<int>*, const Sent&) { return true; } +}; + +struct ConstSent { + int i; + constexpr ConstSent() = default; + constexpr ConstSent(int ii) : i(ii) {} + constexpr ConstSent(const Sent& s) : i(s.i) {} + friend constexpr bool operator==(std::tuple<int>*, const ConstSent&) { return true; } +}; + +struct Range : std::ranges::view_base { + std::tuple<int>* begin() const; + Sent end(); + ConstSent end() const; +}; + +struct NonConvertConstSent { + int i; + constexpr NonConvertConstSent() = default; + constexpr NonConvertConstSent(int ii) : i(ii) {} + friend constexpr bool operator==(std::tuple<int>*, const NonConvertConstSent&) { return true; } +}; + +struct NonConvertConstSentRange : std::ranges::view_base { + std::tuple<int>* begin() const; + Sent end(); + NonConvertConstSent end() const; +}; + +// Test Constraint +static_assert(std::is_constructible_v<std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>, + std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>>); + +// !Const +static_assert(!std::is_constructible_v<std::ranges::sentinel_t<std::ranges::elements_view<Range, 0>>, + std::ranges::sentinel_t<const std::ranges::elements_view<Range, 0>>>); + +// !convertible_to<sentinel_t<V>, sentinel_t<Base>> +static_assert(!std::is_constructible_v< + std::ranges::sentinel_t<const std::ranges::elements_view<NonConvertConstSentRange, 0>>, + std::ranges::sentinel_t<std::ranges::elements_view<NonConvertConstSentRange, 0>>>); + +constexpr bool test() { + // base is init correctly + { + using R = std::ranges::elements_view<Range, 0>; + using Sentinel = std::ranges::sentinel_t<R>; + using ConstSentinel = std::ranges::sentinel_t<const R>; + static_assert(!std::same_as<Sentinel, ConstSentinel>); + + Sentinel s1(Sent{5}); + ConstSentinel s2 = s1; + assert(s2.base().i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/ctor.default.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// sentinel() = default; + +#include <cassert> +#include <ranges> +#include <tuple> + +struct PODSentinel { + int i; // deliberately uninitialised + + friend constexpr bool operator==(std::tuple<int>*, const PODSentinel&) { return true; } +}; + +struct Range : std::ranges::view_base { + std::tuple<int>* begin() const; + PODSentinel end(); +}; + +constexpr bool test() { + using EleView = std::ranges::elements_view<Range, 0>; + using Sentinel = std::ranges::sentinel_t<EleView>; + static_assert(!std::is_same_v<Sentinel, std::ranges::iterator_t<EleView>>); + + { + Sentinel s; + assert(s.base().i == 0); + } + { + Sentinel s = {}; + assert(s.base().i == 0); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/equality.pass.cpp @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template<bool OtherConst> +// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); + +#include <array> +#include <cassert> +#include <ranges> + +#include "../types.h" + +template <bool Const> +struct Iter { + std::tuple<int>* it_; + + using value_type = std::tuple<int>; + using difference_type = intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr decltype(auto) operator*() const { return *it_; } + constexpr Iter& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } +}; + +template <bool Const> +struct Sent { + std::tuple<int>* end_; + + constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } +}; + +template <bool Const> +struct CrossComparableSent { + std::tuple<int>* end_; + + template <bool C> + constexpr bool operator==(const Iter<C>& i) const { + return i.it_ == end_; + } +}; + +template <template <bool> typename St> +struct Range : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr Iter<false> begin() { return Iter<false>{buffer_}; } + constexpr Iter<true> begin() const { return Iter<true>{buffer_}; } + constexpr St<false> end() { return St<false>{buffer_ + size_}; } + constexpr St<true> end() const { return St<true>{buffer_ + size_}; } +}; + +using R = Range<Sent>; +using CrossComparableR = Range<CrossComparableSent>; + +// Test Constraint +template <class I, class S> +concept HasEqual = requires(const I i, const S s) { i == s; }; + +using std::ranges::elements_view; +using std::ranges::iterator_t; +using std::ranges::sentinel_t; + +static_assert(HasEqual<iterator_t<elements_view<R, 0>>, // + sentinel_t<elements_view<R, 0>>>); + +static_assert(!HasEqual<iterator_t<const elements_view<R, 0>>, // + sentinel_t<elements_view<R, 0>>>); + +static_assert(!HasEqual<iterator_t<elements_view<R, 0>>, // + sentinel_t<const elements_view<R, 0>>>); + +static_assert(HasEqual<iterator_t<const elements_view<R, 0>>, // + sentinel_t<const elements_view<R, 0>>>); + +static_assert(HasEqual<iterator_t<elements_view<R, 0>>, // + sentinel_t<elements_view<R, 0>>>); + +static_assert(HasEqual<iterator_t<const elements_view<CrossComparableR, 0>>, // + sentinel_t<elements_view<CrossComparableR, 0>>>); + +static_assert(HasEqual<iterator_t<elements_view<CrossComparableR, 0>>, // + sentinel_t<const elements_view<CrossComparableR, 0>>>); + +static_assert(HasEqual<iterator_t<const elements_view<CrossComparableR, 0>>, // + sentinel_t<const elements_view<CrossComparableR, 0>>>); + +template <class R, bool ConstIter, bool ConstSent> +constexpr void testOne() { + auto getBegin = [](auto&& rng) { + if constexpr (ConstIter) { + return std::as_const(rng).begin(); + } else { + return rng.begin(); + } + }; + + auto getEnd = [](auto&& rng) { + if constexpr (ConstSent) { + return std::as_const(rng).end(); + } else { + return rng.end(); + } + }; + + // iter == sentinel.base + { + std::tuple<int> buffer[] = {{1}}; + R v{buffer}; + std::ranges::elements_view<R, 0> ev(v); + auto iter = getBegin(ev); + auto st = getEnd(ev); + ++iter; + assert(iter == st); + } + + // iter != sentinel.base + { + std::tuple<int> buffer[] = {{1}}; + R v{buffer}; + std::ranges::elements_view<R, 0> ev(v); + auto iter = getBegin(ev); + auto st = getEnd(ev); + assert(iter != st); + } + + // empty range + { + std::array<std::tuple<int>, 0> arr; + R v{arr}; + std::ranges::elements_view<R, 0> ev(v); + auto iter = getBegin(ev); + auto sent = getEnd(ev); + assert(iter == sent); + } +} + +constexpr bool test() { + testOne<R, false, false>(); + testOne<R, true, true>(); + testOne<CrossComparableR, false, false>(); + testOne<CrossComparableR, true, true>(); + testOne<CrossComparableR, true, false>(); + testOne<CrossComparableR, false, true>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp @@ -0,0 +1,213 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template<bool OtherConst> +// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr range_difference_t<maybe-const<OtherConst, V>> +// operator-(const iterator<OtherConst>& x, const sentinel& y); +// +// template<bool OtherConst> +// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr range_difference_t<maybe-const<OtherConst, V>> +// operator-(const sentinel& x, const iterator<OtherConst>& y); + +#include <cassert> +#include <concepts> +#include <functional> +#include <ranges> +#include <tuple> + +#include "../types.h" + +template <bool Const> +struct Iter { + std::tuple<int>* it_; + + using value_type = std::tuple<int>; + using difference_type = intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr decltype(auto) operator*() const { return *it_; } + constexpr Iter& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } +}; + +template <bool Const> +struct Sent { + std::tuple<int>* end_; + + constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } +}; + +template <bool Const> +struct SizedSent { + std::tuple<int>* end_; + + constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } + + friend constexpr auto operator-(const SizedSent& st, const Iter<Const>& it) { return st.end_ - it.it_; } + + friend constexpr auto operator-(const Iter<Const>& it, const SizedSent& st) { return it.it_ - st.end_; } +}; + +template <bool Const> +struct CrossSizedSent { + std::tuple<int>* end_; + + template <bool C> + constexpr bool operator==(const Iter<C>& i) const { + return i.it_ == end_; + } + + template <bool C> + friend constexpr auto operator-(const CrossSizedSent& st, const Iter<C>& it) { + return st.end_ - it.it_; + } + + template <bool C> + friend constexpr auto operator-(const Iter<C>& it, const CrossSizedSent& st) { + return it.it_ - st.end_; + } +}; + +template <template <bool> class It, template <bool> class St> +struct Range : TupleBufferView { + using TupleBufferView::TupleBufferView; + + using iterator = It<false>; + using sentinel = St<false>; + using const_iterator = It<true>; + using const_sentinel = St<true>; + + constexpr iterator begin() { return {buffer_}; } + constexpr const_iterator begin() const { return {buffer_}; } + constexpr sentinel end() { return sentinel{buffer_ + size_}; } + constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; } +}; + +template <class T, class U> +concept HasMinus = requires(const T t, const U u) { t - u; }; + +template <class BaseRange> +using ElementsView = std::ranges::elements_view<BaseRange, 0>; + +template <class BaseRange> +using ElemIter = std::ranges::iterator_t<ElementsView<BaseRange>>; + +template <class BaseRange> +using EleConstIter = std::ranges::iterator_t<const ElementsView<BaseRange>>; + +template <class BaseRange> +using EleSent = std::ranges::sentinel_t<ElementsView<BaseRange>>; + +template <class BaseRange> +using EleConstSent = std::ranges::sentinel_t<const ElementsView<BaseRange>>; + +constexpr void testConstraints() { + // base is not sized + { + using Base = Range<Iter, Sent>; + static_assert(!HasMinus<EleSent<Base>, ElemIter<Base>>); + static_assert(!HasMinus<ElemIter<Base>, EleSent<Base>>); + + static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>); + static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>); + + static_assert(!HasMinus<EleConstSent<Base>, EleConstIter<Base>>); + static_assert(!HasMinus<EleConstIter<Base>, EleConstSent<Base>>); + + static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>); + static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>); + } + + // base is sized but not cross const + { + using Base = Range<Iter, SizedSent>; + static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>); + static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>); + + static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>); + static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>); + + static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>); + static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>); + + static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>); + static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>); + } + + // base is cross const sized + { + using Base = Range<Iter, CrossSizedSent>; + static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>); + static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>); + + static_assert(HasMinus<EleSent<Base>, EleConstIter<Base>>); + static_assert(HasMinus<EleConstIter<Base>, EleSent<Base>>); + + static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>); + static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>); + + static_assert(HasMinus<EleConstSent<Base>, ElemIter<Base>>); + static_assert(HasMinus<ElemIter<Base>, EleConstSent<Base>>); + } +} + +constexpr bool test() { + std::tuple<int> buffer[] = {{1}, {2}, {3}, {4}, {5}}; + + // base is sized but not cross const + { + using Base = Range<Iter, SizedSent>; + Base base{buffer}; + auto ev = base | std::views::elements<0>; + auto iter = ev.begin(); + auto const_iter = std::as_const(ev).begin(); + auto sent = ev.end(); + auto const_sent = std::as_const(ev).end(); + + assert(iter - sent == -5); + assert(sent - iter == 5); + assert(const_iter - const_sent == -5); + assert(const_sent - const_iter == 5); + } + + // base is cross const sized + { + using Base = Range<Iter, CrossSizedSent>; + Base base{buffer}; + auto ev = base | std::views::elements<0>; + auto iter = ev.begin(); + auto const_iter = std::as_const(ev).begin(); + auto sent = ev.end(); + auto const_sent = std::as_const(ev).end(); + + assert(iter - sent == -5); + assert(sent - iter == 5); + assert(iter - const_sent == -5); + assert(const_sent - iter == 5); + assert(const_iter - sent == -5); + assert(sent - const_iter == 5); + assert(const_iter - const_sent == -5); + assert(const_sent - const_iter == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto size() requires sized_range<V> +// constexpr auto size() const requires sized_range<const V> + +#include <cassert> +#include <ranges> +#include <tuple> +#include <utility> + +#include "types.h" + +template <class T> +concept HasSize = requires(T t) { t.size(); }; + +static_assert(HasSize<std::ranges::elements_view<SimpleCommon, 0>>); +static_assert(HasSize<const std::ranges::elements_view<SimpleCommon, 0>>); + +struct NonSized : std::ranges::view_base { + using iterator = forward_iterator<std::tuple<int>*>; + iterator begin() const; + iterator end() const; +}; +static_assert(!std::ranges::sized_range<NonSized>); +static_assert(!std::ranges::sized_range<const NonSized>); + +static_assert(!HasSize<std::ranges::elements_view<NonSized, 0>>); +static_assert(!HasSize<const std::ranges::elements_view<NonSized, 0>>); + +struct SizedNonConst : TupleBufferView { + using TupleBufferView::TupleBufferView; + + using iterator = forward_iterator<std::tuple<int>*>; + constexpr auto begin() const { return iterator{buffer_}; } + constexpr auto end() const { return iterator{buffer_ + size_}; } + constexpr std::size_t size() { return size_; } +}; + +static_assert(HasSize<std::ranges::elements_view<SizedNonConst, 0>>); +static_assert(!HasSize<const std::ranges::elements_view<SizedNonConst, 0>>); + +struct OnlyConstSized : TupleBufferView { + using TupleBufferView::TupleBufferView; + + using iterator = forward_iterator<std::tuple<int>*>; + constexpr auto begin() const { return iterator{buffer_}; } + constexpr auto end() const { return iterator{buffer_ + size_}; } + constexpr std::size_t size() const { return size_; } + constexpr std::size_t size() = delete; +}; + +static_assert(HasSize<const OnlyConstSized>); +static_assert(HasSize<std::ranges::elements_view<OnlyConstSized, 0>>); +static_assert(HasSize<const std::ranges::elements_view<OnlyConstSized, 0>>); + +constexpr bool test() { + std::tuple<int> buffer[] = {{1}, {2}, {3}}; + + // non-const and const are sized + { + auto ev = std::views::elements<0>(buffer); + assert(ev.size() == 3); + assert(std::as_const(ev).size() == 3); + } + + { + // const-view non-sized range + auto ev = std::views::elements<0>(SizedNonConst{buffer}); + assert(ev.size() == 3); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/types.h b/libcxx/test/std/ranges/range.adaptors/range.elements/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/types.h @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H + +#include <array> +#include <functional> +#include <ranges> +#include <tuple> + +#include "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" + +template <class T> +struct BufferView : std::ranges::view_base { + T* buffer_; + std::size_t size_; + + template <std::size_t N> + constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} + + template <std::size_t N> + constexpr BufferView(std::array<T, N>& arr) : buffer_(arr.data()), size_(N) {} +}; + +using TupleBufferView = BufferView<std::tuple<int>>; + +template <bool Simple> +struct Common : TupleBufferView { + using TupleBufferView::TupleBufferView; + + constexpr std::tuple<int>* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const std::tuple<int>* begin() const { return buffer_; } + constexpr std::tuple<int>* end() + requires(!Simple) + { + return buffer_ + size_; + } + constexpr const std::tuple<int>* end() const { return buffer_ + size_; } +}; +using SimpleCommon = Common<true>; +using NonSimpleCommon = Common<false>; + +using SimpleCommonRandomAccessSized = SimpleCommon; +using NonSimpleCommonRandomAccessSized = NonSimpleCommon; + +static_assert(std::ranges::common_range<Common<true>>); +static_assert(std::ranges::random_access_range<SimpleCommon>); +static_assert(std::ranges::sized_range<SimpleCommon>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommon>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommon>); + +template <bool Simple> +struct NonCommon : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr std::tuple<int>* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const std::tuple<int>* begin() const { return buffer_; } + constexpr sentinel_wrapper<std::tuple<int>*> end() + requires(!Simple) + { + return sentinel_wrapper<std::tuple<int>*>(buffer_ + size_); + } + constexpr sentinel_wrapper<const std::tuple<int>*> end() const { + return sentinel_wrapper<const std::tuple<int>*>(buffer_ + size_); + } +}; + +using SimpleNonCommon = NonCommon<true>; +using NonSimpleNonCommon = NonCommon<false>; + +static_assert(!std::ranges::common_range<SimpleNonCommon>); +static_assert(std::ranges::random_access_range<SimpleNonCommon>); +static_assert(!std::ranges::sized_range<SimpleNonCommon>); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommon>); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommon>); + +template <class Derived> +struct IterBase { + using iterator_concept = std::random_access_iterator_tag; + using value_type = std::tuple<int>; + using difference_type = intptr_t; + + constexpr std::tuple<int> operator*() const { return std::tuple<int>(5); } + + constexpr Derived& operator++() { return *this; } + constexpr void operator++(int) {} + + friend constexpr bool operator==(const IterBase&, const IterBase&) = default; +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn --- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn +++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn @@ -628,10 +628,12 @@ "__tree", "__tuple_dir/apply_cv.h", "__tuple_dir/make_tuple_types.h", + "__tuple_dir/pair_like.h", "__tuple_dir/sfinae_helpers.h", "__tuple_dir/tuple_element.h", "__tuple_dir/tuple_indices.h", "__tuple_dir/tuple_like.h", + "__tuple_dir/tuple_like_ext.h", "__tuple_dir/tuple_size.h", "__tuple_dir/tuple_types.h", "__type_traits/add_const.h",