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, Todo: elements_view","Cologne","|In Progress|","" +"`P1035R7 `__","LWG","Input Range Adaptors","Cologne","|Complete|","16.0" "`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" @@ -183,7 +183,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" -"`P1994R1 `__","LWG","elements_view needs its own sentinel","Prague","* *","" +"`P1994R1 `__","LWG","elements_view needs its own sentinel","Prague","Complete","16.0" "`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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -344,6 +344,7 @@ __fwd/pair.h __fwd/span.h __fwd/string_view.h + __fwd/subrange.h __fwd/tuple.h __hash_table __ios/fpos.h @@ -353,6 +354,11 @@ __iterator/bounded_iter.h __iterator/common_iterator.h __iterator/concepts.h + __iterator/concepts/can_reference.h + __iterator/concepts/input_or_output_iterator.h + __iterator/concepts/sentinel_for.h + __iterator/concepts/sized_sentinel_for.h + __iterator/concepts/weakly_incrementable.h __iterator/counted_iterator.h __iterator/data.h __iterator/default_sentinel.h @@ -482,6 +488,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 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/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,40 @@ +//===---------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// + +#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/input_or_output_iterator.h> +# include <__iterator/concepts/sentinel_for.h> +# include <__iterator/concepts/sized_sentinel_for.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/__iterator/concepts.h b/libcxx/include/__iterator/concepts.h --- a/libcxx/include/__iterator/concepts.h +++ b/libcxx/include/__iterator/concepts.h @@ -11,6 +11,10 @@ #define _LIBCPP___ITERATOR_CONCEPTS_H #include <__config> +#include <__iterator/concepts/input_or_output_iterator.h> +#include <__iterator/concepts/sentinel_for.h> +#include <__iterator/concepts/sized_sentinel_for.h> +#include <__iterator/concepts/weakly_incrementable.h> #include <__iterator/incrementable_traits.h> #include <__iterator/iter_move.h> #include <__iterator/iterator_traits.h> @@ -58,24 +62,6 @@ const_cast&&>(*_VSTD::forward<_Out>(__o)) = _VSTD::forward<_Tp>(__t); // not required to be equality-preserving }; -// [iterator.concept.winc] -template -concept __integer_like = integral<_Tp> && !same_as<_Tp, bool>; - -template -concept __signed_integer_like = signed_integral<_Tp>; - -template -concept weakly_incrementable = - // TODO: remove this once the clang bug is fixed (bugs.llvm.org/PR48173). - !same_as<_Ip, bool> && // Currently, clang does not handle bool correctly. - movable<_Ip> && - requires(_Ip __i) { - typename iter_difference_t<_Ip>; - requires __signed_integer_like>; - { ++__i } -> same_as<_Ip&>; // not required to be equality-preserving - __i++; // not required to be equality-preserving - }; // [iterator.concept.inc] template @@ -86,33 +72,6 @@ { __i++ } -> same_as<_Ip>; }; -// [iterator.concept.iterator] -template -concept input_or_output_iterator = - requires(_Ip __i) { - { *__i } -> __can_reference; - } && - weakly_incrementable<_Ip>; - -// [iterator.concept.sentinel] -template -concept sentinel_for = - semiregular<_Sp> && - input_or_output_iterator<_Ip> && - __weakly_equality_comparable_with<_Sp, _Ip>; - -template -inline constexpr bool disable_sized_sentinel_for = false; - -template -concept sized_sentinel_for = - sentinel_for<_Sp, _Ip> && - !disable_sized_sentinel_for, remove_cv_t<_Ip>> && - requires(const _Ip& __i, const _Sp& __s) { - { __s - __i } -> same_as>; - { __i - __s } -> same_as>; - }; - // [iterator.concept.input] template concept input_iterator = diff --git a/libcxx/include/__iterator/concepts/can_reference.h b/libcxx/include/__iterator/concepts/can_reference.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__iterator/concepts/can_reference.h @@ -0,0 +1,36 @@ +// -*- 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___ITERATOR_CONCEPTS_CAN_REFERENCE_H +#define _LIBCPP___ITERATOR_CONCEPTS_CAN_REFERENCE_H + +#include <__config> + + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +using __with_reference = _Tp&; + +template +concept __can_reference = requires { + typename __with_reference<_Tp>; +}; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ITERATOR_CONCEPTS_CAN_REFERENCE_H diff --git a/libcxx/include/__iterator/concepts/input_or_output_iterator.h b/libcxx/include/__iterator/concepts/input_or_output_iterator.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__iterator/concepts/input_or_output_iterator.h @@ -0,0 +1,39 @@ +// -*- 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___ITERATOR_CONCEPTS_INPUT_OR_OUTPUT_ITERATOR_H +#define _LIBCPP___ITERATOR_CONCEPTS_INPUT_OR_OUTPUT_ITERATOR_H + +#include <__config> +#include <__iterator/concepts/can_reference.h> +#include <__iterator/concepts/weakly_incrementable.h> + + +// [iterator.concept.iterator] + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +concept input_or_output_iterator = + requires(_Ip __i) { + { *__i } -> __can_reference; + } && + weakly_incrementable<_Ip>; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ITERATOR_CONCEPTS_INPUT_OR_OUTPUT_ITERATOR_H diff --git a/libcxx/include/__iterator/concepts/sentinel_for.h b/libcxx/include/__iterator/concepts/sentinel_for.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__iterator/concepts/sentinel_for.h @@ -0,0 +1,38 @@ +// -*- 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___ITERATOR_CONCEPTS_SENTINEL_FOR_H +#define _LIBCPP___ITERATOR_CONCEPTS_SENTINEL_FOR_H + +#include <__concepts/equality_comparable.h> +#include <__concepts/semiregular.h> +#include <__config> +#include <__iterator/concepts/input_or_output_iterator.h> + +// [iterator.concept.sentinel] + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +concept sentinel_for = + semiregular<_Sp> && + input_or_output_iterator<_Ip> && + __weakly_equality_comparable_with<_Sp, _Ip>; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ITERATOR_CONCEPTS_SENTINEL_FOR_H diff --git a/libcxx/include/__iterator/concepts/sized_sentinel_for.h b/libcxx/include/__iterator/concepts/sized_sentinel_for.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__iterator/concepts/sized_sentinel_for.h @@ -0,0 +1,45 @@ +// -*- 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___ITERATOR_CONCEPTS_SIZED_SENTINEL_FOR_H +#define _LIBCPP___ITERATOR_CONCEPTS_SIZED_SENTINEL_FOR_H + +#include <__concepts/same_as.h> +#include <__config> +#include <__iterator/concepts/sentinel_for.h> +#include <__iterator/incrementable_traits.h> +#include <__type_traits/remove_cv.h> + +// [iterator.concept.sizedsentinel] + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +inline constexpr bool disable_sized_sentinel_for = false; + +template +concept sized_sentinel_for = + sentinel_for<_Sp, _Ip> && + !disable_sized_sentinel_for, remove_cv_t<_Ip>> && + requires(const _Ip& __i, const _Sp& __s) { + { __s - __i } -> same_as>; + { __i - __s } -> same_as>; + }; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ITERATOR_CONCEPTS_SIZED_SENTINEL_FOR_H diff --git a/libcxx/include/__iterator/concepts/weakly_incrementable.h b/libcxx/include/__iterator/concepts/weakly_incrementable.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__iterator/concepts/weakly_incrementable.h @@ -0,0 +1,52 @@ +// -*- 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___ITERATOR_CONCEPTS_WEAKLY_INCREMENTABLE_H +#define _LIBCPP___ITERATOR_CONCEPTS_WEAKLY_INCREMENTABLE_H + +#include <__concepts/arithmetic.h> +#include <__concepts/movable.h> +#include <__concepts/same_as.h> +#include <__config> +#include <__iterator/incrementable_traits.h> + + +// [iterator.concept.winc] + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +template +concept __integer_like = integral<_Tp> && !same_as<_Tp, bool>; + +template +concept __signed_integer_like = signed_integral<_Tp>; + +template +concept weakly_incrementable = + // TODO: remove this once the clang bug is fixed (bugs.llvm.org/PR48173). + !same_as<_Ip, bool> && // Currently, clang does not handle bool correctly. + movable<_Ip> && + requires(_Ip __i) { + typename iter_difference_t<_Ip>; + requires __signed_integer_like>; + { ++__i } -> same_as<_Ip&>; // not required to be equality-preserving + __i++; // not required to be equality-preserving + }; + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ITERATOR_CONCEPTS_WEAKLY_INCREMENTABLE_H diff --git a/libcxx/include/__iterator/incrementable_traits.h b/libcxx/include/__iterator/incrementable_traits.h --- a/libcxx/include/__iterator/incrementable_traits.h +++ b/libcxx/include/__iterator/incrementable_traits.h @@ -10,9 +10,9 @@ #ifndef _LIBCPP___ITERATOR_INCREMENTABLE_TRAITS_H #define _LIBCPP___ITERATOR_INCREMENTABLE_TRAITS_H +#include <__concepts/arithmetic.h> #include <__config> #include <__type_traits/is_primary_template.h> -#include #include #include diff --git a/libcxx/include/__iterator/iter_move.h b/libcxx/include/__iterator/iter_move.h --- a/libcxx/include/__iterator/iter_move.h +++ b/libcxx/include/__iterator/iter_move.h @@ -12,6 +12,7 @@ #include <__concepts/class_or_enum.h> #include <__config> +#include <__iterator/concepts/can_reference.h> #include <__iterator/iterator_traits.h> #include <__utility/forward.h> #include <__utility/move.h> diff --git a/libcxx/include/__iterator/iterator_traits.h b/libcxx/include/__iterator/iterator_traits.h --- a/libcxx/include/__iterator/iterator_traits.h +++ b/libcxx/include/__iterator/iterator_traits.h @@ -12,6 +12,7 @@ #include <__config> #include <__fwd/pair.h> +#include <__iterator/concepts/can_reference.h> #include <__iterator/incrementable_traits.h> #include <__iterator/readable_traits.h> #include @@ -26,13 +27,6 @@ #if _LIBCPP_STD_VER > 17 -template -using __with_reference = _Tp&; - -template -concept __can_reference = requires { - typename __with_reference<_Tp>; -}; template concept __dereferenceable = requires(_Tp& __t) { 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,410 @@ +// -*- 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/tuple_element.h> +#include <__tuple/tuple_like.h> +#include <__tuple/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 +concept __has_tuple_element = __tuple_like<_Tp>::value && _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 + class __iterator; + + template + class __sentinel; + + _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 + requires view<_View> && __has_tuple_element, _Np> && + __has_tuple_element>, _Np> && + __returnable_element, _Np> +template +class elements_view<_View, _Np>::__iterator + : public __elements_view_iterator_category_base<__maybe_const<_Const, _View>, _Np> { + template + friend class elements_view<_View, _Np>::__sentinel; + + using _Base = __maybe_const<_Const, _View>; + + iterator_t<_Base> __current_ = iterator_t<_Base>(); + + template + friend class elements_view<_View, _Np>::__iterator; + + _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 _Ele = remove_cv_t>>; + return static_cast<_Ele>(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 __iterator() + requires default_initializable> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current) : __current_(std::move(__current)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __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 __iterator& operator++() { + ++__current_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++__current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires forward_range<_Base> + { + auto temp = *this; + ++__current_; + return temp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + --__current_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires bidirectional_range<_Base> + { + auto temp = *this; + --__current_; + return temp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n) + requires random_access_range<_Base> + { + __current_ += __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __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 __iterator& __x, const __iterator& __y) + requires equality_comparable> + { + return __x.__current_ == __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__current_ < __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> && three_way_comparable> + { + return __x.__current_ <=> __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __x, difference_type __y) + requires random_access_range<_Base> + { + return __iterator{__x} += __y; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __y + __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __x, difference_type __y) + requires random_access_range<_Base> + { + return __iterator{__x} -= __y; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + return __x.__current_ - __y.__current_; + } +}; + +template + requires view<_View> && __has_tuple_element, _Np> && + __has_tuple_element>, _Np> && + __returnable_element, _Np> +template +class elements_view<_View, _Np>::__sentinel { +private: + using _Base = __maybe_const<_Const, _View>; + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + template + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) + __get_current(const elements_view<_View, _Np>::__iterator<_AnyConst>& __iter) { + return (__iter.__current_); + } + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __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 __iterator<_OtherConst>& __x, const __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 __iterator<_OtherConst>& __x, const __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 __sentinel& __x, const __iterator<_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 @@ -17,6 +17,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/size.h> #include <__ranges/view_interface.h> #include <__tuple/tuple_element.h> +#include <__tuple/tuple_like.h> #include <__tuple/tuple_size.h> #include <__utility/move.h> #include @@ -69,8 +71,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/make_tuple_types.h b/libcxx/include/__tuple/make_tuple_types.h --- a/libcxx/include/__tuple/make_tuple_types.h +++ b/libcxx/include/__tuple/make_tuple_types.h @@ -11,12 +11,14 @@ #include <__config> #include <__fwd/array.h> +#include <__fwd/subrange.h> #include <__fwd/tuple.h> #include <__tuple/apply_cv.h> #include <__tuple/tuple_element.h> #include <__tuple/tuple_indices.h> #include <__tuple/tuple_size.h> #include <__tuple/tuple_types.h> +#include <__type_traits/conditional.h> #include <__type_traits/remove_cv.h> #include <__type_traits/remove_reference.h> #include @@ -56,6 +58,16 @@ >; }; +# if _LIBCPP_STD_VER >= 20 +template +struct __make_tuple_types_flat, __tuple_indices<_Idx...>> { + template + using __element_type = std::conditional_t<_Index == 0, _Iter, _Sent>; + template > + using __apply_quals = __tuple_types< typename _ApplyFn::template __apply<__element_type<_Idx>>... >; +}; +# endif // _LIBCPP_STD_VER >= 20 + template >::value, size_t _Sp = 0, bool _SameSize = (_Ep == tuple_size<__libcpp_remove_reference_t<_Tp> >::value)> diff --git a/libcxx/include/__tuple/tuple_like.h b/libcxx/include/__tuple/tuple_like.h --- a/libcxx/include/__tuple/tuple_like.h +++ b/libcxx/include/__tuple/tuple_like.h @@ -12,9 +12,12 @@ #include <__config> #include <__fwd/array.h> #include <__fwd/pair.h> +#include <__fwd/subrange.h> #include <__fwd/tuple.h> #include <__tuple/tuple_types.h> -#include <__type_traits/integral_constant.h> +#include <__type_traits/enable_if.h> +#include <__type_traits/is_same.h> +#include <__type_traits/remove_cvref.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -23,11 +26,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD -template struct __tuple_like : false_type {}; +template struct __tuple_like : false_type {}; -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<_Tp, __enable_if_t >::value> > + : public __tuple_like<__remove_cvref_t<_Tp> > {}; #ifndef _LIBCPP_CXX03_LANG template struct __tuple_like > : true_type {}; @@ -39,6 +42,13 @@ template struct __tuple_like<__tuple_types<_Tp...> > : true_type {}; +#if _LIBCPP_STD_VER >= 20 + +template +struct __tuple_like > : true_type {}; + +#endif // _LIBCPP_STD_VER >= 20 + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___TUPLE_TUPLE_LIKE_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 @@ -874,7 +874,29 @@ module back_insert_iterator { private header "__iterator/back_insert_iterator.h" } module bounded_iter { private header "__iterator/bounded_iter.h" } module common_iterator { private header "__iterator/common_iterator.h" } - module concepts { private header "__iterator/concepts.h" } + module concepts { + private header "__iterator/concepts.h" + + module can_reference { + private header "__iterator/concepts/can_reference.h" + } + + module input_or_output_iterator { + private header "__iterator/concepts/input_or_output_iterator.h" + } + + module sentinel_for { + private header "__iterator/concepts/sentinel_for.h" + } + + module sized_sentinel_for { + private header "__iterator/concepts/sized_sentinel_for.h" + } + + module weakly_incrementable { + private header "__iterator/concepts/weakly_incrementable.h" + } + } module counted_iterator { private header "__iterator/counted_iterator.h" } module data { private header "__iterator/data.h" } module default_sentinel { private header "__iterator/default_sentinel.h" } @@ -1121,6 +1143,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" } @@ -1142,7 +1165,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 { 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/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 @@ -376,6 +376,7 @@ #include <__fwd/pair.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/pair.h'}} #include <__fwd/span.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/span.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'}} @@ -384,6 +385,11 @@ #include <__iterator/bounded_iter.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/bounded_iter.h'}} #include <__iterator/common_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/common_iterator.h'}} #include <__iterator/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts.h'}} +#include <__iterator/concepts/can_reference.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts/can_reference.h'}} +#include <__iterator/concepts/input_or_output_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts/input_or_output_iterator.h'}} +#include <__iterator/concepts/sentinel_for.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts/sentinel_for.h'}} +#include <__iterator/concepts/sized_sentinel_for.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts/sized_sentinel_for.h'}} +#include <__iterator/concepts/weakly_incrementable.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/concepts/weakly_incrementable.h'}} #include <__iterator/counted_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/counted_iterator.h'}} #include <__iterator/data.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/data.h'}} #include <__iterator/default_sentinel.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/default_sentinel.h'}} @@ -513,6 +519,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'}} 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,158 @@ +//===----------------------------------------------------------------------===// +// +// 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, 2}}, {{3}, {4, 4}}, {{5}, {6, 6}}}; + 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 = {2, 4, 6}; + 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,83 @@ +//===----------------------------------------------------------------------===// +// +// 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,46 @@ +//===----------------------------------------------------------------------===// +// +// 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,43 @@ +//===----------------------------------------------------------------------===// +// +// 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_constructible_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,133 @@ +//===----------------------------------------------------------------------===// +// +// 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 "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); + } + + 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 historical_figures = + 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 = historical_figures | std::views::elements<0>; + auto expectedNames = {"Babbage"sv, "Hamilton"sv, "Lovelace"sv, "Turing"sv}; + assert(std::ranges::equal(names, expectedNames)); + + auto birth_years = historical_figures | std::views::elements<1>; + assert(std::ranges::equal(birth_years, expectedYears)); + } + + // views::keys + { + auto names = historical_figures | 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(historical_figures | 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 EleIter = 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, EleIter>); +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, EleIter>); +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,97 @@ +//===----------------------------------------------------------------------===// +// +// 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 MoveOnlyEleIter = + std::ranges::iterator_t, 0>>; + + MoveOnlyEleIter 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,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 + +// 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 != 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 EleIter = decltype(it); + static_assert(!std::invocable, EleIter, EleIter>); + 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,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 EleIter = std::ranges::iterator_t>; + +template +using ConstEleIter = std::ranges::iterator_t>; + +using ConvertibleView = BasicView, ConvertibleIter>; +using NonConvertibleView = BasicView*>, bidirectional_iterator*>>; + +static_assert(std::is_constructible_v, EleIter>); +static_assert(!std::is_constructible_v, ConstEleIter>); +static_assert(!std::is_constructible_v, EleIter>); +static_assert(!std::is_constructible_v, ConstEleIter>); + +constexpr bool test() { + EleIter iter{ConvertibleIter{5}}; + ConstEleIter 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,90 @@ +//===----------------------------------------------------------------------===// +// +// 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) { + // --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]); + } + } else { + auto it = ev.begin(); + static_assert(!CanPreDecrement); + static_assert(!CanPostDecrement); + } +} + +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,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 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])); + } +} + +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,77 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// 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 EleIter = 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,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 + +// 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 "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(!HasElementsView, 1>, 0>); + +// !input_range +static_assert(!HasElementsView*>>, 0>); + +// !tuple-like +static_assert(!HasElementsView, 1>); + +// !(N > tuple_size_v) +static_assert(!HasElementsView*>, 2>); + +// ! (is_reference_v || move_constructible>) +struct NonMovable { + NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; + +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/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/size.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr auto size() requires sized_range +// constexpr auto size() const requires sized_range + +#include +#include +#include +#include + +#include "types.h" + +template +concept HasSize = requires(T t) { t.size(); }; + +static_assert(HasSize>); +static_assert(HasSize>); + +struct NonSized : std::ranges::view_base { + using iterator = forward_iterator*>; + iterator begin() const; + iterator end() const; +}; +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +static_assert(!HasSize>); +static_assert(!HasSize>); + +struct SizedNonConst : TupleBufferView { + using TupleBufferView::TupleBufferView; + + using iterator = forward_iterator*>; + constexpr auto begin() const { return iterator{buffer_}; } + constexpr auto end() const { return iterator{buffer_ + size_}; } + constexpr std::size_t size() { return size_; } +}; + +static_assert(HasSize>); +static_assert(!HasSize>); + +constexpr bool test() { + std::tuple buffer[] = {{1}, {2}, {3}}; + + // non-const and const are sized + { + auto ev = std::views::elements<0>(buffer); + assert(ev.size() == 3); + assert(std::as_const(ev).size() == 3); + } + + { + // const-view non-sized range + auto ev = std::views::elements<0>(SizedNonConst{buffer}); + assert(ev.size() == 3); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/types.h b/libcxx/test/std/ranges/range.adaptors/range.elements/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/types.h @@ -0,0 +1,475 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "test_range.h" + +template +struct BufferView : std::ranges::view_base { + T* buffer_; + std::size_t size_; + + template + constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} +}; + +using TupleBufferView = BufferView>; + +template +struct Common : TupleBufferView { + using TupleBufferView::TupleBufferView; + + constexpr std::tuple* begin() + requires(!Simple) + { + return buffer_; + } + constexpr const std::tuple* begin() const { return buffer_; } + constexpr std::tuple* end() + requires(!Simple) + { + return buffer_ + size_; + } + constexpr const std::tuple* end() const { return buffer_ + size_; } +}; +using SimpleCommon = Common; +using NonSimpleCommon = Common; + +using SimpleCommonRandomAccessSized = SimpleCommon; +using NonSimpleCommonRandomAccessSized = NonSimpleCommon; + +static_assert(std::ranges::common_range>); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); +#if 0 +template +struct CommonNonRandom : TupleBufferView { + using TupleBufferView::TupleBufferView; + using const_iterator = forward_iterator; + using iterator = forward_iterator; + constexpr iterator begin() + requires(!Simple) { + return iterator(buffer_); + } + constexpr const_iterator begin() const { return const_iterator(buffer_); } + constexpr iterator end() + requires(!Simple) { + return iterator(buffer_ + size_); + } + constexpr const_iterator end() const { return const_iterator(buffer_ + size_); } +}; + +using SimpleCommonNonRandom = CommonNonRandom; +using NonSimpleCommonNonRandom = CommonNonRandom; + +static_assert(std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); +#endif + +template +struct NonCommon : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr std::tuple* begin() + requires(!Simple) { + return buffer_; + } + constexpr const std::tuple* begin() const { return buffer_; } + constexpr sentinel_wrapper*> end() + requires(!Simple) { + return sentinel_wrapper*>(buffer_ + size_); + } + constexpr sentinel_wrapper*> end() const { return sentinel_wrapper*>(buffer_ + size_); } +}; + +using SimpleNonCommon = NonCommon; +using NonSimpleNonCommon = NonCommon; + +static_assert(!std::ranges::common_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +#if 0 +template +struct NonCommonSized : TupleBufferView { + using TupleBufferView::TupleBufferView; + constexpr int* begin() + requires(!Simple) { + return buffer_; + } + constexpr const int* begin() const { return buffer_; } + constexpr sentinel_wrapper end() + requires(!Simple) { + return sentinel_wrapper(buffer_ + size_); + } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(buffer_ + size_); } + constexpr std::size_t size() const { return size_; } +}; + +using SimpleNonCommonSized = NonCommonSized; +using SimpleNonCommonRandomAcessSized = SimpleNonCommonSized; +using NonSimpleNonCommonSized = NonCommonSized; +using NonSimpleNonCommonRandomAcessSized = NonSimpleNonCommonSized; + +static_assert(!std::ranges::common_range); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct NonCommonNonRandom : TupleBufferView { + using TupleBufferView::TupleBufferView; + + using const_iterator = forward_iterator; + using iterator = forward_iterator; + + constexpr iterator begin() + requires(!Simple) { + return iterator(buffer_); + } + constexpr const_iterator begin() const { return const_iterator(buffer_); } + constexpr sentinel_wrapper end() + requires(!Simple) { + return sentinel_wrapper(iterator(buffer_ + size_)); + } + constexpr sentinel_wrapper end() const { + return sentinel_wrapper(const_iterator(buffer_ + size_)); + } +}; + +using SimpleNonCommonNonRandom = NonCommonNonRandom; +using NonSimpleNonCommonNonRandom = NonCommonNonRandom; + +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +template +struct BasicView : TupleBufferView { + using TupleBufferView::TupleBufferView; + + constexpr NonConstIter begin() + requires(!std::is_same_v) { + return NonConstIter(buffer_); + } + constexpr Iter begin() const { return Iter(buffer_); } + + constexpr NonConstSent end() + requires(!std::is_same_v) { + if constexpr (std::is_same_v) { + return NonConstIter(buffer_ + size_); + } else { + return NonConstSent(NonConstIter(buffer_ + size_)); + } + } + + constexpr Sent end() const { + if constexpr (std::is_same_v) { + return Iter(buffer_ + size_); + } else { + return Sent(Iter(buffer_ + size_)); + } + } +}; + +template +struct forward_sized_iterator { + Base it_ = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + using pointer = Base; + using reference = decltype(*Base{}); + + forward_sized_iterator() = default; + constexpr forward_sized_iterator(Base it) : it_(it) {} + + constexpr reference operator*() const { return *it_; } + + constexpr forward_sized_iterator& operator++() { + ++it_; + return *this; + } + constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); } + + friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default; + + friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) { + return x.it_ - y.it_; + } +}; +static_assert(std::forward_iterator>); +static_assert(std::sized_sentinel_for, forward_sized_iterator<>>); + +using ForwardSizedView = BasicView>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleForwardSizedView = BasicView, forward_sized_iterator, + forward_sized_iterator, forward_sized_iterator>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using ForwardSizedNonCommon = BasicView, sized_sentinel>>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleForwardSizedNonCommon = + BasicView, sized_sentinel>, + forward_sized_iterator, sized_sentinel>>; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::sized_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::random_access_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +struct SizedRandomAccessView : TupleBufferView { + using TupleBufferView::TupleBufferView; + using iterator = random_access_iterator; + + constexpr auto begin() const { return iterator(buffer_); } + constexpr auto end() const { return sized_sentinel(iterator(buffer_ + size_)); } + + constexpr decltype(auto) operator[](std::size_t n) const { return *(begin() + n); } +}; +static_assert(std::ranges::view); +static_assert(std::ranges::random_access_range); +static_assert(std::ranges::sized_range); + +using NonSizedRandomAccessView = + BasicView, sentinel_wrapper>>; +static_assert(!std::ranges::contiguous_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleNonSizedRandomAccessView = + BasicView, sentinel_wrapper>, + random_access_iterator, sentinel_wrapper> >; +static_assert(!std::ranges::contiguous_range); +static_assert(std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::sized_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using ContiguousCommonView = BasicView; +static_assert(std::ranges::contiguous_range); +static_assert(std::ranges::common_range); +static_assert(std::ranges::sized_range); + +using ContiguousNonCommonView = BasicView>; +static_assert(std::ranges::contiguous_range); +static_assert(!std::ranges::common_range); +static_assert(!std::ranges::sized_range); + +using ContiguousNonCommonSized = BasicView>; + +static_assert(std::ranges::contiguous_range); +static_assert(!std::ranges::common_range); +static_assert(std::ranges::sized_range); + +template +struct common_input_iterator { + Base it_; + + using value_type = int; + using difference_type = std::intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr common_input_iterator() = default; + constexpr explicit common_input_iterator(Base it) : it_(it) {} + + constexpr common_input_iterator& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } + + constexpr int& operator*() const { return *it_; } + + friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default; +}; + +using InputCommonView = BasicView>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleInputCommonView = BasicView, common_input_iterator, + common_input_iterator, common_input_iterator>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using InputNonCommonView = BasicView, sentinel_wrapper>>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleInputNonCommonView = + BasicView, sentinel_wrapper>, + common_input_iterator, sentinel_wrapper>>; +static_assert(std::ranges::input_range); +static_assert(!std::ranges::forward_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using BidiCommonView = BasicView>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleBidiCommonView = BasicView, bidirectional_iterator, + bidirectional_iterator, bidirectional_iterator>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +struct SizedBidiCommon : BidiCommonView { + using BidiCommonView::BidiCommonView; + std::size_t size() const { return base(end()) - base(begin()); } +}; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +struct NonSimpleSizedBidiCommon : NonSimpleBidiCommonView { + using NonSimpleBidiCommonView::NonSimpleBidiCommonView; + std::size_t size() const { return base(end()) - base(begin()); } +}; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using BidiNonCommonView = BasicView, sentinel_wrapper>>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleBidiNonCommonView = + BasicView, sentinel_wrapper>, + bidirectional_iterator, sentinel_wrapper>>; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +using SizedBidiNonCommonView = BasicView, sized_sentinel>>; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + +using NonSimpleSizedBidiNonCommonView = + BasicView, sized_sentinel>, + bidirectional_iterator, sized_sentinel>>; +static_assert(std::ranges::sized_range); +static_assert(std::ranges::bidirectional_range); +static_assert(!std::ranges::random_access_range); +static_assert(!std::ranges::common_range); +LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + +namespace adltest{ +struct iter_move_swap_iterator { + + std::reference_wrapper iter_move_called_times; + std::reference_wrapper iter_swap_called_times; + int i = 0; + + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = intptr_t; + + constexpr int operator*() const { return i; } + + constexpr iter_move_swap_iterator& operator++() { + ++i; + return *this; + } + constexpr void operator++(int) { ++i; } + + friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; } + + friend constexpr int iter_move(iter_move_swap_iterator const& it) { + ++it.iter_move_called_times; + return it.i; + } + friend constexpr void iter_swap(iter_move_swap_iterator const& x, iter_move_swap_iterator const& y) { + ++x.iter_swap_called_times; + ++y.iter_swap_called_times; + } +}; + +struct IterMoveSwapRange { + int iter_move_called_times = 0; + int iter_swap_called_times = 0; + constexpr auto begin() { return iter_move_swap_iterator{iter_move_called_times, iter_swap_called_times}; } + constexpr auto end() const { return std::default_sentinel; } +}; +} // namespace adltest + +#endif + +template +struct IterBase { + using iterator_concept = std::random_access_iterator_tag; + using value_type = std::tuple; + using difference_type = intptr_t; + + constexpr std::tuple operator*() const { return std::tuple(5); } + + constexpr Derived& operator++() { return *this; } + constexpr void operator++(int) {} + + friend constexpr bool operator==(const IterBase&, const IterBase&) = default; +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ELEMENTS_TYPES_H