diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -66,6 +66,7 @@ - P0415R1 - ``constexpr`` for ``std::complex`` - P1208R6 - ``std::source_location`` - P0323R12 - ``std::expected`` +- 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 `__","``{to,from}_chars_result``\ and ``format_to_n_result``\ need the ""we really mean what we say"" wording","Prague","|Complete|","14.0","|format|" "`3374 `__","P0653 + P1006 should have made the other ``std::to_address``\ overload ``constexpr``\ ","Prague","|Complete|","12.0" "`3375 `__","``decay``\ in ``viewable_range``\ should be ``remove_cvref``\ ","Prague","|Complete|","15.0","|ranges|" -"`3377 `__","``elements_view::iterator``\ befriends a specialization of itself","Prague","","","|ranges|" +"`3377 `__","``elements_view::iterator``\ befriends a specialization of itself","Prague","|Nothing To Do|","","|ranges|" "`3379 `__","""``safe``\ "" in several library names is misleading","Prague","|Complete|","15.0","|ranges|" "`3380 `__","``common_type``\ and comparison categories","Prague","|Complete|","15.0","|spaceship|" "`3381 `__","``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 `__","CWG","More constexpr containers","Cologne","|Complete|","12.0" "`P0980R1 `__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0" "`P1004R2 `__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0" -"`P1035R7 `__","LWG","Input Range Adaptors (mostly implemented, TODO: elements_view)","Cologne","|In Progress|","","|ranges|" +"`P1035R7 `__","LWG","Input Range Adaptors","Cologne","|Complete|","16.0","|ranges|" "`P1065R2 `__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0" "`P1135R6 `__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0" "`P1207R4 `__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0","|ranges|" @@ -182,7 +182,7 @@ "`P1981R0 `__","LWG","Rename leap to leap_second","Prague","* *","" "`P1982R0 `__","LWG","Rename link to time_zone_link","Prague","* *","" "`P1983R0 `__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|" -"`P1994R1 `__","LWG","elements_view needs its own sentinel","Prague","* *","","|ranges|" +"`P1994R1 `__","LWG","elements_view needs its own sentinel","Prague","Complete","16.0","|ranges|" "`P2002R1 `__","CWG","Defaulted comparison specification cleanups","Prague","* *","" "`P2045R1 `__","LWG","Missing Mandates for the standard library","Prague","* *","" "`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 `__","Domain of ``ranges::ssize(E)`` doesn't ``match ranges::size(E)``","November 2020","","","|ranges|" "`3404 `__","Finish removing subrange's conversions from pair-like","November 2020","","","|ranges|" "`3405 `__","``common_view``'s converting constructor is bad, too","November 2020","|Complete|","14.0","|ranges|" -"`3406 `__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","November 2020","","","|ranges|" +"`3406 `__","``elements_view::begin()`` and ``elements_view::end()`` have incompatible constraints","November 2020","|Complete|","16.0","|ranges|" "`3419 `__","[algorithms.requirements]/15 doesn't reserve as many rights as it intends to","November 2020","|Nothing To Do|","" "`3420 `__","cpp17-iterator should check that the type looks like an iterator first","November 2020","|Complete|","14.0","|ranges|" "`3421 `__","Imperfect ADL emulation for boolean-testable","November 2020","|Nothing To Do|","","|ranges|" @@ -53,11 +53,11 @@ "`3391 `__","Problems with ``counted_iterator``/``move_iterator::base() const &``","February 2021","","","|ranges|" "`3433 `__","``subrange::advance(n)`` has UB when ``n < 0``","February 2021","|Complete|","14.0","|ranges|" "`3490 `__","``ranges::drop_while_view::begin()`` is missing a precondition","February 2021","|Nothing To Do|","","|ranges|" -"`3492 `__","Minimal improvements to ``elements_view::iterator``","February 2021","","","|ranges|" +"`3492 `__","Minimal improvements to ``elements_view::iterator``","February 2021","|Complete|","16.0","|ranges|" "`3494 `__","Allow ranges to be conditionally borrowed","February 2021","Superseded by `P2017R1 `__","","|ranges|" "`3495 `__","``constexpr launder`` makes pointers to inactive members of unions usable","February 2021","|Nothing To Do|","" "`3500 `__","``join_view::iterator::operator->()`` is bogus","February 2021","|Complete|","14.0","|ranges|" -"`3502 `__","``elements_view`` should not be allowed to return dangling reference","February 2021","","","|ranges|" +"`3502 `__","``elements_view`` should not be allowed to return dangling reference","February 2021","|Complete|","16.0","|ranges|" "`3505 `__","``split_view::outer-iterator::operator++`` misspecified","February 2021","","","|ranges|" "","","","","","" `2774 `__,"``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] `_,| counted_iterator,None,Unassigned,|Not Started| | `[range.iota.iterator] `_,| `ranges::iota_view::iterator `_,[concepts.cmp],Arthur O'Dwyer,|Complete| | `[range.transform.iterator] `_,| `ranges::transform_view::iterator `_,[concepts.cmp],Arthur O'Dwyer,|Complete| -| `[range.elements.iterator] `_,| ranges::elements_view::iterator,[concepts.cmp],Unassigned,|Not Started| +| `[range.elements.iterator] `_,| ranges::elements_view::iterator,[concepts.cmp],Hui Xie,|Complete| | `[time.duration.comparisons] `_, "chrono::duration", None, Mark de Wever, |Not Started| | `[time.point.comparisons] `_, "chrono::time_point", None, Mark de Wever, |Not Started| "| `[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 @@ -364,6 +364,7 @@ __fwd/span.h __fwd/string.h __fwd/string_view.h + __fwd/subrange.h __fwd/tuple.h __hash_table __ios/fpos.h @@ -503,6 +504,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 @@ -554,10 +556,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 @@ -90,6 +92,24 @@ get(const array<_Tp, _Size>&&) _NOEXCEPT; #endif +#if _LIBCPP_STD_VER >= 20 + +namespace ranges { + +template + requires((_Index == 0 && copyable<_Iter>) || _Index == 1) +_LIBCPP_HIDE_FROM_ABI constexpr auto get(const subrange<_Iter, _Sent, _Kind>& __subrange); + +template + 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 _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 + +#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 __elements_view_iterator; + +template +class __elements_view_sentinel; + +template +concept __has_tuple_element = __tuple_like<_Tp> && _Np < tuple_size<_Tp>::value; + +template +concept __returnable_element = is_reference_v<_Tp> || move_constructible>; + +template + requires view<_View> && __has_tuple_element, _Np> && + __has_tuple_element>, _Np> && + __returnable_element, _Np> +class elements_view : public view_interface> { +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(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return __iterator(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View> && !common_range<_View>) + { + return __sentinel{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View> && common_range<_View>) + { + return __iterator{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range + { + return __sentinel{ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires common_range + { + return __iterator{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 + { + return ranges::size(__base_); + } + +private: + template + using __iterator = __elements_view_iterator<_View, _Np, _Const>; + + template + using __sentinel = __elements_view_sentinel<_View, _Np, _Const>; + + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); +}; + +template +struct __elements_view_iterator_category_base {}; + +template +struct __elements_view_iterator_category_base<_Base, _Np> { + static consteval auto __get_iterator_category() { + using _Result = decltype(std::get<_Np>(*std::declval>())); + using _Cat = typename iterator_traits>::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 __elements_view_iterator : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> { + template + friend class __elements_view_iterator; + + template + 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>) { + return std::get<_Np>(*__i); + } else { + using _Element = remove_cv_t>>; + 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>>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI __elements_view_iterator() + requires default_initializable> + = 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<_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> + { + 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> + { + 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>> + { + return __x.__current_ - __y.__current_; + } +}; + +template +class __elements_view_sentinel { +private: + using _Base = __maybe_const<_Const, _View>; + _LIBCPP_NO_UNIQUE_ADDRESS sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + template + friend class __elements_view_sentinel; + + template + _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<_Base>> + : __end_(std::move(__other.__end_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; } + + template + requires sentinel_for, 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 + requires sized_sentinel_for, 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 + requires sized_sentinel_for, 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 +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Tp>; + +template +using keys_view = elements_view<_Tp, 0>; +template +using values_view = elements_view<_Tp, 1>; + +namespace views { +namespace __elements { + +template +struct __fn : __range_adaptor_closure<__fn<_Np>> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + /**/ noexcept(noexcept(elements_view, _Np>(std::forward<_Range>(__range)))) + /*------*/ -> decltype(elements_view, _Np>(std::forward<_Range>(__range))) { + /*-------------*/ return elements_view, _Np>(std::forward<_Range>(__range)); + } +}; +} // namespace __elements + +inline namespace __cpo { +template +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<_To>>; - template - concept __pair_like = - !is_reference_v<_Tp> && requires(_Tp __t) { - typename tuple_size<_Tp>::type; // Ensures `tuple_size` is complete. - requires derived_from, integral_constant>; - typename tuple_element_t<0, remove_const_t<_Tp>>; - typename tuple_element_t<1, remove_const_t<_Tp>>; - { std::get<0>(__t) } -> convertible_to&>; - { std::get<1>(__t) } -> convertible_to&>; - }; - template 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 _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 +concept __pair_like = __tuple_like<_Tp> && tuple_size>::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 >::value, - bool = __tuple_like<_Up>::value> +template >::value, + bool = __tuple_like_ext<_Up>::value> struct __tuple_convertible : public false_type {}; @@ -73,8 +73,8 @@ // __tuple_constructible -template >::value, - bool = __tuple_like<_Up>::value> +template >::value, + bool = __tuple_like_ext<_Up>::value> struct __tuple_constructible : public false_type {}; @@ -88,8 +88,8 @@ // __tuple_assignable -template >::value, - bool = __tuple_like<_Up>::value> +template >::value, + bool = __tuple_like_ext<_Up>::value> struct __tuple_assignable : public false_type {}; @@ -117,7 +117,7 @@ template > 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 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -23,21 +24,27 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template struct __tuple_like : false_type {}; +#if _LIBCPP_STD_VER >= 20 -template struct __tuple_like : public __tuple_like<_Tp> {}; -template struct __tuple_like : public __tuple_like<_Tp> {}; -template struct __tuple_like : public __tuple_like<_Tp> {}; +template +struct __tuple_like_impl : false_type {}; -#ifndef _LIBCPP_CXX03_LANG -template struct __tuple_like > : true_type {}; -#endif +template +struct __tuple_like_impl > : true_type {}; + +template +struct __tuple_like_impl > : true_type {}; + +template +struct __tuple_like_impl > : true_type {}; -template struct __tuple_like > : true_type {}; +template +struct __tuple_like_impl > : true_type {}; -template struct __tuple_like > : true_type {}; +template +concept __tuple_like = __tuple_like_impl>::value; -template 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 + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template struct __tuple_like_ext : false_type {}; + +template struct __tuple_like_ext : public __tuple_like_ext<_Tp> {}; +template struct __tuple_like_ext : public __tuple_like_ext<_Tp> {}; +template struct __tuple_like_ext : public __tuple_like_ext<_Tp> {}; + +#ifndef _LIBCPP_CXX03_LANG +template struct __tuple_like_ext > : true_type {}; +#endif + +template struct __tuple_like_ext > : true_type {}; + +template struct __tuple_like_ext > : true_type {}; + +template 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 @@ -1229,6 +1229,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" } @@ -1250,7 +1251,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 { @@ -1365,11 +1370,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 using borrowed_subrange_t = see below; + // [range.elements], elements view + template + requires see below + class elements_view; + + template + inline constexpr bool enable_borrowed_range> = + enable_borrowed_range; + + template + using keys_view = elements_view; + template + using values_view = elements_view; + + namespace views { + template + inline constexpr unspecified elements = unspecified; + inline constexpr auto keys = elements<0>; + inline constexpr auto values = elements<1>; + } + // [range.empty], empty view template requires is_object_v @@ -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 struct __tuple_cat_return<_Tuple0, _Tuples...> : public __tuple_cat_return_1, - __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 @@ -396,6 +396,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'}} @@ -534,6 +535,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'}} @@ -567,10 +569,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 +#include + + +struct EmptyView : std::ranges::view_base { + std::tuple* begin() const; + std::tuple* end() const; +}; + +using ElementsView = std::ranges::elements_view; + +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 +#include + +struct EmptySentinel { + friend bool operator==(std::tuple* iter, EmptySentinel) { return iter; } +}; + +struct Range : std::ranges::view_base { + std::tuple* begin() const; + EmptySentinel end() const; +}; + +using ElementsView = std::ranges::elements_view; +using ElementsSent = std::ranges::sentinel_t; + +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 +// std::views::keys +// std::views::values + +#include +#include +#include +#include +#include +#include + +template +struct View : std::ranges::view_base { + T* begin() const; + T* end() const; +}; + +static_assert(!std::is_invocable_v))>); +static_assert(!std::is_invocable_v)), View>); +static_assert(std::is_invocable_v)), View>>); +static_assert(std::is_invocable_v)), View>>); +static_assert(!std::is_invocable_v)), View>>); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v>); +static_assert(std::is_invocable_v>>); +static_assert(std::is_invocable_v>>); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v>); +static_assert(std::is_invocable_v>>); +static_assert(!std::is_invocable_v>>); + +template +concept CanBePiped = + requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; + }; + +static_assert(!CanBePiped, decltype((std::views::elements<0>))>); +static_assert(CanBePiped>, decltype((std::views::elements<0>))>); +static_assert(CanBePiped>, decltype((std::views::elements<0>))>); +static_assert(!CanBePiped>, decltype((std::views::elements<5>))>); + +static_assert(!CanBePiped, decltype((std::views::keys))>); +static_assert(CanBePiped>, decltype((std::views::keys))>); +static_assert(CanBePiped>, decltype((std::views::keys))>); + +static_assert(!CanBePiped, decltype((std::views::values))>); +static_assert(CanBePiped>, decltype((std::views::values))>); +static_assert(!CanBePiped>, decltype((std::views::values))>); + +constexpr bool test() { + std::pair buff[] = {{1, 2}, {3, 4}, {5, 6}}; + + // Test `views::elements(v)` + { + using Result = std::ranges::elements_view[3]>, 0>; + std::same_as 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[3]>, 0>; + std::same_as 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[3]>, 1>; + std::same_as decltype(auto) result = std::views::values(buff); + auto expected = {2, 4, 6}; + assert(std::ranges::equal(result, expected)); + } + + // Test `v | views::elements` + { + using Result = std::ranges::elements_view[3]>, 1>; + std::same_as 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[3]>, 0>; + std::same_as 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[3]>, 1>; + std::same_as 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> nested[] = {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view, std::tuple>[3]>, 0>, + 0>; + auto const partial = std::views::elements<0> | std::views::elements<0>; + std::same_as 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> nested[] = {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view, std::tuple>[3]>, 0>, + 0>; + auto const partial = std::views::keys | std::views::keys; + std::same_as 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> nested[] = {{{1}, {2, 3}}, {{4}, {5, 6}}, {{7}, {8, 9}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view, std::tuple>[3]>, 1>, + 1>; + auto const partial = std::views::values | std::views::values; + std::same_as 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> nested[] = {{{1, 2}, {3}}, {{4, 5}, {6}}, {{7, 8}, {9}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view, std::tuple>[3]>, 0>, + 1>; + auto const partial = std::views::keys | std::views::values; + std::same_as 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> nested[] = {{{1}, {2, 3}}, {{4}, {5, 6}}, {{7}, {8, 9}}}; + using Result = std::ranges::elements_view< + std::ranges::elements_view, std::tuple>[3]>, 1>, + 0>; + auto const partial = std::views::values | std::views::keys; + std::same_as 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 { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include +#include +#include +#include + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + int i; + std::tuple* begin() const; + std::tuple* end() const; +}; + +struct MoveOnlyView : View { + MoveOnly mo; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + // const & + { + const std::ranges::elements_view ev{View{{}, 5}}; + std::same_as decltype(auto) v = ev.base(); + assert(v.i == 5); + } + + // & + { + std::ranges::elements_view ev{View{{}, 5}}; + std::same_as decltype(auto) v = ev.base(); + assert(v.i == 5); + } + + // && + { + std::ranges::elements_view ev{View{{}, 5}}; + std::same_as decltype(auto) v = std::move(ev).base(); + assert(v.i == 5); + } + + // const && + { + const std::ranges::elements_view ev{View{{}, 5}}; + std::same_as decltype(auto) v = std::move(ev).base(); + assert(v.i == 5); + } + + // move only + { + std::ranges::elements_view ev{MoveOnlyView{{}, 5}}; + std::same_as 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) +// constexpr auto begin() const requires range + +#include +#include +#include +#include +#include + +#include "types.h" + +template +concept HasConstBegin = requires(const T ct) { ct.begin(); }; + +template +concept HasBegin = requires(T t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = + HasConstBegin && + // because const begin and non-const begin returns different types (iterator, iterator) + requires(T t, const T ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +struct NoConstBeginView : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr std::tuple* begin() { return buffer_; } + constexpr std::tuple* end() { return buffer_ + size_; } +}; + +// simple-view +static_assert(HasOnlyConstBegin>); + +// !simple-view && range +static_assert(HasConstAndNonConstBegin>); + +// !range +static_assert(HasOnlyNonConstBegin>); + +constexpr bool test() { + std::tuple 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); + 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 +// inline constexpr bool enable_borrowed_range> = +// enable_borrowed_range; + +#include +#include + +struct NonBorrowed : std::ranges::view_base { + std::tuple* begin(); + std::tuple* end(); +}; + +struct Borrowed : std::ranges::view_base { + std::tuple* begin(); + std::tuple* end(); +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(!std::ranges::borrowed_range>); +static_assert(std::ranges::borrowed_range>); 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 = default; + +#include +#include +#include +#include + +template +struct View : std::ranges::view_base { + int i = 42; + constexpr explicit View() + requires DefaultInitializable + = default; + std::tuple* begin() const; + std::tuple* end() const; +}; + + +// clang-format off +static_assert( std::is_default_constructible_v, 0>>); +static_assert(!std::is_default_constructible_v, 0>>); +// clang-format on + +constexpr bool test() { + { + std::ranges::elements_view, 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 +#include +#include +#include +#include + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + MoveOnly mo; + std::tuple* begin() const; + std::tuple* end() const; +}; + +// Test Explicit +static_assert(std::is_constructible_v, View>); +static_assert(!std::is_convertible_v>); + +constexpr bool test() { + { + std::ranges::elements_view 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 && !common_range) +// constexpr auto end() requires (!simple-view && common_range) +// constexpr auto end() const requires range +// constexpr auto end() const requires common_range + +#include +#include +#include +#include +#include + +#include "types.h" + +// | simple | common | v.end() | as_const(v) +// | | | | .end() +// |--------|--------|------------------|--------------- +// | Y | Y | iterator | iterator +// | Y | N | sentinel | sentinel +// | N | Y | iterator | iterator +// | N | N | sentinel | sentinel + +// !range +template +concept HasEnd = requires(T t) { t.end(); }; + +template +concept HasConstEnd = requires(const T ct) { ct.end(); }; + +struct NoConstEndView : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr std::tuple* begin() { return buffer_; } + constexpr std::tuple* end() { return buffer_ + size_; } +}; + +static_assert(HasEnd>); +static_assert(!HasConstEnd>); + +constexpr bool test() { + std::tuple 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 + static_assert(std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + } + + // 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 + static_assert(std::same_as); + static_assert(!std::same_as); + static_assert(!std::same_as); + } + + // !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 and iterator + static_assert(!std::same_as); + static_assert(std::same_as); + static_assert(std::same_as); + } + + // !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 and sentinel + static_assert(!std::same_as); + static_assert(!std::same_as); + static_assert(!std::same_as); + } + + // LWG 3406 elements_view::begin() and elements_view::end() have incompatible constraints + { + std::tuple 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 +#include +#include +#include +#include +#include +#include +#include + +#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 + { + 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 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 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 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; + using Sent = sentinel_wrapper; + using SubRange = std::ranges::subrange; + 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>); + 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>); + 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; +// +// constexpr iterator& operator-=(difference_type n) +// requires random_access_range; +// +// friend constexpr iterator operator+(const iterator& x, difference_type y) +// requires random_access_range; +// +// friend constexpr iterator operator+(difference_type x, const iterator& y) +// requires random_access_range; +// +// friend constexpr iterator operator-(const iterator& x, difference_type y) +// requires random_access_range; +// +// friend constexpr difference_type operator-(const iterator& x, const iterator& y) +// requires sized_sentinel_for, iterator_t>; + +#include + +#include + +#include "test_iterators.h" + +template +concept CanPlus = requires(T t, U u) { t + u; }; + +template +concept CanPlusEqual = requires(T t, U u) { t += u; }; + +template +concept CanMinus = requires(T t, U u) { t - u; }; + +template +concept CanMinusEqual = requires(T t, U u) { t -= u; }; + +template +using ElemIter = std::ranges::iterator_t>; + +using RandomAccessRange = std::ranges::subrange*>; +static_assert(std::ranges::random_access_range); +static_assert(std::sized_sentinel_for, // + std::ranges::iterator_t>); + +static_assert(CanPlus, int>); +static_assert(CanPlus>); +static_assert(CanPlusEqual, int>); +static_assert(CanMinus, int>); +static_assert(CanMinus, ElemIter>); +static_assert(CanMinusEqual, int>); + +using BidiRange = std::ranges::subrange*>>; +static_assert(!std::ranges::random_access_range); +static_assert(!std::sized_sentinel_for, // + std::ranges::iterator_t>); + +static_assert(!CanPlus, int>); +static_assert(!CanPlus>); +static_assert(!CanPlusEqual, int>); +static_assert(!CanMinus, int>); +static_assert(!CanMinus, ElemIter>); +static_assert(!CanMinusEqual, int>); + +constexpr bool test() { + std::tuple 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() const & noexcept; +// constexpr iterator_t base() &&; + +#include +#include +#include +#include +#include + +#include "MoveOnly.h" +#include "../types.h" + +// Test Noexcept +template +concept IsBaseNoexcept = + requires { + { std::declval().base() } noexcept; + }; + +using BaseView = std::ranges::subrange*>; +using ElementsIter = std::ranges::iterator_t>; + +static_assert(IsBaseNoexcept); +static_assert(IsBaseNoexcept); +static_assert(IsBaseNoexcept); +static_assert(!IsBaseNoexcept); + +constexpr bool test() { + std::tuple t{5}; + + // const & + { + const ElementsIter it{&t}; + decltype(auto) base = it.base(); + static_assert(std::is_same_v* const&>); + assert(base == &t); + } + + // & + { + ElementsIter it{&t}; + decltype(auto) base = it.base(); + static_assert(std::is_same_v* const&>); + assert(base == &t); + } + + // && + { + ElementsIter it{&t}; + decltype(auto) base = std::move(it).base(); + static_assert(std::is_same_v*>); + assert(base == &t); + } + + // const && + { + const ElementsIter it{&t}; + decltype(auto) base = std::move(it).base(); + static_assert(std::is_same_v* const&>); + assert(base == &t); + } + + // move only + { + struct MoveOnlyIter : IterBase { + MoveOnly mo; + }; + struct Sent { + constexpr bool operator==(const MoveOnlyIter&) const { return true; } + }; + + using MoveOnlyElemIter = + std::ranges::iterator_t, 0>>; + + MoveOnlyElemIter it{MoveOnlyIter{{}, MoveOnly{5}}}; + decltype(auto) base = std::move(it).base(); + static_assert(std::is_same_v); + 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>; +// friend constexpr bool operator<(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires random_access_range && three_way_comparable>; + +#include +#include +#include +#include + +#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, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); + static_assert(!std::is_invocable_v, Iter1, Iter2>); +} + +constexpr bool test() { + std::tuple ts[] = {{1}, {2}, {3}}; + + { + // Test a new-school iterator with operator<=>; the iterator should also have operator<=>. + using It = three_way_contiguous_iterator*>; + using Subrange = std::ranges::subrange; + static_assert(std::three_way_comparable); + using R = std::ranges::elements_view; + static_assert(std::three_way_comparable>); + + 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*>; + using Subrange = std::ranges::subrange; + static_assert(!std::three_way_comparable); + using R = std::ranges::elements_view; + static_assert(!std::three_way_comparable>); + + 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*>; + using Subrange = std::ranges::subrange; + static_assert(!std::ranges::random_access_range); + using R = std::ranges::elements_view; + static_assert(!std::three_way_comparable>); + + 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*>; + using Sent = sentinel_wrapper; + using Subrange = std::ranges::subrange; + using R = std::ranges::elements_view; + static_assert(!std::three_way_comparable>); + + 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, ElemIter, ElemIter>); + static_assert(!std::invocable, 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 current); + +#include +#include +#include + +#include "../types.h" + +// Test explicit +using BaseIter = std::tuple*; +using ElementsIter = std::ranges::iterator_t, 0>>; + +static_assert(std::is_constructible_v); +static_assert(!std::is_convertible_v); + +struct TracedMoveIter : IterBase{ + 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>; + 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> = default; + +#include +#include + +#include "../types.h" + +struct PODIter : IterBase { + 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*> begin() const; + sentinel_wrapper*>> end() const; +}; + +template +using ElementsIter = std::ranges::iterator_t>; + +static_assert(!std::default_initializable>); +static_assert(std::default_initializable>); + +constexpr bool test() { + using Iter = ElementsIter; + { + 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 i) +// requires Const && convertible_to, iterator_t>; + +#include +#include +#include + +#include "../types.h" + +template +struct ConvertibleIter : IterBase> { + using iterator_category = std::random_access_iterator_tag; + using value_type = std::tuple; + using difference_type = intptr_t; + + bool movedFromOtherConst = false; + int i = 0; + + constexpr ConvertibleIter() = default; + constexpr ConvertibleIter(int ii) : i(ii) {} + template + requires(Const != otherConst) + constexpr ConvertibleIter(ConvertibleIter it) : movedFromOtherConst(true), i(it.i) {} +}; + +template +struct BasicView : std::ranges::view_base { + Iter begin(); + Iter end(); + + ConstIter begin() const; + ConstIter end() const; +}; + +template +using ElemIter = std::ranges::iterator_t>; + +template +using ConstElemIter = std::ranges::iterator_t>; + +using ConvertibleView = BasicView, ConvertibleIter>; +using NonConvertibleView = BasicView*>, bidirectional_iterator*>>; + +static_assert(std::is_constructible_v, ElemIter>); +static_assert(!std::is_constructible_v, ConstElemIter>); +static_assert(!std::is_constructible_v, ElemIter>); +static_assert(!std::is_constructible_v, ConstElemIter>); + +constexpr bool test() { + ElemIter iter{ConvertibleIter{5}}; + ConstElemIter 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; +// constexpr iterator operator--(int) requires bidirectional_range; + +#include +#include +#include +#include + +#include "test_iterators.h" + +template +concept CanPreDecrement = requires(Iter it) { --it; }; + +template +concept CanPostDecrement = requires(Iter it) { it--; }; + +template > +constexpr void testOne() { + using Range = std::ranges::subrange; + std::tuple 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; + + if constexpr (!std::bidirectional_iterator) { + auto it = ev.begin(); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } else { + // --i + { + auto it = ev.begin(); + static_assert(CanPreDecrement); + + ++it; + assert(base(it.base()) == &ts[1]); + + decltype(auto) result = --it; + + static_assert(std::is_same_v); + assert(&result == &it); + + assert(base(it.base()) == &ts[0]); + } + + // i-- + { + auto it = ev.begin(); + static_assert(CanPostDecrement); + + ++it; + assert(base(it.base()) == &ts[1]); + + decltype(auto) result = it--; + + static_assert(std::is_same_v); + + assert(base(it.base()) == &ts[0]); + assert(base(result.base()) == &ts[1]); + } + } +} + +constexpr bool test() { + using Ptr = std::tuple*; + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne(); + + 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 +#include +#include +#include +#include + +template +constexpr void testReference(T (&ts)[Size]) { + auto ev = ts | std::views::elements; + auto it = ev.begin(); + + decltype(auto) result = *it; + using ExpectedType = decltype(std::get(ts[0])); + static_assert(std::is_same_v); + + if constexpr (std::is_reference_v) { + // tuple/array/pair + assert(&result == &std::get(ts[0])); + } else { + // subrange + assert(result == std::get(ts[0])); + } +} + +// LWG 3502 elements_view should not be allowed to return dangling references +template +constexpr void testValue(T t) { + auto ev = std::views::iota(0, 1) | std::views::transform([&t](int) { return t; }) | std::views::elements; + auto it = ev.begin(); + + decltype(auto) result = *it; + using ExpectedType = std::remove_cvref_t(t))>; + static_assert(std::is_same_v); + + assert(result == std::get(t)); +} + +constexpr bool test() { + // test tuple + { + std::tuple 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 ps[] = {{1, 2}, {4, 5}}; + testReference<0>(ps); + testReference<1>(ps); + testValue<0>(ps[0]); + testValue<1>(ps[0]); + } + + // test array + { + std::array 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 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; + +#include +#include +#include +#include + +#include "test_iterators.h" + +template > +constexpr void testOne() { + using Range = std::ranges::subrange; + std::tuple 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; + + // ++i + { + auto it = ev.begin(); + decltype(auto) result = ++it; + + static_assert(std::is_same_v); + assert(&result == &it); + + assert(base(it.base()) == &ts[1]); + } + + // i++ + { + if constexpr (std::forward_iterator) { + auto it = ev.begin(); + decltype(auto) result = it++; + + static_assert(std::is_same_v); + + assert(base(it.base()) == &ts[1]); + assert(base(result.base()) == &ts[0]); + } else { + auto it = ev.begin(); + it++; + + static_assert(std::is_same_v); + assert(base(it.base()) == &ts[1]); + } + } +} + +constexpr bool test() { + using Ptr = std::tuple*; + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne>(); + testOne(); + + 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::iterator. + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +template +using Range = std::ranges::subrange>; + +template +using ElementsIter = std::ranges::iterator_t>; + +// using iterator_concept = see below; +static_assert(std::same_as*>>>::iterator_concept, + std::input_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_concept, // + std::forward_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_concept, + std::bidirectional_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_concept, + std::random_access_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_concept, + std::random_access_iterator_tag>); + +static_assert(std::same_as*>>::iterator_concept, // + std::random_access_iterator_tag>); + +// using iterator_category = see below; // not always present +template +concept HasIterCategory = requires { typename T::iterator_category; }; + +static_assert(!HasIterCategory*>>>>); +static_assert(HasIterCategory*>>>>); + +static_assert(std::same_as*>>>::iterator_category, + std::forward_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_category, + std::bidirectional_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_category, + std::random_access_iterator_tag>); + +static_assert(std::same_as*>>>::iterator_category, + std::random_access_iterator_tag>); + +static_assert(std::same_as*>>::iterator_category, // + std::random_access_iterator_tag>); + +using Generator = decltype(std::views::iota(0, 1) | std::views::transform([](int) { + return std::pair{1, 1}; + })); +static_assert(std::ranges::random_access_range); + +static_assert(std::same_as::iterator_category, // + std::input_iterator_tag>); + +// using value_type = remove_cvref_t>>; +static_assert(std::same_as*>, 0>::value_type, int>); + +static_assert(std::same_as*>, 1>::value_type, long>); + +static_assert(std::same_as::value_type, int>); + +static_assert(std::same_as::value_type, short>); + +// using difference_type = range_difference_t; +static_assert(std::same_as*>>::difference_type, + std::ranges::range_difference_t*>>>); + +static_assert(std::same_as::difference_type, // + std::ranges::range_difference_t>); 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 + +#include +#include +#include + +#include "test_iterators.h" + +template +concept CanSubscript = requires(T t, U u) { t[u]; }; + +template +using ElemIter = std::ranges::iterator_t>; + +using RandomAccessRange = std::ranges::subrange*>; +static_assert(std::ranges::random_access_range); + +static_assert(CanSubscript, int>); + +using BidiRange = std::ranges::subrange*>>; +static_assert(!std::ranges::random_access_range); + +static_assert(!CanSubscript, int>); + +constexpr bool test() { + { + // reference + std::tuple 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); + } + + { + // value + auto ev = std::views::iota(0, 5) | std::views::transform([](int i) { return std::tuple{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); + } + + 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 +// concept has-tuple-element = +// tuple-like && N < tuple_size_v; +// +// template +// concept returnable-element = +// is_reference_v || move_constructible>; +// +// template +// requires view && has-tuple-element, N> && +// has-tuple-element>, N> && +// returnable-element, N> +// class elements_view; + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +template +using Range = std::ranges::subrange>; + +template +concept HasElementsView = requires { typename std::ranges::elements_view; }; + +static_assert(HasElementsView*>, 0>); +static_assert(HasElementsView*>, 1>); +static_assert(HasElementsView*>, 2>); +static_assert(HasElementsView*>, 3>); + +// !view +static_assert(!std::ranges::view, 1>>); +static_assert(!HasElementsView, 1>, 0>); + +// !input_range +static_assert(!std::ranges::input_range*>>>); +static_assert(!HasElementsView*>>, 0>); + +// !tuple-like +LIBCPP_STATIC_ASSERT(!std::__tuple_like); +static_assert(!HasElementsView, 1>); + +// !(N < tuple_size_v) +static_assert(!(2 < std::tuple_size_v< std::pair>)); +static_assert(!HasElementsView*>, 2>); + +// ! (is_reference_v || move_constructible>) +struct NonMovable { + NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; +static_assert(!std::move_constructible); + +using NonMovableGenerator = + decltype(std::views::iota(0, 1) | std::views::transform([](int) { + return std::pair{1, 1}; + })); + +static_assert(!HasElementsView); +static_assert(HasElementsView); 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() const; + +#include +#include +#include +#include +#include + +struct Sent { + int i; + + friend constexpr bool operator==(std::tuple*, const Sent&) { return true; } +}; + +constexpr bool test() { + using BaseRange = std::ranges::subrange*, Sent>; + using EleRange = std::ranges::elements_view; + using EleSent = std::ranges::sentinel_t; + + const EleSent st{Sent{5}}; + std::same_as 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 end); + +#include +#include +#include +#include + +struct Sent { + int i; + + friend constexpr bool operator==(std::tuple*, const Sent&) { return true; } +}; + +struct Range : std::ranges::view_base { + std::tuple* begin() const; + Sent end(); +}; + +// Test explicit + +static_assert(std::is_constructible_v>, Sent>); +static_assert(!std::is_convertible_v>>); + +constexpr bool test() { + // base is init correctly + { + using R = std::ranges::elements_view; + using Sentinel = std::ranges::sentinel_t; + + 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 s) +// requires Const && convertible_to, sentinel_t>; + +#include +#include +#include + +#include "../types.h" + +struct Sent { + int i; + constexpr Sent() = default; + constexpr Sent(int ii) : i(ii) {} + friend constexpr bool operator==(std::tuple*, 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*, const ConstSent&) { return true; } +}; + +struct Range : std::ranges::view_base { + std::tuple* 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*, const NonConvertConstSent&) { return true; } +}; + +struct NonConvertConstSentRange : std::ranges::view_base { + std::tuple* begin() const; + Sent end(); + NonConvertConstSent end() const; +}; + +// Test Constraint +static_assert(std::is_constructible_v>, + std::ranges::sentinel_t>>); + +// !Const +static_assert(!std::is_constructible_v>, + std::ranges::sentinel_t>>); + +// !convertible_to, sentinel_t> +static_assert(!std::is_constructible_v< + std::ranges::sentinel_t>, + std::ranges::sentinel_t>>); + +constexpr bool test() { + // base is init correctly + { + using R = std::ranges::elements_view; + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::same_as); + + 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 +#include +#include + +struct PODSentinel { + int i; // deliberately uninitialised + + friend constexpr bool operator==(std::tuple*, const PODSentinel&) { return true; } +}; + +struct Range : std::ranges::view_base { + std::tuple* begin() const; + PODSentinel end(); +}; + +constexpr bool test() { + using EleView = std::ranges::elements_view; + using Sentinel = std::ranges::sentinel_t; + static_assert(!std::is_same_v>); + + { + 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 +// requires sentinel_for, iterator_t>> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include +#include +#include + +#include "../types.h" + +template +struct Iter { + std::tuple* it_; + + using value_type = std::tuple; + 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 +struct Sent { + std::tuple* end_; + + constexpr bool operator==(const Iter& i) const { return i.it_ == end_; } +}; + +template +struct CrossComparableSent { + std::tuple* end_; + + template + constexpr bool operator==(const Iter& i) const { + return i.it_ == end_; + } +}; + +template