diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -348,6 +348,8 @@ --------------------------------------------------- ----------------- ``__cpp_lib_ranges_chunk_by`` *unimplemented* --------------------------------------------------- ----------------- + ``__cpp_lib_ranges_enumerate`` ``202302L`` + --------------------------------------------------- ----------------- ``__cpp_lib_ranges_iota`` *unimplemented* --------------------------------------------------- ----------------- ``__cpp_lib_ranges_join_with`` *unimplemented* diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -105,7 +105,7 @@ "","","","","","","" "`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|" "`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","","","|ranges|" -"`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","","","|ranges|" +"`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","|Complete|","18.0","|ranges|" "`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|Partial| [#note-P2711R1]_","","|ranges|" "`P2609R3 <https://wg21.link/P2609R3>`__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|" "`P2713R1 <https://wg21.link/P2713R1>`__","LWG", "Escaping improvements in ``std::format``","February 2023","","","|format|" diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -8,8 +8,8 @@ "`3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","Varna June 2023","|Complete|","7.0","" "`3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","Varna June 2023","","","|ranges|" "`3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","Varna June 2023","|Complete|","3.4","" -"`3912 <https://wg21.link/LWG3912>`__","``enumerate_view::iterator::operator-`` should be ``noexcept``","Varna June 2023","","","|ranges|" -"`3914 <https://wg21.link/LWG3914>`__","Inconsistent template-head of ``ranges::enumerate_view``","Varna June 2023","","","|ranges|" +"`3912 <https://wg21.link/LWG3912>`__","``enumerate_view::iterator::operator-`` should be ``noexcept``","Varna June 2023","|Complete|","18.0","|ranges|" +"`3914 <https://wg21.link/LWG3914>`__","Inconsistent template-head of ``ranges::enumerate_view``","Varna June 2023","|Nothing To Do|","","|ranges|" "`3915 <https://wg21.link/LWG3915>`__","Redundant paragraph about expression variations","Varna June 2023","","","|ranges|" "`3925 <https://wg21.link/LWG3925>`__","Concept ``formattable``'s definition is incorrect","Varna June 2023","|Complete|","17.0","|format|" "`3927 <https://wg21.link/LWG3927>`__","Unclear preconditions for ``operator[]`` for sequence containers","Varna June 2023","|Nothing To Do|","","" diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv --- a/libcxx/docs/Status/RangesViews.csv +++ b/libcxx/docs/Status/RangesViews.csv @@ -35,4 +35,4 @@ C++23,`as_const <https://wg21.link/P2278R4>`_,Unassigned,No patch yet,Not started C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,✅ C++23,`stride <https://wg21.link/P1899R3>`_,Hristo Hristov,No patch yet,In Progress -C++23,`enumerate <https://wg21.link/P2164R9>`_,Hristo Hristov,No patch yet,In Progress +C++23,`enumerate <https://wg21.link/P2164R9>`_,Hristo Hristov,No patch yet,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -614,6 +614,7 @@ __ranges/empty_view.h __ranges/enable_borrowed_range.h __ranges/enable_view.h + __ranges/enumerate_view.h __ranges/filter_view.h __ranges/from_range.h __ranges/iota_view.h diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h --- a/libcxx/include/__ranges/concepts.h +++ b/libcxx/include/__ranges/concepts.h @@ -141,6 +141,17 @@ (is_lvalue_reference_v<_Tp> || (movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>)))); +# if _LIBCPP_STD_VER >= 23 + + // [concept.object] + + template <class _Rp> + concept __range_with_movable_references = + ranges::input_range<_Rp> && std::move_constructible<ranges::range_reference_t<_Rp>> && + std::move_constructible<ranges::range_rvalue_reference_t<_Rp>>; // exposition only + +# endif // _LIBCPP_STD_VER >= 23 + } // namespace ranges #endif // _LIBCPP_STD_VER >= 20 diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/enumerate_view.h @@ -0,0 +1,331 @@ +// -*- 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_ENUMERATE_VIEW_H +#define _LIBCPP___RANGES_ENUMERATE_VIEW_H + +#include <__concepts/convertible_to.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/distance.h> +#include <__iterator/iter_move.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 <__type_traits/maybe_const.h> +#include <__utility/forward.h> +#include <__utility/move.h> +#include <tuple> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { + +// [range.enumerate.view] + +template <view _View> + requires __range_with_movable_references<_View> +class enumerate_view : public view_interface<enumerate_view<_View>> { + _View __base_ = _View(); // exposition only + + // [range.enumerate.iterator], class template enumerate_view::iterator + template <bool _Const> + class __iterator; // exposition only + + // [range.enumerate.sentinel], class template enumerate_view::sentinel + template <bool _Const> + class __sentinel; // exposition only + +public: + template <bool _AnyConst> + _LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto) __get_current(const __iterator<_AnyConst>& __iter) { + return (__iter.__current_); + } + +public: + _LIBCPP_HIDE_FROM_ABI constexpr enumerate_view() + requires default_initializable<_View> + = default; + _LIBCPP_HIDE_FROM_ABI constexpr explicit enumerate_view(_View __base) : __base_(std::move(__base)){}; + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator<false>(ranges::begin(__base_), 0); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires __range_with_movable_references<const _View> + { + return __iterator<true>(ranges::begin(__base_), 0); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (common_range<_View> && sized_range<_View>) + return __iterator<false>(ranges::end(__base_), ranges::distance(__base_)); + else + return __sentinel<false>(ranges::end(__base_)); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires __range_with_movable_references<const _View> + { + if constexpr (common_range<const _View> && sized_range<const _View>) + return __iterator<true>(ranges::end(__base_), ranges::distance(__base_)); + else + return __sentinel<true>(ranges::end(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range<const _View> + { + return ranges::size(__base_); + } + + _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_); } +}; + +template <class _Range> +enumerate_view(_Range&&) -> enumerate_view<views::all_t<_Range>>; + +// [range.enumerate.iterator] + +template <view _View> + requires __range_with_movable_references<_View> +template <bool _Const> +class enumerate_view<_View>::__iterator { + using _Base = __maybe_const<_Const, _View>; // exposition only + + 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{}; + } + } + + friend class enumerate_view<_View>; + +public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(__get_iterator_concept()); + using difference_type = range_difference_t<_Base>; + using value_type = tuple<difference_type, range_value_t<_Base>>; + +private: + using __reference_type = tuple<difference_type, range_reference_t<_Base>>; // exposition only + iterator_t<_Base> __current_ = iterator_t<_Base>(); // exposition only + difference_type __pos_ = 0; // exposition only + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current, difference_type __pos) + : __current_(std::move(__current)), __pos_(std::move(__pos)) {} // exposition only + +public: + _LIBCPP_HIDE_FROM_ABI __iterator() + requires default_initializable<iterator_t<_Base>> + = default; + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i) + requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> + : __current_(std::move(__i.__current)), __pos_(std::move(__i.__pos)){}; + + _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 difference_type index() const noexcept { return __pos_; } + + _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const { return __reference_type(__pos_, *__current_); } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + ++__current_; + ++__pos_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { return ++*this; } + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) + requires forward_range<_Base> + { + auto __temp = *this; + ++*this; + return __temp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() + requires bidirectional_range<_Base> + { + --__current_; + --__pos_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires bidirectional_range<_Base> + { + auto __temp = *this; + --*this; + return *__temp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n) + requires random_access_range<_Base> + { + __current_ += __n; + __pos_ += __n; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n) + requires random_access_range<_Base> + { + __current_ -= __n; + __pos_ -= __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const + requires random_access_range<_Base> + { + return __reference_type(__pos_ + __n, __current_[__n]); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) noexcept { + return __x.__pos_ == __y.__pos_; + } + _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering + operator<=>(const __iterator& __x, const __iterator& __y) noexcept { + return __x.__pos_ <=> __y.__pos_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + auto __temp = __i; + __temp += __n; + return __temp; + } + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires random_access_range<_Base> + { + return __i + __n; + } + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + auto __temp = __i; + __temp -= __n; + return __temp; + } + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(const __iterator& __x, const __iterator& __y) noexcept { + return __x.__pos_ - __y.__pos_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept( + noexcept(ranges::iter_move(__i.__current_)) && is_nothrow_move_constructible_v<range_rvalue_reference_t<_Base>>) { + return tuple<difference_type, range_rvalue_reference_t<_Base>>(__i.__pos_, ranges::iter_move(__i.__current_)); + } +}; + +// [range.enumerate.sentinel] + +template <view _View> + requires __range_with_movable_references<_View> +template <bool _Const> +class enumerate_view<_View>::__sentinel { + using _Base = __maybe_const<_Const, _View>; // exposition only + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + _LIBCPP_HIDE_FROM_ABI // exposition only + constexpr explicit __sentinel(sentinel_t<_Base> __end) + : __end_(std::move(__end)) {} // exposition only + + friend class enumerate_view<_View>; + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __other) + requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>> + : __end_(std::move(__other.__end_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; } + + template <bool _OtherConst> + requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return enumerate_view<_View>::__get_current(__x) == __y.__end_; + } + + template <bool _OtherConst> + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return enumerate_view<_View>::__get_current(__x) - __y.__end_; + } + + template <bool _OtherConst> + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) { + return __x.__end_ - enumerate_view<_View>::__get_current(__y); + } +}; + +template <class _View> +constexpr bool enable_borrowed_range<enumerate_view<_View>> = enable_borrowed_range<_View>; // freestanding + +namespace views { +namespace __enumerate { + +// clang-format off +struct __fn : __range_adaptor_closure<__fn> { + template <class _Range> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(enumerate_view(std::forward<_Range>(__range)))) + -> decltype(enumerate_view(std::forward<_Range>(__range))) { + return enumerate_view(std::forward<_Range>(__range)); + } +}; +// clang-format on + +} // namespace __enumerate + +inline namespace __cpo { + +inline constexpr auto enumerate = __enumerate::__fn{}; + +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_ENUMERATE_VIEW_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 @@ -1677,6 +1677,7 @@ module std_private_ranges_empty_view [system] { header "__ranges/empty_view.h" } module std_private_ranges_enable_borrowed_range [system] { header "__ranges/enable_borrowed_range.h" } module std_private_ranges_enable_view [system] { header "__ranges/enable_view.h" } +module std_private_ranges_enumerate_view [system] { header "__ranges/enumerate_view.h" } module std_private_ranges_filter_view [system] { header "__ranges/filter_view.h" export std_private_ranges_range_adaptor diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -314,6 +314,17 @@ namespace views { template<class T> inline constexpr unspecified istream = unspecified; } + // [range.enumerate], enumerate view + template<view View> + requires see below + class enumerate_view; // freestanding, since C++23 + + template<class View> + constexpr bool enable_borrowed_range<enumerate_view<View>> = // freestanding, since C++23 + enable_borrowed_range<View>; + + namespace views { inline constexpr unspecified enumerate = unspecified; } // freestanding, since C++23 + // [range.zip], zip view template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) @@ -385,6 +396,7 @@ #include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> +#include <__ranges/enumerate_view.h> #include <__ranges/filter_view.h> #include <__ranges/from_range.h> #include <__ranges/iota_view.h> diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -156,6 +156,7 @@ __cpp_lib_ranges_as_rvalue 202207L <ranges> __cpp_lib_ranges_chunk 202202L <ranges> __cpp_lib_ranges_chunk_by 202202L <ranges> +__cpp_lib_ranges_enumerate 202302L <ranges> __cpp_lib_ranges_iota 202202L <numeric> __cpp_lib_ranges_join_with 202202L <ranges> __cpp_lib_ranges_repeat 202207L <ranges> @@ -435,6 +436,7 @@ # define __cpp_lib_ranges_as_rvalue 202207L // # define __cpp_lib_ranges_chunk 202202L // # define __cpp_lib_ranges_chunk_by 202202L +# define __cpp_lib_ranges_enumerate 202302L // # define __cpp_lib_ranges_iota 202202L // # define __cpp_lib_ranges_join_with 202202L # define __cpp_lib_ranges_repeat 202207L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -322,6 +322,13 @@ using std::ranges::views::cartesian_product; } #endif + + // [range.enumerate] + using std::ranges::enumerate_view; + + namespace views { + using std::ranges::views::enumerate; + } } // namespace ranges namespace views = ranges::views; diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -20,6 +20,7 @@ __cpp_lib_ranges_as_rvalue 202207L [C++23] __cpp_lib_ranges_chunk 202202L [C++23] __cpp_lib_ranges_chunk_by 202202L [C++23] + __cpp_lib_ranges_enumerate 202302L [C++23] __cpp_lib_ranges_join_with 202202L [C++23] __cpp_lib_ranges_repeat 202207L [C++23] __cpp_lib_ranges_slide 202202L [C++23] @@ -48,6 +49,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should not be defined before c++23" # endif @@ -86,6 +91,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should not be defined before c++23" # endif @@ -124,6 +133,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should not be defined before c++23" # endif @@ -165,6 +178,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should not be defined before c++23" # endif @@ -227,6 +244,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should be defined in c++23" +# endif +# if __cpp_lib_ranges_enumerate != 202302L +# error "__cpp_lib_ranges_enumerate should have the value 202302L in c++23" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should be defined in c++23" @@ -322,6 +346,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should be defined in c++26" +# endif +# if __cpp_lib_ranges_enumerate != 202302L +# error "__cpp_lib_ranges_enumerate should have the value 202302L in c++26" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should be defined in c++26" diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -144,6 +144,7 @@ __cpp_lib_ranges_as_rvalue 202207L [C++23] __cpp_lib_ranges_chunk 202202L [C++23] __cpp_lib_ranges_chunk_by 202202L [C++23] + __cpp_lib_ranges_enumerate 202302L [C++23] __cpp_lib_ranges_iota 202202L [C++23] __cpp_lib_ranges_join_with 202202L [C++23] __cpp_lib_ranges_repeat 202207L [C++23] @@ -700,6 +701,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_iota # error "__cpp_lib_ranges_iota should not be defined before c++23" # endif @@ -1458,6 +1463,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_iota # error "__cpp_lib_ranges_iota should not be defined before c++23" # endif @@ -2387,6 +2396,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_iota # error "__cpp_lib_ranges_iota should not be defined before c++23" # endif @@ -3586,6 +3599,10 @@ # error "__cpp_lib_ranges_chunk_by should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should not be defined before c++23" +# endif + # ifdef __cpp_lib_ranges_iota # error "__cpp_lib_ranges_iota should not be defined before c++23" # endif @@ -4983,6 +5000,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should be defined in c++23" +# endif +# if __cpp_lib_ranges_enumerate != 202302L +# error "__cpp_lib_ranges_enumerate should have the value 202302L in c++23" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_iota # error "__cpp_lib_ranges_iota should be defined in c++23" @@ -6536,6 +6560,13 @@ # endif # endif +# ifndef __cpp_lib_ranges_enumerate +# error "__cpp_lib_ranges_enumerate should be defined in c++26" +# endif +# if __cpp_lib_ranges_enumerate != 202302L +# error "__cpp_lib_ranges_enumerate should have the value 202302L in c++26" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_iota # error "__cpp_lib_ranges_iota should be defined in c++26" diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// std::views::enumerate + +#include <cassert> +#include <ranges> +#include <string_view> + +#include "types.h" + +// Concepts + +template <class View, class T> +concept CanBePiped = requires(View&& view, T&& t) { + { std::forward<View>(view) | std::forward<T>(t) }; +}; + +// Helpers + +template <typename View, typename T = int> +constexpr void +compareViews(View v, + std::initializer_list<std::tuple<typename std::ranges::iterator_t<View>::difference_type, T>> list) { + auto b1 = v.begin(); + auto e1 = v.end(); + auto b2 = list.begin(); + auto e2 = list.end(); + for (; b1 != e1 && b2 != e2; ++b1, ++b2) { + assert(*b1 == *b2); + } + assert(b1 == e1); + assert(b2 == e2); +} + +// Test SFINAE friendliness + +static_assert(CanBePiped<Range, decltype(std::views::enumerate)>); + +static_assert(!std::is_invocable_v<decltype(std::views::enumerate)>); +static_assert(std::is_invocable_v<decltype(std::views::enumerate), Range>); +static_assert(!std::is_invocable_v<decltype(std::views::enumerate), NotAView>); +static_assert(!std::is_invocable_v<decltype(std::views::enumerate), NotInvocable>); + +static_assert(std::is_same_v<decltype(std::ranges::views::enumerate), decltype(std::views::enumerate)>); + +constexpr bool test() { + // Test `views::enumerate_view(v)` + { + int buff[] = {0, 1, 2, 3}; + + using Result = std::ranges::enumerate_view<Range>; + Range const range(buff, buff + 4); + + std::same_as<Result> decltype(auto) result = std::views::enumerate(range); + compareViews(result, {{0, 0}, {1, 1}, {2, 2}, {3, 3}}); + } + { + std::string_view sv{"babazmt"}; + using Result = std::ranges::enumerate_view<std::string_view>; + + std::same_as<Result> decltype(auto) result = std::views::enumerate(sv); + compareViews(result, {{0, 'b'}, {1, 'a'}, {2, 'b'}, {3, 'a'}, {4, 'z'}, {5, 'm'}, {6, 't'}}); + } + // Test `adaptor | views::enumerate` + { + int buff[] = {0, 1, 2, 3}; + + using Result = std::ranges::enumerate_view<Range>; + Range const range(buff, buff + 4); + + std::same_as<Result> decltype(auto) result = range | std::views::enumerate; + compareViews(result, {{0, 0}, {1, 1}, {2, 2}, {3, 3}}); + } + { + std::string_view sv{"babazmt"}; + using Result = std::ranges::enumerate_view<std::string_view>; + + std::same_as<Result> decltype(auto) result = sv | std::views::enumerate; + compareViews(result, {{0, 'b'}, {1, 'a'}, {2, 'b'}, {3, 'a'}, {4, 'z'}, {5, 'm'}, {6, 't'}}); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.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, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr V base() const & requires copy_constructible<V> { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include <cassert> +#include <ranges> + +#include "types.h" + +constexpr bool test() { + // Check the const& overload + { + int buff[] = {0, 1, 2, 3}; + + Range const range(buff, buff + 4); + + std::ranges::enumerate_view<Range> const view{range}; + std::same_as<Range> decltype(auto) result = view.base(); + assert(result.wasCopyInitialized); + assert(range.begin() == result.begin()); + assert(range.end() == result.end()); + } + // Check the && overload + { + int buff[] = {0, 1, 2, 3}; + + Range const range(buff, buff + 4); + + std::ranges::enumerate_view<Range> view{range}; + std::same_as<Range> decltype(auto) result = std::move(view).base(); + assert(result.wasMoveInitialized); + assert(range.begin() == result.begin()); + assert(range.end() == result.end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr auto begin() requires (!simple-view<V>) +// { return iterator<false>(ranges::begin(base_), 0); } +// constexpr auto begin() const requires range-with-movable-references<const V> +// { return iterator<true>(ranges::begin(base_), 0); } + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "types.h" + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Check the return type of `.begin()` + { + Range range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + using Iterator = std::ranges::iterator_t<decltype(view)>; + ASSERT_SAME_TYPE(Iterator, decltype(view.begin())); + } + // begin() over an empty range + { + Range range(buff, buff); + + std::ranges::enumerate_view view(range); + auto it = view.begin(); + assert(base(it.base()) == buff); + assert(it == view.end()); + } + // begin() over a 1-element range + { + Range range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + auto it = view.begin(); + assert(base(it.base()) == buff); + } + // begin() over a N-element range + { + Range range(buff, buff + 8); + + std::ranges::enumerate_view view(range); + auto it = view.begin(); + assert(base(it.base()) == buff); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// template<class R> +// enumerate_view(R&&) -> enumerate_view<views::all_t<R>>; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "types.h" + +constexpr bool test() { + { + JustAView jv; + std::ranges::enumerate_view view(jv); + static_assert(std::is_same_v<decltype(view), std::ranges::enumerate_view<JustAView>>); + } + // Test with a range that isn't a view, to make sure we properly use views::all_t in the implementation. + { + NotAViewRange range; + std::ranges::enumerate_view view(range); + static_assert(std::is_same_v<decltype(view), std::ranges::enumerate_view<std::ranges::ref_view<NotAViewRange>>>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.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, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr explicit enumerate_view(V base); + +#include <ranges> + +#include <cassert> +#include <tuple> +#include <type_traits> + +constexpr int buff[] = {0, 1}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 1) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + + int const* begin_; + int const* end_; +}; + +template <bool DefaultInitializable> +struct DefaultInitializableView : std::ranges::view_base { + constexpr explicit DefaultInitializableView() + requires DefaultInitializable + = default; + + int* begin() const; + int* end() const; +}; + +struct NoDefaultView : std::ranges::view_base { + NoDefaultView() = delete; + + int* begin() const; + int* end() const; +}; + +// SFINAE + +static_assert(!std::is_default_constructible_v<std::ranges::enumerate_view<NoDefaultView>>); +static_assert(std::is_default_constructible_v<std::ranges::enumerate_view<DefaultInitializableView<true>>>); +static_assert(!std::is_default_constructible_v<std::ranges::enumerate_view<DefaultInitializableView<false>>>); + +constexpr bool test() { + using EnumerateView = std::ranges::enumerate_view<DefaultConstructibleView>; + + { + EnumerateView view; + + assert((*view.begin() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{0, 0})); + assert((*view.end() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{1, 1})); + + auto [bi, bv] = *view.begin(); + assert(bi == 0); + assert(bv == 0); + + auto [ei, ev] = *view.end(); + assert(ei == 1); + assert(ev == 1); + } + { + EnumerateView view = {}; + + assert((*view.begin() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{0, 0})); + assert((*view.end() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{1, 1})); + + auto [bi, bv] = *view.begin(); + assert(bi == 0); + assert(bv == 0); + + auto [ei, ev] = *view.end(); + assert(ei == 1); + assert(ev == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.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, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr enumerate_view() requires default_initializable<V> = default; + +#include <ranges> + +#include <cassert> +#include <tuple> +#include <type_traits> + +constexpr int buff[] = {0, 1}; + +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 1) {} + constexpr int const* begin() const { return begin_; } + constexpr int const* end() const { return end_; } + + int const* begin_; + int const* end_; +}; + +template <bool DefaultInitializable> +struct DefaultInitializableView : std::ranges::view_base { + constexpr explicit DefaultInitializableView() + requires DefaultInitializable + = default; + + int* begin() const; + int* end() const; +}; + +struct NoDefaultView : std::ranges::view_base { + NoDefaultView() = delete; + + int* begin() const; + int* end() const; +}; + +// SFINAE + +static_assert(!std::is_default_constructible_v<std::ranges::enumerate_view<NoDefaultView>>); +static_assert(std::is_default_constructible_v<std::ranges::enumerate_view<DefaultInitializableView<true>>>); +static_assert(!std::is_default_constructible_v<std::ranges::enumerate_view<DefaultInitializableView<false>>>); + +constexpr bool test() { + using EnumerateView = std::ranges::enumerate_view<DefaultConstructibleView>; + + { + EnumerateView view; + + assert((*view.begin() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{0, 0})); + assert((*view.end() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{1, 1})); + + auto [bi, bv] = *view.begin(); + assert(bi == 0); + assert(bv == 0); + + auto [ei, ev] = *view.end(); + assert(ei == 1); + assert(ev == 1); + } + { + EnumerateView view = {}; + + assert((*view.begin() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{0, 0})); + assert((*view.end() == std::tuple<std::ranges::iterator_t<EnumerateView>::difference_type, int>{1, 1})); + + auto [bi, bv] = *view.begin(); + assert(bi == 0); + assert(bv == 0); + + auto [ei, ev] = *view.end(); + assert(ei == 1); + assert(ev == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// template<class View> +// constexpr bool enable_borrowed_range<enumerate_view<View>> = // freestanding +// enable_borrowed_range<View>; + +#include <cassert> +#include <ranges> + +struct NonBorrowedRange : std::ranges::view_base { + int* begin(); + int* end(); +}; + +struct BorrowedRange : std::ranges::view_base { + int* begin(); + int* end(); +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange> = true; + +static_assert(!std::ranges::borrowed_range<std::ranges::enumerate_view<NonBorrowedRange>>); +static_assert(std::ranges::borrowed_range<std::ranges::enumerate_view<BorrowedRange>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr auto end() requires (!simple-view<V>) { +// if constexpr (common_range<V> && sized_range<V>) +// return iterator<false>(ranges::end(base_), ranges::distance(base_)); +// else +// return sentinel<false>(ranges::end(base_)); +// } +// constexpr auto end() const requires range-with-movable-references<const V> { +// if constexpr (common_range<const V> && sized_range<const V>) +// return iterator<true>(ranges::end(base_), ranges::distance(base_)); +// else +// return sentinel<true>(ranges::end(base_)); +// } + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "types.h" + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Check the return type of `.end()` + { + Range range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + using Iterator = std::ranges::iterator_t<decltype(view)>; + ASSERT_SAME_TYPE(Iterator, decltype(view.end())); + } + // begin() over an empty range + { + Range range(buff, buff); + + std::ranges::enumerate_view view(range); + auto it = view.begin(); + assert(base(it.base()) == buff); + assert(it == view.end()); + } + // begin() over a 1-element range + { + Range range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + auto it = view.end(); + assert(base(it.base()) == buff + 1); + } + // begin() over a N-element range + { + Range range(buff, buff + 8); + + std::ranges::enumerate_view view(range); + auto it = view.end(); + assert(base(it.base()) == buff + 8); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/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, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr iterator& operator+=(difference_type x) +// requires random_access_range<Base>; +// constexpr iterator& operator-=(difference_type x) +// requires random_access_range<Base>; + +// friend constexpr iterator operator+(const iterator& x, difference_type y) +// requires random_access_range<Base>; +// friend constexpr iterator operator+(difference_type x, const iterator& y) +// requires random_access_range<Base>; +// friend constexpr iterator operator-(const iterator& x, difference_type y) +// requires random_access_range<Base>; +// friend constexpr difference_type operator-(const iterator& x, const iterator& y) + +#include <ranges> + +#include "test_iterators.h" + +// Concepts + +template <class T, class U> +concept CanPlus = requires(T t, U u) { t + u; }; + +template <class T, class U> +concept CanPlusEqual = requires(T t, U u) { t += u; }; + +template <class T, class U> +concept CanMinus = requires(T t, U u) { t - u; }; + +template <class T, class U> +concept CanMinusEqual = requires(T t, U u) { t -= u; }; + +template <class BaseRange> +using EnumerateIter = std::ranges::iterator_t<std::ranges::enumerate_view<BaseRange>>; + +using RandomAccessRange = std::ranges::subrange<int*>; + +// SFINAE. + +static_assert(std::ranges::random_access_range<RandomAccessRange>); +static_assert( + std::sized_sentinel_for<std::ranges::iterator_t<RandomAccessRange>, std::ranges::iterator_t<RandomAccessRange>>); + +static_assert(CanPlus<EnumerateIter<RandomAccessRange>, int>); +static_assert(CanPlus<int, EnumerateIter<RandomAccessRange>>); +static_assert(CanPlusEqual<EnumerateIter<RandomAccessRange>, int>); +static_assert(CanMinus<EnumerateIter<RandomAccessRange>, int>); +static_assert(CanMinus<EnumerateIter<RandomAccessRange>, EnumerateIter<RandomAccessRange>>); +static_assert(CanMinusEqual<EnumerateIter<RandomAccessRange>, int>); + +using BidirectionalRange = std::ranges::subrange<bidirectional_iterator<int*>>; +static_assert(!std::ranges::random_access_range<BidirectionalRange>); +static_assert( + !std::sized_sentinel_for<std::ranges::iterator_t<BidirectionalRange>, std::ranges::iterator_t<BidirectionalRange>>); + +static_assert(!CanPlus<EnumerateIter<BidirectionalRange>, int>); +static_assert(!CanPlus<int, EnumerateIter<BidirectionalRange>>); +static_assert(!CanPlusEqual<EnumerateIter<BidirectionalRange>, int>); +static_assert(!CanMinus<EnumerateIter<BidirectionalRange>, int>); +static_assert(!CanMinusEqual<EnumerateIter<BidirectionalRange>, int>); + +constexpr bool test() { + int ts[] = {1, 2, 3, 4, 5}; + + RandomAccessRange r{&ts[0], &ts[0] + 5}; + auto ev = r | std::views::enumerate; + + // 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 - 4; + assert(it2.base() == &ts[1]); + + it1 -= 4; + assert(it1 == it2); + assert(it1.base() == &ts[1]); + } + // operator-(x, y) + { + assert((ev.end() - ev.begin()) == 5); + + auto it1 = ev.begin() + 2; + auto it2 = ev.end() - 2; + 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.enumerate/iterator/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr sentinel_t<Base> base() const; + +#include <ranges> + +#include <array> +#include <cassert> +#include <concepts> +#include <utility> + +#include "test_iterators.h" +#include "../types.h" + +template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>> +constexpr void test() { + using View = MinimalView<Iterator, Sentinel>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateIterator = std::ranges::iterator_t<EnumerateView>; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + + return EnumerateView(std::move(view)); + }; + + std::array<int, 5> array{0, 1, 2, 3, 84}; + const auto view = make_enumerate_view(array.begin(), array.end()); + + // Test the const& version + { + EnumerateIterator const it = view.begin(); + std::same_as<const Iterator&> decltype(auto) result = it.base(); + ASSERT_NOEXCEPT(it.base()); + assert(base(base(result)) == array.begin()); + } + + // Test the && version + { + EnumerateIterator it = view.begin(); + std::same_as<Iterator> decltype(auto) result = std::move(it).base(); + assert(base(base(result)) == array.begin()); + } +} + +constexpr bool tests() { + test<cpp17_input_iterator<int*>>(); + test<cpp20_input_iterator<int*>>(); + test<forward_iterator<int*>>(); + test<bidirectional_iterator<int*>>(); + test<random_access_iterator<int*>>(); + test<contiguous_iterator<int*>>(); + test<int*>(); + test<int const*>(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp @@ -0,0 +1,101 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// friend constexpr strong_ordering operator<=>(const iterator& x, const iterator& y) noexcept; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "../types.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)); +} + +template <class T, class U> +concept HasSpaceship = requires(T t, U u) { t <=> u; }; + +constexpr bool test() { + int buff[] = {0, 1, 2, 3}; + { + using View = std::ranges::enumerate_view<Range>; + Range const range(buff, buff + 4); + + std::same_as<View> decltype(auto) ev = std::views::enumerate(range); + + using Iterator = decltype(ev.begin()); + static_assert(HasSpaceship<Iterator, Iterator>); + + auto it1 = ev.begin(); + auto it2 = it1 + 1; + + compareOperatorTest(it1, it2); + + assert((it1 <=> it2) == std::strong_ordering::less); + assert((it1 <=> it1) == std::strong_ordering::equal); + assert((it2 <=> it2) == std::strong_ordering::equal); + assert((it2 <=> it1) == std::strong_ordering::greater); + } + { + // Test an old-school iterator with no operator<=> + using Iterator = random_access_iterator<int*>; + using Subrange = std::ranges::subrange<Iterator>; + static_assert(!std::three_way_comparable<Iterator>); + using EnumerateView = std::ranges::enumerate_view<Subrange>; + static_assert(std::three_way_comparable<std::ranges::iterator_t<EnumerateView>>); + + auto ev = Subrange{Iterator{&buff[0]}, Iterator{&buff[0] + 3}} | std::views::enumerate; + auto it1 = ev.begin(); + auto it2 = it1 + 1; + + compareOperatorTest(it1, it2); + + assert((it1 <=> it2) == std::strong_ordering::less); + assert((it1 <=> it1) == std::strong_ordering::equal); + assert((it2 <=> it2) == std::strong_ordering::equal); + assert((it2 <=> it1) == std::strong_ordering::greater); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr iterator(iterator<!Const> i) +// requires Const && convertible_to<iterator_t<V>, iterator_t<Base>>; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" + +constexpr bool test() { return true; } + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// iterator() requires default_initializable<iterator_t<Base>> = default; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "../types.h" + +template <class Iterator, bool IsNoexcept = true> +constexpr void test_default_constructible() { + using View = MinimalView<Iterator, sentinel_wrapper<Iterator>>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateIterator = std::ranges::iterator_t<EnumerateView>; + + EnumerateIterator it1; + EnumerateIterator it2{}; + + assert(it1 == it2); + + static_assert(noexcept(EnumerateIterator()) == IsNoexcept); +} + +template <class Iterator> +constexpr void test_not_default_constructible() { + // Make sure the iterator is *not* default constructible when the underlying iterator isn't. + using View = MinimalView<Iterator, sentinel_wrapper<Iterator>>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateIterator = std::ranges::iterator_t<EnumerateView>; + + static_assert(!std::is_default_constructible_v<EnumerateIterator>); +} + +constexpr bool tests() { + // clang-format off + test_not_default_constructible<cpp17_input_iterator<int*>>(); + test_not_default_constructible<cpp20_input_iterator<int*>>(); + test_default_constructible<forward_iterator<int*>, /* noexcept */ false>(); + test_default_constructible<bidirectional_iterator<int*>, /* noexcept */ false>(); + test_default_constructible<random_access_iterator<int*>, /* noexcept */ false>(); + test_default_constructible<contiguous_iterator<int*>, /* noexcept */ false>(); + test_default_constructible<int*, /* noexcept */ true>(); + // clang-format on + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr auto operator*() const { +// return reference-type(pos_, *current_); +// } + +#include <array> +#include <cassert> +#include <concepts> +#include <cstddef> +#include <utility> +#include <tuple> + +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +template <class Iterator, class ValueType = int, class Sentinel = sentinel_wrapper<Iterator>> +constexpr void test() { + using View = MinimalView<Iterator, Sentinel>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateIterator = std::ranges::iterator_t<EnumerateView>; + + using Result = std::tuple<typename EnumerateIterator::difference_type, + std::ranges::range_reference_t<MinimalView<Iterator, Sentinel>>>; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + return EnumerateView(std::move(view)); + }; + + std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + EnumerateView view = make_enumerate_view(array.begin(), array.end()); + + auto it = view.begin(); + for (std::size_t index = 0; index < array.size(); ++index) { + std::same_as<Result> decltype(auto) result = *it; + + auto [resultIndex, resultValue] = result; + assert(std::cmp_equal(index, resultIndex)); + assert(array[index] == resultValue); + + ++it; + } + + assert(it == view.end()); +} + +constexpr bool tests() { + test<cpp17_input_iterator<int*>>(); + test<cpp20_input_iterator<int*>>(); + test<forward_iterator<int*>>(); + test<bidirectional_iterator<int*>>(); + test<random_access_iterator<int*>>(); + test<contiguous_iterator<int*>>(); + test<int*>(); + + test<cpp17_input_iterator<int const*>, int const>(); + test<cpp20_input_iterator<int const*>, int const>(); + test<forward_iterator<int const*>, int const>(); + test<bidirectional_iterator<int const*>, int const>(); + test<random_access_iterator<int const*>, int const>(); + test<contiguous_iterator<int const*>, int const>(); + test<int const*, int const>(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" + +constexpr bool test() { return true; } + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr difference_type index() const noexcept; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" + +constexpr bool test() { return true; } + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// friend constexpr auto iter_move(const iterator& i) +// noexcept(noexcept(ranges::iter_move(i.current_)) && +// is_nothrow_move_constructible_v<range_rvalue_reference_t<Base>>) { +// return tuple<difference_type, +// range_rvalue_reference_t<Base>>(i.pos_, ranges::iter_move(i.current_)); +// } + +#include <ranges> +#include <array> +#include <cassert> +#include <utility> + +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +template <class Iterator, bool HasNoexceptIterMove> +constexpr void test() { + using Sentinel = sentinel_wrapper<Iterator>; + using View = MinimalView<Iterator, Sentinel>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateIterator = std::ranges::iterator_t<EnumerateView>; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + + return EnumerateView(std::move(view)); + }; + + std::array<int, 5> array{0, 1, 2, 3, 4}; + EnumerateView view = make_enumerate_view(array.begin(), array.end()); + EnumerateIterator const it = view.begin(); + + auto&& result = iter_move(it); + + static_assert(std::is_same_v<decltype(result), + std::tuple<typename std::ranges::iterator_t<EnumerateView>::difference_type, int&&>&&>); + static_assert(std::is_same_v<decltype(result), std::tuple<typename decltype(it)::difference_type, int&&>&&>); + + assert(get<0>(result) == 0); + assert(&get<1>(result) == array.begin()); + + static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove); +} + +constexpr bool tests() { + // clang-format off + test<cpp17_input_iterator<int*>, /* noexcept */ false>(); + test<cpp20_input_iterator<int*>, /* noexcept */ false>(); + test<forward_iterator<int*>, /* noexcept */ false>(); + test<bidirectional_iterator<int*>, /* noexcept */ false>(); + test<random_access_iterator<int*>, /* noexcept */ false>(); + test<contiguous_iterator<int*>, /* noexcept */ false>(); + test<int*, /* noexcept */ true>(); + test<NoexceptIterMoveInputIterator<true>, /* noexcept */ true>(); + test<NoexceptIterMoveInputIterator<false>, /* noexcept */ false>(); + // clang-format on + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr auto operator[](difference_type n) const +// requires random_access_range<Base> +// { return reference-type(pos_ + n, current_[n]); } + +#include <cassert> +#include <ranges> +#include <tuple> + +#include "test_iterators.h" + +template <class T, class U> +concept HasSubscriptOperator = requires(T t, U u) { t[u]; }; + +template <class BaseRange> +using EnumerateIterator = std::ranges::iterator_t<std::ranges::enumerate_view<BaseRange>>; + +using RandomAccessRange = std::ranges::subrange<int*>; +static_assert(std::ranges::random_access_range<RandomAccessRange>); + +static_assert(HasSubscriptOperator<EnumerateIterator<RandomAccessRange>, int>); + +using BidirectionalRange = std::ranges::subrange<bidirectional_iterator<int*>>; +static_assert(!std::ranges::random_access_range<BidirectionalRange>); + +static_assert(!HasSubscriptOperator<EnumerateIterator<BidirectionalRange>, int>); + +constexpr bool test() { + // Reference + { + int ts[] = {0, 1, 2, 3, 84}; + auto view = ts | std::views::enumerate; + auto it = view.begin(); + + assert(std::get<0>(it[0]) == std::get<0>(*it)); + assert(std::get<1>(it[0]) == std::get<1>(*it)); + assert(std::get<0>(it[2]) == std::get<0>(*(it + 2))); + assert(std::get<1>(it[2]) == std::get<1>(*(it + 2))); + assert(std::get<0>(it[4]) == std::get<0>(*(it + 4))); + assert(std::get<1>(it[4]) == std::get<1>(*(it + 4))); + + static_assert(std::is_same_v<decltype(it[2]), std::tuple<decltype(it)::difference_type, int&>>); + } + + // Value + { + auto view = std::views::iota(0, 5) | std::views::enumerate; + auto it = view.begin(); + + assert(it[0] == *it); + assert(it[2] == *(it + 2)); + assert(it[4] == *(it + 4)); + + static_assert(std::is_same_v<decltype(it[2]), std::tuple<decltype(it)::difference_type, int>>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/base.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr sentinel_t<Base> base() const; + +#include <ranges> + +#include <array> +#include <cassert> +#include <concepts> +#include <utility> + +#include "test_iterators.h" +#include "../types.h" + +template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>> +constexpr void test() { + using View = MinimalView<Iterator, Sentinel>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView>; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + + return EnumerateView(std::move(view)); + }; + + std::array<int, 5> array{0, 1, 2, 3, 84}; + auto view = make_enumerate_view(array.begin(), array.end()); + + EnumerateSentinel const s = view.end(); + std::same_as<Sentinel> decltype(auto) result = s.base(); + assert(base(base(result)) == array.end()); +} + +constexpr bool tests() { + test<cpp17_input_iterator<int*>>(); + test<cpp20_input_iterator<int*>>(); + test<forward_iterator<int*>>(); + test<bidirectional_iterator<int*>>(); + test<random_access_iterator<int*>>(); + test<contiguous_iterator<int*>>(); + test<int*>(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr sentinel(sentinel<!Const> other) +// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>; + +// #include <cassert> +// #include <ranges> +// #include <tuple> + +// #include "../types.h" + +#include <ranges> + +#include <array> +#include <cassert> +#include <concepts> +#include <utility> + +#include "test_iterators.h" +#include "../types.h" + +template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>> +constexpr void test() { + using View = MinimalView<Iterator, Sentinel>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView>; + using EnumerateConstSentinel = std::ranges::sentinel_t<const EnumerateView>; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + + return EnumerateView(std::move(view)); + }; + + static_assert(std::is_convertible_v<EnumerateSentinel, EnumerateConstSentinel>); + + std::array<int, 5> array{0, 1, 2, 3, 84}; + auto view = make_enumerate_view(array.begin(), array.end()); + + std::same_as<EnumerateSentinel> decltype(auto) s = view.end(); + std::same_as<Sentinel> decltype(auto) sResult = s.base(); + assert(base(base(sResult)) == array.end()); + + // Test assignment + EnumerateConstSentinel cs = s; + std::same_as<Sentinel> decltype(auto) csResult = cs.base(); + assert(base(base(csResult)) == array.end()); +} + +constexpr bool tests() { + test<cpp17_input_iterator<int*>>(); + test<cpp20_input_iterator<int*>>(); + test<forward_iterator<int*>>(); + test<bidirectional_iterator<int*>>(); + test<random_access_iterator<int*>>(); + test<contiguous_iterator<int*>>(); + test<int*>(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.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, c++20 + +// <ranges> + +// class enumerate_view + +// iterator() requires default_initializable<iterator_t<Base>> = default; + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "../types.h" + +template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>> +constexpr void test() { + using View = MinimalView<Iterator, Sentinel>; + using EnumerateView = std::ranges::enumerate_view<View>; + using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView>; + + EnumerateSentinel s1; + EnumerateSentinel s2{}; + + assert(base(base(s1.base())) == base(base(s2.base()))); + + static_assert(noexcept(EnumerateSentinel())); +} + +constexpr bool tests() { + test<cpp17_input_iterator<int*>>(); + test<cpp20_input_iterator<int*>>(); + test<forward_iterator<int*>>(); + test<bidirectional_iterator<int*>>(); + test<random_access_iterator<int*>>(); + test<contiguous_iterator<int*>>(); + test<int*>(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// template<bool OtherConst> +// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); + +#include <ranges> + +#include <array> +#include <cassert> +#include <concepts> +#include <utility> + +#include "test_iterators.h" +#include "../types.h" + +template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>> +constexpr void test() { + using View = MinimalView<Iterator, Sentinel>; + + std::array<int, 5> array{0, 1, 2, 3, 84}; + + View v(Iterator(array.begin()), Sentinel(Iterator(array.end()))); + std::ranges::enumerate_view view(std::move(v)); + + auto const it = view.begin(); + auto const s = view.end(); + + std::same_as<bool> decltype(auto) eqResult = (it == s); + assert(!eqResult); + std::same_as<bool> decltype(auto) neqResult = (it != s); + assert(neqResult); +} + +constexpr bool tests() { + test<cpp17_input_iterator<int*>>(); + test<cpp20_input_iterator<int*>>(); + test<forward_iterator<int*>>(); + test<bidirectional_iterator<int*>>(); + test<random_access_iterator<int*>>(); + test<contiguous_iterator<int*>>(); + test<int*>(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// template<bool OtherConst> +// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr range_difference_t<maybe-const<OtherConst, V>> +// operator-(const iterator<OtherConst>& x, const sentinel& y); + +// template<bool OtherConst> +// requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> +// friend constexpr range_difference_t<maybe-const<OtherConst, V>> +// operator-(const sentinel& x, const iterator<OtherConst>& y); + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" + +template <bool Const> +struct Iter { + int* it_; + + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::input_iterator_tag; + + constexpr decltype(auto) operator*() const { return *it_; } + constexpr Iter& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } +}; + +template <bool Const> +struct Sent { + int* end_; + + constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } +}; + +template <bool Const> +struct SizedSent { + int* end_; + + constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; } + + friend constexpr auto operator-(const SizedSent& st, const Iter<Const>& it) { return st.end_ - it.it_; } + + friend constexpr auto operator-(const Iter<Const>& it, const SizedSent& st) { return it.it_ - st.end_; } +}; + +template <bool Const> +struct CrossSizedSent { + int* end_; + + template <bool C> + constexpr bool operator==(const Iter<C>& i) const { + return i.it_ == end_; + } + + template <bool C> + friend constexpr auto operator-(const CrossSizedSent& st, const Iter<C>& it) { + return st.end_ - it.it_; + } + + template <bool C> + friend constexpr auto operator-(const Iter<C>& it, const CrossSizedSent& st) { + return it.it_ - st.end_; + } +}; + +template <class T> +struct BufferView : std::ranges::view_base { + T* buffer_; + std::size_t size_; + + template <std::size_t N> + constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} + + template <std::size_t N> + constexpr BufferView(std::array<T, N>& arr) : buffer_(arr.data()), size_(N) {} +}; + +template <template <bool> class It, template <bool> class St> +struct Range : BufferView<int> { + using BufferView<int>::BufferView; + + using iterator = It<false>; + using sentinel = St<false>; + using const_iterator = It<true>; + using const_sentinel = St<true>; + + constexpr iterator begin() { return {buffer_}; } + constexpr const_iterator begin() const { return {buffer_}; } + constexpr sentinel end() { return sentinel{buffer_ + size_}; } + constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; } +}; + +template <class T, class U> +concept HasMinus = requires(const T t, const U u) { t - u; }; + +template <class BaseRange> +using EnumerateView = std::ranges::enumerate_view<BaseRange>; + +template <class BaseRange> +using EnumerateIter = std::ranges::iterator_t<EnumerateView<BaseRange>>; + +template <class BaseRange> +using EnumerateConstIter = std::ranges::iterator_t<const EnumerateView<BaseRange>>; + +template <class BaseRange> +using EnumerateSentinel = std::ranges::sentinel_t<EnumerateView<BaseRange>>; + +template <class BaseRange> +using EnumerateConstSentinel = std::ranges::sentinel_t<const EnumerateView<BaseRange>>; + +constexpr void testConstraints() { + // Base is not sized + { + using Base = Range<Iter, Sent>; + static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>); + static_assert(!HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>); + + static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>); + static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>); + + static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>); + static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>); + + static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>); + static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>); + } + + // Base is sized but not cross const + { + using Base = Range<Iter, SizedSent>; + static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>); + static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>); + + static_assert(!HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>); + static_assert(!HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>); + + static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>); + static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>); + + static_assert(!HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>); + static_assert(!HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>); + } + + // Base is cross const sized + { + using Base = Range<Iter, CrossSizedSent>; + static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateIter<Base>>); + static_assert(HasMinus<EnumerateIter<Base>, EnumerateSentinel<Base>>); + + static_assert(HasMinus<EnumerateSentinel<Base>, EnumerateConstIter<Base>>); + static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateSentinel<Base>>); + + static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateConstIter<Base>>); + static_assert(HasMinus<EnumerateConstIter<Base>, EnumerateConstSentinel<Base>>); + + static_assert(HasMinus<EnumerateConstSentinel<Base>, EnumerateIter<Base>>); + static_assert(HasMinus<EnumerateIter<Base>, EnumerateConstSentinel<Base>>); + } +} + +constexpr bool test() { + int buffer[] = {1, 2, 3, 4, 5}; + + // Base is sized but not cross const + { + using Base = Range<Iter, SizedSent>; + Base base{buffer}; + auto ev = base | std::views::enumerate; + auto iter = ev.begin(); + auto const_iter = std::as_const(ev).begin(); + auto sent = ev.end(); + auto const_sent = std::as_const(ev).end(); + + assert(iter - sent == -5); + assert(sent - iter == 5); + assert(const_iter - const_sent == -5); + assert(const_sent - const_iter == 5); + } + + // Base is cross const sized + { + using Base = Range<Iter, CrossSizedSent>; + Base base{buffer}; + auto ev = base | std::views::enumerate; + auto iter = ev.begin(); + auto const_iter = std::as_const(ev).begin(); + auto sent = ev.end(); + auto const_sent = std::as_const(ev).end(); + + assert(iter - sent == -5); + assert(sent - iter == 5); + assert(iter - const_sent == -5); + assert(const_sent - iter == 5); + assert(const_iter - sent == -5); + assert(sent - const_iter == 5); + assert(const_iter - const_sent == -5); + assert(const_sent - const_iter == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/size.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// <ranges> + +// class enumerate_view + +// constexpr auto size() requires sized_range<V> +// { return ranges::size(base_); } +// constexpr auto size() const requires sized_range<const V> +// { return ranges::size(base_); } + +#include <cassert> +#include <ranges> + +#include "test_iterators.h" +#include "types.h" + +template <class T> +concept HasSize = requires(T t) { t.size(); }; + +static_assert(HasSize<std::ranges::enumerate_view<SizedRange>>); +static_assert(HasSize<const std::ranges::enumerate_view<SizedRange>>); + +static_assert(std::ranges::sized_range<Range>); + +struct NonSizedRange : std::ranges::view_base { + using iterator = forward_iterator<int*>; + iterator begin() const; + iterator end() const; +}; + +static_assert(!std::ranges::sized_range<NonSizedRange>); +static_assert(!std::ranges::sized_range<const NonSizedRange>); + +static_assert(!HasSize<std::ranges::enumerate_view<NonSizedRange>>); +static_assert(!HasSize<const std::ranges::enumerate_view<NonSizedRange>>); + +constexpr bool test() { + int buffer[] = {1, 2, 3}; + + // Non-const and const are sized + { + auto view = std::views::enumerate(buffer); + assert(view.size() == 3); + assert(std::as_const(view).size() == 3); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/types.h @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// 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_ENUMERATE_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_H + +#include <ranges> + +#include "test_iterators.h" + +// Types + +struct Range : std::ranges::view_base { + using Iterator = cpp20_input_iterator<int*>; + using Sentinel = sentinel_wrapper<Iterator>; + + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {} + constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {} + Range& operator=(Range const&) = default; + Range& operator=(Range&&) = default; + + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + + int* begin_; + int* end_; + + bool wasCopyInitialized = false; + bool wasMoveInitialized = false; +}; + +static_assert(std::ranges::__range_with_movable_references<Range>); +static_assert(std::ranges::range<Range> && std::ranges::view<Range>); + +struct SizedRange : public Range { + std::size_t size_; + + constexpr std::size_t size() { return size_; } +}; + +struct JustAView : std::ranges::view_base { + JustAView() = default; + + forward_iterator<int*> begin() const; + sentinel_wrapper<forward_iterator<int*>> end() const; +}; + +static_assert(std::ranges::view<JustAView>); + +template <class Iterator, class Sentinel> +struct MinimalView : std::ranges::view_base { + constexpr explicit MinimalView(Iterator it, Sentinel sent) : it_(base(std::move(it))), sent_(base(std::move(sent))) {} + + MinimalView(MinimalView&&) = default; + MinimalView& operator=(MinimalView&&) = default; + + constexpr Iterator begin() const { return Iterator(it_); } + constexpr Sentinel end() const { return Sentinel(sent_); } + +private: + decltype(base(std::declval<Iterator>())) it_; + decltype(base(std::declval<Sentinel>())) sent_; +}; + +struct NotInvocable {}; + +struct NotAView {}; + +static_assert(!std::ranges::view<NotAView>); + +struct NotAViewRange { + using Iterator = cpp20_input_iterator<int*>; + using Sentinel = sentinel_wrapper<Iterator>; + + NotAViewRange() = default; + constexpr explicit NotAViewRange(int* b, int* e) : begin_(b), end_(e) {} + constexpr NotAViewRange(NotAViewRange const& other) = default; + constexpr NotAViewRange(NotAViewRange&& other) = default; + NotAViewRange& operator=(NotAViewRange const&) = default; + NotAViewRange& operator=(NotAViewRange&&) = default; + + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + + int* begin_; + int* end_; +}; + +static_assert(std::ranges::range<NotAViewRange> && !std::ranges::view<NotAViewRange>); + +template <bool IsNoexcept> +class NoexceptIterMoveInputIterator { + int* it_; + +public: + using iterator_category = std::input_iterator_tag; + using value_type = int; + using difference_type = typename std::iterator_traits<int*>::difference_type; + using pointer = int*; + using reference = int&; + + NoexceptIterMoveInputIterator() = default; + explicit constexpr NoexceptIterMoveInputIterator(int* it) : it_(it) {} + + friend constexpr decltype(auto) iter_move(const NoexceptIterMoveInputIterator& it) noexcept(IsNoexcept) { + return std::ranges::iter_move(it.it_); + } + + friend constexpr int* base(const NoexceptIterMoveInputIterator& i) { return i.it_; } + + constexpr reference operator*() const { return *it_; } + constexpr NoexceptIterMoveInputIterator& operator++() { + ++it_; + return *this; + } + constexpr NoexceptIterMoveInputIterator operator++(int) { + NoexceptIterMoveInputIterator tmp(*this); + ++(*this); + return tmp; + } +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ENUMERATE_TYPES_H diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -804,6 +804,11 @@ "headers": ["ranges"], "unimplemented": True, }, + { + "name": "__cpp_lib_ranges_enumerate", + "values": {"c++23": 202302}, + "headers": ["ranges"], + }, { "name": "__cpp_lib_ranges_iota", "values": {"c++23": 202202},