diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 358889d8dbc37..9071845bf8294 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -376,6 +376,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_contains`` ``202207L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_ranges_enumerate`` ``202302L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_find_last`` ``202207L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_iota`` ``202202L`` diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index e56f0a88db138..41f1fda315e3a 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -40,6 +40,7 @@ Implemented Papers - P2321R2: ``zip`` (`Github `__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release) +- P2164R9: ``views::enumerate`` (`Github `__) - P3168R2: Give ``std::optional`` Range Support (`Github `__) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 3a87e64339e1f..04f6605a9fa3b 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -104,7 +104,7 @@ "","","","","","" "`P0290R4 `__","``apply()`` for ``synchronized_value``","2023-02 (Issaquah)","","","`#105249 `__","" "`P2770R0 `__","Stashing stashing ``iterators`` for proper flattening","2023-02 (Issaquah)","|Complete|","21","`#105250 `__","" -"`P2164R9 `__","``views::enumerate``","2023-02 (Issaquah)","","","`#105251 `__","" +"`P2164R9 `__","``views::enumerate``","2023-02 (Issaquah)","|Complete|","22","`#105251 `__","" "`P2711R1 `__","Making multi-param constructors of ``views`` ``explicit``","2023-02 (Issaquah)","|Complete|","21","`#105252 `__","" "`P2609R3 `__","Relaxing Ranges Just A Smidge","2023-02 (Issaquah)","|Complete|","20","`#105253 `__","Implemented as a DR in C++20. Other implementations will do the same." "`P2713R1 `__","Escaping improvements in ``std::format``","2023-02 (Issaquah)","|Complete|","19","`#105254 `__","" diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index aaf561982e120..fb9dc2795baad 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -8,8 +8,8 @@ "`LWG3903 `__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 `__","" "`LWG3904 `__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 `__","" "`LWG3905 `__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 `__","" -"`LWG3912 `__","``enumerate_view::iterator::operator-`` should be ``noexcept``","2023-06 (Varna)","","","`#105279 `__","" -"`LWG3914 `__","Inconsistent template-head of ``ranges::enumerate_view``","2023-06 (Varna)","","","`#105280 `__","" +"`LWG3912 `__","``enumerate_view::iterator::operator-`` should be ``noexcept``","2023-06 (Varna)","|Complete|","22","`#105279 `__","" +"`LWG3914 `__","Inconsistent template-head of ``ranges::enumerate_view``","2023-06 (Varna)","|Complete|","22","`#105280 `__","" "`LWG3915 `__","Redundant paragraph about expression variations","2023-06 (Varna)","","","`#105281 `__","" "`LWG3925 `__","Concept ``formattable``'s definition is incorrect","2023-06 (Varna)","|Complete|","17","`#105282 `__","" "`LWG3927 `__","Unclear preconditions for ``operator[]`` for sequence containers","2023-06 (Varna)","|Nothing To Do|","","`#105283 `__","" @@ -41,7 +41,7 @@ "`LWG4001 `__","``iota_view`` should provide ``empty``","2023-11 (Kona)","|Complete|","19","`#105311 `__","" "","","","","","" "`LWG3767 `__","``codecvt`` incorrectly added to locale","2024-03 (Tokyo)","","","`#105313 `__","" -"`LWG3919 `__","``enumerate_view`` may invoke UB for sized common non-forward underlying ranges","2024-03 (Tokyo)","","","`#105315 `__","" +"`LWG3919 `__","``enumerate_view`` may invoke UB for sized common non-forward underlying ranges","2024-03 (Tokyo)","|Complete|","22","`#105315 `__","" "`LWG3950 `__","``std::basic_string_view`` comparison operators are overspecified","2024-03 (Tokyo)","|Complete|","18","`#105316 `__","" "`LWG3975 `__","Specializations of ``basic_format_context`` should not be permitted","2024-03 (Tokyo)","|Nothing To Do|","","`#105317 `__","" "`LWG3984 `__","``ranges::to``'s recursion branch may be ill-formed","2024-03 (Tokyo)","|Complete|","19","`#105318 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index db918a16e9a61..4bba8274c805e 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -711,6 +711,7 @@ set(files __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/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h new file mode 100644 index 0000000000000..4f538c11720cf --- /dev/null +++ b/libcxx/include/__ranges/enumerate_view.h @@ -0,0 +1,350 @@ +// -*- 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/constructible.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 + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { + +// [concept.object] + +template +concept __range_with_movable_references = + input_range<_Rp> && std::move_constructible> && + std::move_constructible>; + +// [range.enumerate.view] + +template + requires __range_with_movable_references<_View> +class enumerate_view : public view_interface> { + _View __base_ = _View(); + + // [range.enumerate.iterator] + template + class __iterator; + + // [range.enumerate.sentinel] + template + class __sentinel; + +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)) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator(ranges::begin(__base_), 0); + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires __range_with_movable_references + { + return __iterator(ranges::begin(__base_), 0); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (forward_range<_View> && common_range<_View> && sized_range<_View>) + return __iterator(ranges::end(__base_), ranges::distance(__base_)); + else + return __sentinel(ranges::end(__base_)); + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires __range_with_movable_references + { + if constexpr (forward_range<_View> && common_range && sized_range) + return __iterator(ranges::end(__base_), ranges::distance(__base_)); + else + return __sentinel(ranges::end(__base_)); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return ranges::size(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } +}; + +template +enumerate_view(_Range&&) -> enumerate_view>; + +// [range.enumerate.iterator] + +template + requires __range_with_movable_references<_View> +template +class enumerate_view<_View>::__iterator { + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + + 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>; + +private: + using __reference_type _LIBCPP_NODEBUG = tuple>; + + iterator_t<_Base> __current_ = iterator_t<_Base>(); + difference_type __pos_ = 0; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(iterator_t<_Base> __current, difference_type __pos) + : __current_(std::move(__current)), __pos_(__pos) {} + +public: + _LIBCPP_HIDE_FROM_ABI __iterator() + requires default_initializable> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> + : __current_(std::move(__i.__current_)), __pos_(__i.__pos_) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr difference_type index() const noexcept { return __pos_; } + + [[nodiscard]] _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; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const + requires random_access_range<_Base> + { + return __reference_type(__pos_ + __n, __current_[__n]); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __iterator& __x, const __iterator& __y) noexcept { + return __x.__pos_ == __y.__pos_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering + operator<=>(const __iterator& __x, const __iterator& __y) noexcept { + return __x.__pos_ <=> __y.__pos_; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i) + requires random_access_range<_Base> + { + return __i + __n; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type + operator-(const __iterator& __x, const __iterator& __y) noexcept { + return __x.__pos_ - __y.__pos_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto iter_move(const __iterator& __i) noexcept( + noexcept(ranges::iter_move(__i.__current_)) && is_nothrow_move_constructible_v>) { + return tuple>(__i.__pos_, ranges::iter_move(__i.__current_)); + } +}; + +// [range.enumerate.sentinel] + +template + requires __range_with_movable_references<_View> +template +class enumerate_view<_View>::__sentinel { + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; + + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end) : __end_(std::move(__end)) {} + + friend class enumerate_view<_View>; + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __other) + requires _Const && convertible_to, sentinel_t<_Base>> + : __end_(std::move(__other.__end_)) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; } + + template + requires sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__current_ == __y.__end_; + } + + template + requires sized_sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__current_ - __y.__end_; + } + + template + requires sized_sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>> + operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) { + return __x.__end_ - __y.__current_; + } +}; + +template +constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; + +namespace views { +namespace __enumerate { + +struct __fn : __range_adaptor_closure<__fn> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto + operator()(_Range&& __range) noexcept(noexcept(/**/ enumerate_view>(__range))) + -> decltype(/*-------------------------------*/ enumerate_view>(__range)) { + return /*--------------------------------------*/ enumerate_view>(__range); + } +}; + +} // 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 + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_ENUMERATE_VIEW_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 63cf8e847751f..d9f4ad1b09889 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1882,6 +1882,7 @@ module std [system] { module empty_view { header "__ranges/empty_view.h" } module enable_borrowed_range { header "__ranges/enable_borrowed_range.h" } module enable_view { header "__ranges/enable_view.h" } + module enumerate_view { header "__ranges/enumerate_view.h" } module filter_view { header "__ranges/filter_view.h" export std.functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 96d7a6b897188..4b188c3cb90dc 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -328,6 +328,17 @@ namespace std::ranges { namespace views { template inline constexpr unspecified istream = unspecified; } + // [range.enumerate], enumerate view + template + requires see below + class enumerate_view; // since C++23 + + template + constexpr bool enable_borrowed_range> = // since C++23 + enable_borrowed_range; + + namespace views { inline constexpr unspecified enumerate = unspecified; } // since C++23 + // [range.zip], zip view template requires (view && ...) && (sizeof...(Views) > 0) @@ -445,6 +456,7 @@ namespace std { # if _LIBCPP_STD_VER >= 23 # include <__ranges/as_rvalue_view.h> # include <__ranges/chunk_by_view.h> +# include <__ranges/enumerate_view.h> # include <__ranges/from_range.h> # include <__ranges/join_with_view.h> # include <__ranges/repeat_view.h> diff --git a/libcxx/include/version b/libcxx/include/version index 16917a3bd9ddd..8235dda403d7a 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -204,6 +204,7 @@ __cpp_lib_ranges_chunk 202202L __cpp_lib_ranges_chunk_by 202202L __cpp_lib_ranges_concat 202403L __cpp_lib_ranges_contains 202207L +__cpp_lib_ranges_enumerate 202302L __cpp_lib_ranges_find_last 202207L __cpp_lib_ranges_iota 202202L __cpp_lib_ranges_join_with 202202L @@ -521,6 +522,7 @@ __cpp_lib_void_t 201411L // # define __cpp_lib_ranges_chunk 202202L # define __cpp_lib_ranges_chunk_by 202202L # define __cpp_lib_ranges_contains 202207L +# define __cpp_lib_ranges_enumerate 202302L # define __cpp_lib_ranges_find_last 202207L # define __cpp_lib_ranges_iota 202202L # define __cpp_lib_ranges_join_with 202202L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 7ede42e4f7b0a..d1f53d0e95710 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -335,9 +335,8 @@ export namespace std { namespace views { using std::ranges::views::chunk_by; } -#endif // _LIBCPP_STD_VER >= 23 -#if 0 +# if 0 // [range.stride], stride view using std::ranges::stride_view; @@ -350,7 +349,15 @@ export namespace std { namespace views { using std::ranges::views::cartesian_product; } -#endif +# endif + + // [range.enumerate] + using std::ranges::enumerate_view; + + namespace views { + using std::ranges::views::enumerate; + } +#endif // _LIBCPP_STD_VER >= 23 } // namespace ranges namespace views = ranges::views; diff --git a/libcxx/test/libcxx/diagnostics/ranges.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/ranges.nodiscard.verify.cpp index 03b4df735edb5..dda852ac1b40c 100644 --- a/libcxx/test/libcxx/diagnostics/ranges.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/ranges.nodiscard.verify.cpp @@ -10,39 +10,89 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 -// clang-format off - #include #include +#include #include #include "test_macros.h" +// clang-format off + void test() { std::vector range; std::ranges::less_equal pred; - std::views::drop(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#if TEST_STD_VER >= 23 + auto rvalue_view = std::views::as_rvalue(range); + std::views::as_rvalue(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::as_rvalue(rvalue_view); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::split(range, 3); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::split(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk_by(range, pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#endif // TEST_STD_VER >= 23 - std::views::take(range, 3); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::take(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::drop(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} #if TEST_STD_VER >= 23 std::views::drop(std::views::repeat(1)); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::repeat(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + // std::ranges::enumerate + { + std::views::enumerate(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + // class enumerate_view<> + auto ev = std::ranges::subrange(range.begin(), range.end() - 1) | std::views::enumerate; + ev.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(ev).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + ev.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(ev).end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + ev.size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(ev).size(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + ev.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(ev).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + // class enumerate_view<>::__iterator + auto it = ev.begin(); + auto const_it = std::as_const(ev).begin(); + it.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(it).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(it).index(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + *std::as_const(it); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + it[2]; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + it == it; // expected-warning {{equality comparison result unused}} + it <=> it; // expected-warning {{three-way comparison result unused}} + it + 1; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + it - 1; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + it - it; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + iter_move(it); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + // class enumerate_view<>::__sentinel + auto st = ev.end(); + auto const_st = std::as_const(ev).end(); + st.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + it == st; // expected-warning {{equality comparison result unused}} + it == const_st; // expected-warning {{equality comparison result unused}} + const_it == st; // expected-warning {{equality comparison result unused}} + const_it == const_st; // expected-warning {{equality comparison result unused}} + st == it; // expected-warning {{equality comparison result unused}} + st == const_it; // expected-warning {{equality comparison result unused}} + const_st == it; // expected-warning {{equality comparison result unused}} + const_st == const_it; // expected-warning {{equality comparison result unused}} + it - st; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + st - it; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + } + std::views::repeat(1, std::unreachable_sentinel); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#endif // TEST_STD_VER >= 23 - auto rvalue_view = std::views::as_rvalue(range); - std::views::as_rvalue(range); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::as_rvalue(rvalue_view); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::split(range, 3); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::split(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} - std::views::chunk_by(range, pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::take(range, 3); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::take(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +#if TEST_STD_VER >= 23 std::views::take(std::views::repeat(3), 3); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} std::views::take(std::views::repeat(3, std::unreachable_sentinel), 3); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index df19f03e7dba1..08ca82a92f5d6 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -48,6 +48,10 @@ # error "__cpp_lib_ranges_concat should not be defined before c++26" # 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 @@ -98,6 +102,10 @@ # error "__cpp_lib_ranges_concat should not be defined before c++26" # 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 @@ -148,6 +156,10 @@ # error "__cpp_lib_ranges_concat should not be defined before c++26" # 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 @@ -201,6 +213,10 @@ # error "__cpp_lib_ranges_concat should not be defined before c++26" # 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 @@ -278,6 +294,13 @@ # error "__cpp_lib_ranges_concat should not be defined before c++26" # 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 + # ifndef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should be defined in c++23" # endif @@ -400,6 +423,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 + # ifndef __cpp_lib_ranges_join_with # error "__cpp_lib_ranges_join_with should be defined in c++26" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index cde2f258b7732..b77c3901dd8b0 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -660,6 +660,10 @@ # error "__cpp_lib_ranges_contains 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_find_last # error "__cpp_lib_ranges_find_last should not be defined before c++23" # endif @@ -1600,6 +1604,10 @@ # error "__cpp_lib_ranges_contains 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_find_last # error "__cpp_lib_ranges_find_last should not be defined before c++23" # endif @@ -2711,6 +2719,10 @@ # error "__cpp_lib_ranges_contains 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_find_last # error "__cpp_lib_ranges_find_last should not be defined before c++23" # endif @@ -4095,6 +4107,10 @@ # error "__cpp_lib_ranges_contains 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_find_last # error "__cpp_lib_ranges_find_last should not be defined before c++23" # endif @@ -5671,6 +5687,13 @@ # error "__cpp_lib_ranges_contains should have the value 202207L in c++23" # 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 + # ifndef __cpp_lib_ranges_find_last # error "__cpp_lib_ranges_find_last should be defined in c++23" # endif @@ -7583,6 +7606,13 @@ # error "__cpp_lib_ranges_contains should have the value 202207L in c++26" # 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 + # ifndef __cpp_lib_ranges_find_last # error "__cpp_lib_ranges_find_last should be defined in c++26" # endif diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp index 49497875dcf95..5ee03b3b51c80 100644 --- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp @@ -124,7 +124,7 @@ static_assert(test(std::views::as_rvalue, a)); // static_assert(test(std::views::cartesian_product, a, a, a)); static_assert(test(std::views::chunk_by, a, [](int x, int y) { return x < y; })); // static_assert(test(std::views::chunk, a, 1)); -// static_assert(test(std::views::enumerate, a)); +static_assert(test(std::views::enumerate, a)); static_assert(test(std::views::join_with, 1)); // static_assert(test(std::views::stride, a, 1)); static_assert(test(std::views::zip_transform, [](int x, int y) { return x + y; }, a, a)); diff --git a/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp b/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp index 5efd6c72a13db..9070fddae7238 100644 --- a/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp +++ b/libcxx/test/std/ranges/iterator_robust_against_adl.compile.pass.cpp @@ -75,4 +75,5 @@ static_assert(!CanFindADLFunc= 23 static_assert(!CanFindADLFunc>); +static_assert(!CanFindADLFunc>); #endif 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 index 0000000000000..5aa3138ebfd07 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/adaptor.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// std::views::enumerate; + +#include +#include +#include +#include + +#include "types.h" + +// Concepts + +template +concept CanBePiped = requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +template +concept CanEnumerate = requires(Range&& range) { std::views::enumerate(std::forward(range)); }; + +// Helpers + +struct ImmovableReference { + ImmovableReference(ImmovableReference&&) = delete; + + operator int(); +}; + +struct IteratorWithImmovableReferences { + using value_type = int; + using difference_type = std::ptrdiff_t; + + ImmovableReference operator*() const; + IteratorWithImmovableReferences& operator++(); + void operator++(int); + bool operator==(std::default_sentinel_t) const; +}; + +static_assert(std::input_iterator); + +using NonEnumeratableRangeWithImmmovabaleReferences = + std::ranges::subrange; + +static_assert(std::ranges::input_range); +static_assert(!CanEnumerate); +static_assert(!std::move_constructible>); +static_assert( + !std::move_constructible>); + +template +using ExpectedViewElement = std::tuple::difference_type, T>; + +// Helpers + +template +constexpr void compareViews(View v, std::initializer_list> list) { + assert(std::ranges::equal(v, list)); +} + +// Test SFINAE friendliness + +static_assert(CanBePiped); + +static_assert(CanEnumerate); + +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_same_v); + +constexpr bool test() { + // Test `views::enumerate_view(v)` + { + int buff[] = {0, 1, 2, 3}; + + using Result = std::ranges::enumerate_view; + RangeView const range(buff, buff + 4); + + std::same_as 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::same_as 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; + RangeView const range(buff, buff + 4); + + std::same_as 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::same_as 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 index 0000000000000..c75c15fdee0a0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/base.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// constexpr V base() const & requires copy_constructible; +// constexpr V base() &&; + +#include +#include + +#include "MoveOnly.h" + +#include "types.h" + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +// SFINAE + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +struct MoveOnlyView : RangeView { + MoveOnly mo; +}; + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + // Check the const& overload + { + int buff[] = {0, 1, 2, 3}; + + RangeView range(buff, buff + 4); + + std::ranges::enumerate_view view{range}; + std::same_as decltype(auto) result = view.base(); + assert(result.wasCopyInitialized); + assert(range.begin() == result.begin()); + assert(range.end() == result.end()); + } + { + int buff[] = {0, 1, 2, 3}; + + RangeView const range(buff, buff + 4); + + std::ranges::enumerate_view const view{range}; + std::same_as 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}; + + RangeView const range(buff, buff + 4); + + std::ranges::enumerate_view view{range}; + std::same_as 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 index 0000000000000..28f3f7e9e598f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/begin.pass.cpp @@ -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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// constexpr auto begin() requires (!simple-view); +// constexpr auto begin() const requires range-with-movable-references; + +#include +#include +#include + +#include "test_iterators.h" + +#include "types.h" + +// Types + +template +struct CommonView : std::ranges::view_base { + constexpr std::tuple* begin() + requires(!Simple) + { + return nullptr; + } + constexpr const std::tuple* begin() const { return nullptr; } + constexpr std::tuple* end() + requires(!Simple) + { + return nullptr; + } + constexpr const std::tuple* end() const { return nullptr; } +}; +using SimpleCommonView = CommonView; +using NonSimpleCommonView = CommonView; + +struct NoConstBeginView : std::ranges::view_base { + constexpr std::tuple* begin() { return nullptr; } + constexpr std::tuple* end() { return nullptr; } +}; + +// SFINAE + +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 vs. iterator + requires(T t, const T ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +// simple-view +static_assert(HasOnlyConstBegin>); + +// !simple-view && range +static_assert(HasConstAndNonConstBegin>); + +// !range +static_assert(HasOnlyNonConstBegin>); + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Check the return type of begin() + { + RangeView range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + using Iterator = std::ranges::iterator_t; + static_assert(std::same_as); + // static_assert(std::same_as, decltype(*view.begin())>); + } + + // begin() over an empty range + { + RangeView 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 + { + RangeView range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + auto it = view.begin(); + assert(base(it.base()) == buff); + } + + // begin() over an N-element range + { + RangeView 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 index 0000000000000..c86de0fbe8bef --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctad.compile.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// template +// enumerate_view(R&&) -> enumerate_view>; + +#include +#include + +#include "test_iterators.h" + +#include "types.h" + +constexpr bool test() { + { + MinimalDefaultConstructedView jv; + std::ranges::enumerate_view view(jv); + static_assert(std::is_same_v>); + } + + // 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>>); + } + + 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 index 0000000000000..73cfbaabf19c3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.base.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// constexpr explicit enumerate_view(V base); + +#include + +#include +#include +#include +#include + +#include "types.h" + +constexpr bool test() { + using EnumerateView = std::ranges::enumerate_view; + + { + std::array base = {0, 1, 2, 3, 84}; + + RangeView range(base.begin(), base.end()); + EnumerateView view{range}; + + auto baseIt = base.begin(); + auto viewIt = view.begin(); + for (std::size_t index = 0; index != base.size(); ++index) { + auto [vi, vv] = *viewIt; + assert(std::cmp_equal(index, vi)); + assert(*baseIt == vv); + + ++baseIt; + ++viewIt; + } + } + + 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 index 0000000000000..4770335a255be --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/ctor.default.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// constexpr enumerate_view() requires default_initializable; + +#include + +#include +#include +#include + +constexpr int buff[] = {0, 1}; + +template +struct DefaultConstructibleView : std::ranges::view_base { + constexpr DefaultConstructibleView() + requires DefaultConstructible + : 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_; +}; + +static_assert(std::is_default_constructible_v>>); +static_assert(!std::is_default_constructible_v>>); + +constexpr bool test() { + using EnumerateView = std::ranges::enumerate_view>; + + { + EnumerateView view; + + assert((*view.begin() == std::tuple::difference_type, int>{0, 0})); + assert((*view.end() == std::tuple::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::difference_type, int>{0, 0})); + assert((*view.end() == std::tuple::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 index 0000000000000..54ae0fdcfdf8c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/enable_borrowed_range.compile.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// template +// constexpr bool enable_borrowed_range>; + +#include +#include + +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 = true; + +static_assert(!std::ranges::borrowed_range>); +static_assert(std::ranges::borrowed_range>); 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 index 0000000000000..0c30696b6ac09 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/end.pass.cpp @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// constexpr auto end() requires (!simple-view); +// constexpr auto end() const requires range-with-movable-references; + +#include +#include +#include + +#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() + { + RangeView range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + using Iterator = std::ranges::iterator_t; + static_assert(std::same_as); + using Sentinel = std::ranges::sentinel_t; + static_assert(std::same_as); + } + + // Check the return type of .end() const + { + RangeView range(buff, buff + 1); + + const std::ranges::enumerate_view view(range); + using Iterator = std::ranges::iterator_t; + static_assert(std::same_as); + using Sentinel = std::ranges::sentinel_t; + static_assert(std::same_as); + } + + // end() over an empty range + { + RangeView range(buff, buff); + + std::ranges::enumerate_view view(range); + + auto it = view.end(); + assert(base(it.base()) == buff); + assert(it == view.end()); + + auto constIt = std::as_const(view).end(); + assert(base(constIt.base()) == buff); + assert(constIt == std::as_const(view).end()); + } + + // end() const over an empty range + { + RangeView range(buff, buff); + + const std::ranges::enumerate_view view(range); + + auto it = view.end(); + assert(base(it.base()) == buff); + assert(it == view.end()); + + auto constIt = std::as_const(view).end(); + assert(base(constIt.base()) == buff); + assert(constIt == std::as_const(view).end()); + } + + // end() over an 1-element range + { + RangeView range(buff, buff + 1); + + std::ranges::enumerate_view view(range); + + auto it = view.end(); + assert(base(it.base()) == buff + 1); + + auto constIt = std::as_const(view).end(); + assert(base(constIt.base()) == buff + 1); + } + + // end() over an N-element range + { + RangeView range(buff, buff + 8); + + std::ranges::enumerate_view view(range); + + auto it = view.end(); + assert(base(it.base()) == buff + 8); + + auto constIt = std::as_const(view).end(); + assert(base(constIt.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 index 0000000000000..eb6edd1d9d85e --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/arithmetic.pass.cpp @@ -0,0 +1,173 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// constexpr iterator& operator+=(difference_type x) +// requires random_access_range; +// constexpr iterator& operator-=(difference_type x) +// 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); + +#include + +#include "test_iterators.h" + +// Concepts + +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 EnumerateIter = std::ranges::iterator_t>; + +using RandomAccessRange = std::ranges::subrange; + +// SFINAE. + +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, EnumerateIter>); +static_assert(CanMinusEqual, int>); + +using BidirectionalRange = 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(!CanMinusEqual, int>); + +constexpr void test_with_common_range() { + int ts[] = {90, 1, 2, 84}; + + RandomAccessRange r{ts, ts + 4}; + 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 - 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() + 1; + auto it2 = ev.end() - 1; + assert((it1 - it2) == -2); + } +} + +constexpr void test_with_noncommon_range() { + int ts[] = {90, 1, 2, 84}; + + RandomAccessRange r{ts, ts + 4}; + auto it = std::counted_iterator{r.begin(), std::ssize(r)}; + auto sr = std::ranges::subrange{it, std::default_sentinel}; + auto ev = sr | std::views::enumerate; + + // operator+(x, n), operator+(n,x) and operator+= + { + auto it1 = ev.begin(); + + auto it2 = it1 + 3; + assert(*it2.base() == 84); + + auto it3 = 3 + it1; + assert(*it3.base() == 84); + + it1 += 3; + assert(it1 == it2); + assert(*it1.base() == 84); + } + + // operator-(x, n) and operator-= + { + auto it1 = ev.begin(); + + auto it2 = it1 + 3; + assert(*(it2 - 1).base() == 2); + + it2 -= 3; + assert(it1 == it2); + assert(*it2.base() == 90); + } + + // operator-(x, y) + { + assert((ev.end() - ev.begin()) == 4); + } +} + +constexpr bool test() { + test_with_common_range(); + test_with_noncommon_range(); + + 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 index 0000000000000..b50b1309c5715 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/base.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// constexpr const iterator_t& base() const & noexcept; +// constexpr iterator_t base() &&; + +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "../types.h" + +template +constexpr void testBase() { + using Sentinel = sentinel_wrapper; + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateIterator = std::ranges::iterator_t; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))}; + + return EnumerateView(std::move(view)); + }; + + std::array 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 decltype(auto) result = it.base(); + ASSERT_NOEXCEPT(it.base()); + assert(base(result) == std::to_address(base(array.begin()))); + } + + // Test the && version + { + EnumerateIterator it = view.begin(); + std::same_as decltype(auto) result = std::move(it).base(); + assert(base(result) == std::to_address(base(array.begin()))); + } +} + +constexpr bool test() { + testBase>(); + testBase>(); + testBase>(); + testBase>(); + testBase>(); + testBase>(); + testBase(); + testBase(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + 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 index 0000000000000..aaae01fdc8de9 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/compare.three_way.pass.cpp @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// friend constexpr strong_ordering operator<=>(const iterator& x, const iterator& y) noexcept; + +#include +#include + +#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)); +} + +constexpr bool test() { + int buff[] = {0, 1, 2, 3}; + { + using View = std::ranges::enumerate_view; + + using Iterator = std::ranges::iterator_t; + static_assert(std::three_way_comparable); + using Subrange = std::ranges::subrange; + static_assert(std::three_way_comparable>); + using EnumerateView = std::ranges::enumerate_view; + static_assert(std::three_way_comparable>); + + RangeView const range(buff, buff + 4); + + std::same_as decltype(auto) ev = std::views::enumerate(range); + + 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; + static_assert(!std::three_way_comparable); + using Subrange = std::ranges::subrange; + static_assert(!std::three_way_comparable>); + using EnumerateView = std::ranges::enumerate_view; + static_assert(std::three_way_comparable>); + + auto ev = Subrange{Iterator{buff}, Iterator{buff + 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 index 0000000000000..ca56547aff666 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.convert.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// constexpr iterator(iterator i) +// requires Const && convertible_to, iterator_t>; + +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template > +constexpr void test() { + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateIterator = std::ranges::iterator_t; + using EnumerateConstIterator = std::ranges::iterator_t; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))}; + + return EnumerateView(std::move(view)); + }; + + static_assert(std::is_convertible_v); + + std::array array{0, 84, 2, 3, 4}; + auto view = make_enumerate_view(array.begin(), array.end()); + { + std::same_as decltype(auto) it = view.begin(); + std::same_as decltype(auto) itResult = it.base(); + assert(base(base(itResult)) == std::to_address(base(array.begin()))); + + auto [index, value] = *(++it); + assert(index == 1); + assert(value == 84); + } + { + std::same_as decltype(auto) it = view.begin(); + std::same_as decltype(auto) itResult = it.base(); + assert(base(base(itResult)) == std::to_address(base(array.begin()))); + + auto [index, value] = *(++it); + assert(index == 1); + assert(value == 84); + } +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + 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 index 0000000000000..a0296b7857eea --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/ctor.default.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// iterator() requires default_initializable>; + +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template +constexpr void test_default_constructible() { + using View = MinimalView>; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateIterator = std::ranges::iterator_t; + + EnumerateIterator it1; + EnumerateIterator it2{}; + + assert(it1 == it2); + + static_assert(noexcept(EnumerateIterator()) == IsNoexcept); +} + +template +constexpr void test_not_default_constructible() { + // Make sure the iterator is *not* default constructible when the underlying iterator isn't. + using Sentinel = sentinel_wrapper; + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateIterator = std::ranges::iterator_t; + + static_assert(!std::is_default_constructible_v); +} + +constexpr bool tests() { + // clang-format off + test_not_default_constructible>(); + test_not_default_constructible>(); + test_default_constructible, /* noexcept */ false>(); + test_default_constructible, /* noexcept */ false>(); + test_default_constructible, /* noexcept */ false>(); + test_default_constructible, /* noexcept */ false>(); + test_default_constructible(); + // 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 index 0000000000000..0e2f167788408 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/deref.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// constexpr auto operator*() const; + +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +#include "../types.h" + +template > +constexpr void test() { + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateIterator = std::ranges::iterator_t; + + using Result = std::tuple>>; + + std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + View mv{Iterator(std::to_address(base(array.begin()))), Sentinel(Iterator(std::to_address(base(array.end()))))}; + EnumerateView ev{std::move(mv)}; + + { + auto it = ev.begin(); + for (std::size_t index = 0; index < array.size(); ++index) { + std::same_as decltype(auto) result = *it; + + auto [resultIndex, resultValue] = result; + assert(std::cmp_equal(index, resultIndex)); + assert(array[index] == resultValue); + + ++it; + } + + assert(it == ev.end()); + } + + // const + { + auto constIt = std::as_const(ev).begin(); + for (std::size_t index = 0; index < array.size(); ++index) { + std::same_as decltype(auto) result = *constIt; + + auto [resultIndex, resultValue] = result; + assert(std::cmp_equal(index, resultIndex)); + assert(array[index] == resultValue); + + ++constIt; + } + + assert(constIt == ev.end()); + } +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + test, int const>(); + test, int const>(); + test, int const>(); + test, int const>(); + test, int const>(); + test, int const>(); + test(); + + 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 index 0000000000000..51b64f0f6ffb3 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/equal.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept; + +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +constexpr bool test() { + int buff[] = {0, 1, 2, 3, 5}; + { + using View = std::ranges::enumerate_view; + RangeView const range(buff, buff + 5); + + std::same_as decltype(auto) ev = std::views::enumerate(range); + + auto it1 = ev.begin(); + auto it2 = it1 + 5; + + assert(it1 == it1); + ASSERT_NOEXCEPT(it1 == it1); + assert(it1 != it2); + ASSERT_NOEXCEPT(it1 != it2); + assert(it2 != it1); + ASSERT_NOEXCEPT(it2 != it1); + assert(it2 == ev.end()); + assert(ev.end() == it2); + + for (std::size_t index = 0; index != 5; ++index) { + ++it1; + } + + assert(it1 == it2); + assert(it2 == it1); + } + + 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 index 0000000000000..0b2cb1b3b5b91 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/index.pass.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// constexpr difference_type index() const noexcept; + +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +#include "../types.h" + +template > +constexpr void test() { + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + + std::array array{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + View mv{Iterator(std::to_address(base(array.begin()))), Sentinel(Iterator(std::to_address(base(array.end()))))}; + EnumerateView ev(std::move(mv)); + + { + auto it = ev.begin(); + ASSERT_NOEXCEPT(it.index()); + + static_assert(std::same_as); + for (std::size_t index = 0; index < array.size(); ++index) { + assert(std::cmp_equal(index, it.index())); + + ++it; + } + + assert(it == ev.end()); + } + + // const + { + auto constIt = std::as_const(ev).begin(); + ASSERT_NOEXCEPT(constIt.index()); + + static_assert(std::same_as); + for (std::size_t index = 0; index < array.size(); ++index) { + assert(std::cmp_equal(index, constIt.index())); + + ++constIt; + } + + assert(constIt == ev.end()); + } +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + test, int const>(); + test, int const>(); + test, int const>(); + test, int const>(); + test, int const>(); + test, int const>(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + 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 index 0000000000000..4d2b12e54283a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/iter_move.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// friend constexpr auto iter_move(const iterator& i) +// noexcept(noexcept(ranges::iter_move(i.current_)) && +// is_nothrow_move_constructible_v>); + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template +constexpr void test() { + using Sentinel = sentinel_wrapper; + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateIterator = std::ranges::iterator_t; + + std::array array{0, 1, 2, 3, 4}; + + View mv{Iterator(std::to_address(base(array.begin()))), Sentinel(Iterator(std::to_address(base(array.end()))))}; + EnumerateView ev{std::move(mv)}; + EnumerateIterator const it = ev.begin(); + + auto&& result = iter_move(it); + + static_assert(std::is_same_v::difference_type, int&&>&&>); + static_assert(std::is_same_v&&>); + + assert(get<0>(result) == 0); + assert(&get<1>(result) == std::to_address(base(array.begin()))); + + static_assert(noexcept(iter_move(it)) == HasNoexceptIterMove); +} + +constexpr bool tests() { + // clang-format off + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test, /* noexcept */ false>(); + test(); + test, /* noexcept */ true>(); + test, /* 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 index 0000000000000..0bbdd53021e19 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/subscript.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// constexpr auto operator[](difference_type n) const +// requires random_access_range; + +#include +#include +#include +#include + +#include "test_iterators.h" + +template +concept HasSubscriptOperator = requires(T t, U u) { t[u]; }; + +template +using EnumerateIterator = std::ranges::iterator_t>; + +using Subrange = std::ranges::subrange; +static_assert(HasSubscriptOperator, int>); + +using BidirectionalRange = std::ranges::subrange>; +static_assert(!HasSubscriptOperator, int>); + +constexpr bool test() { + // Reference + { + std::array ts = {90, 1, 2, 84}; + auto view = ts | std::views::enumerate; + auto it = view.begin(); + + using DifferenceT = std::iter_difference_t>; + static_assert(std::is_same_v>); + + assert((it[0] == std::tuple(0, 90))); + assert((it[1] == std::tuple(1, 1))); + assert((it[2] == std::tuple(2, 2))); + assert((it[3] == std::tuple(3, 84))); + } + + // Value + { + auto view = std::views::iota(0, 4) | std::views::enumerate; + auto it = view.begin(); + + using DifferenceT = std::iter_difference_t>; + static_assert(std::is_same_v>); + + assert((it[0] == std::tuple(0, 0))); + assert((it[1] == std::tuple(1, 1))); + assert((it[2] == std::tuple(2, 2))); + assert((it[3] == std::tuple(3, 3))); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp new file mode 100644 index 0000000000000..87b519c624d83 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/iterator/types.compile.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::iterator + +// std::enumerate_view::::difference_type; +// std::enumerate_view::::value_type; +// std::enumerate_view::::iterator_category; +// std::enumerate_view::::iterator_concept; + +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template +concept HasIteratorCategory = requires { typename T::iterator_category; }; + +template +using EnumerateViewFor = std::ranges::enumerate_view< MinimalView>>; + +template +using EnumerateIteratorFor = std::ranges::iterator_t>; + +struct ForwardIteratorWithInputCategory { + using difference_type = int; + using value_type = int; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + ForwardIteratorWithInputCategory(); + ForwardIteratorWithInputCategory& operator++(); + ForwardIteratorWithInputCategory operator++(int); + int& operator*() const; + friend bool operator==(ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory); +}; +static_assert(std::forward_iterator); + +constexpr void test() { + // Check iterator_concept for various categories of ranges + { + static_assert( + std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert( + std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, + std::forward_iterator_tag>); + static_assert( + std::is_same_v>::iterator_concept, std::forward_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, + std::bidirectional_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, + std::random_access_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, + std::random_access_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, std::random_access_iterator_tag>); + } + + // Check iterator_category for various categories of ranges + { + static_assert(HasIteratorCategory>>); + static_assert(HasIteratorCategory>>); + static_assert(std::is_same_v::iterator_category, + std::input_iterator_tag>); + static_assert( + std::is_same_v>::iterator_category, std::input_iterator_tag>); + static_assert( + std::is_same_v>::iterator_category, std::input_iterator_tag>); + static_assert( + std::is_same_v>::iterator_category, std::input_iterator_tag>); + static_assert( + std::is_same_v>::iterator_category, std::input_iterator_tag>); + static_assert(std::is_same_v::iterator_category, std::input_iterator_tag>); + } +} 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 index 0000000000000..56b7e881d0991 --- /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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::sentinel + +// constexpr sentinel_t base() const; + +#include + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template > +constexpr void test() { + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateSentinel = std::ranges::sentinel_t; + + std::array array{0, 1, 2, 3, 84}; + + View mv{Iterator(std::to_address(base(array.begin()))), Sentinel(Iterator(std::to_address(base(array.end()))))}; + EnumerateView ev{std::move(mv)}; + + EnumerateSentinel const s = ev.end(); + std::same_as decltype(auto) result = s.base(); + assert(base(base(result)) == std::to_address(base(array.end()))); +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + 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 index 0000000000000..b3bee42685a9a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.convert.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::sentinel + +// constexpr sentinel(sentinel other) +// requires Const && convertible_to, sentinel_t>; + +#include + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template > +constexpr void test() { + using View = MinimalView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateSentinel = std::ranges::sentinel_t; + using EnumerateConstSentinel = std::ranges::sentinel_t; + + auto make_enumerate_view = [](auto begin, auto end) { + View view{Iterator(std::to_address(base(begin))), Sentinel(Iterator(std::to_address(base(end))))}; + + return EnumerateView(std::move(view)); + }; + + static_assert(std::is_convertible_v); + + std::array array{0, 1, 2, 3, 84}; + auto view = make_enumerate_view(array.begin(), array.end()); + + std::same_as decltype(auto) s = view.end(); + std::same_as decltype(auto) sResult = s.base(); + assert(base(base(sResult)) == std::to_address(base(array.end()))); + + // Test assignment + EnumerateConstSentinel cs = s; + std::same_as decltype(auto) csResult = cs.base(); + assert(base(base(csResult)) == std::to_address(base(array.end()))); +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + 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 index 0000000000000..177dba4970d95 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/ctor.default.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::sentinel + +// sentinel() = default; + +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +struct PODSentinel { + int i; // deliberately uninitialised + + friend constexpr bool operator==(std::tuple*, const PODSentinel&) { return true; } +}; + +template +struct PODSentinelView : MinimalView { + std::tuple* begin() const; + PODSentinel end(); +}; + +template +constexpr void test() { + using Sentinel = sentinel_wrapper; + using View = PODSentinelView; + using EnumerateView = std::ranges::enumerate_view; + using EnumerateSentinel = std::ranges::sentinel_t; + + { + EnumerateSentinel s; + + assert(s.base().i == 0); + } + + { + EnumerateSentinel s = {}; + + assert(s.base().i == 0); + } + + static_assert(noexcept(EnumerateSentinel())); +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + 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 index 0000000000000..b66377c3068e8 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/equal.pass.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::sentinel + +// template +// requires sentinel_for, iterator_t>> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include + +#include +#include +#include +#include + +#include "test_iterators.h" + +#include "../types.h" + +template > +constexpr void test() { + using View = MinimalView; + + std::array array{0, 1, 2, 3, 84}; + + View mv{Iterator(std::to_address(base(array.begin()))), Sentinel(Iterator(std::to_address(base(array.end()))))}; + std::ranges::enumerate_view view(std::move(mv)); + + auto const it = view.begin(); + auto const s = view.end(); + + std::same_as decltype(auto) eqItSResult = (it == s); + assert(!eqItSResult); + std::same_as decltype(auto) eqSItResult = (s == it); + assert(!eqSItResult); + + std::same_as decltype(auto) neqItSResult = (it != s); + assert(neqItSResult); + std::same_as decltype(auto) neqSItResult = (s != it); + assert(neqSItResult); +} + +constexpr bool tests() { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test(); + + 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 index 0000000000000..336f64ec40511 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/sentinel/minus.pass.cpp @@ -0,0 +1,265 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// class enumerate_view + +// class enumerate_view::sentinel + +// template +// requires sized_sentinel_for, iterator_t>> +// friend constexpr range_difference_t> +// operator-(const iterator& x, const sentinel& y); + +// template +// requires sized_sentinel_for, iterator_t>> +// friend constexpr range_difference_t> +// operator-(const sentinel& x, const iterator& y); + +#include +#include +#include + +#include "test_iterators.h" + +#include "../test_concepts.h" +// #include "../types_iterators.h" +#include "../types.h" + +template +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 +struct Sent { + int* end_; + + constexpr bool operator==(const Iter& i) const { return i.it_ == end_; } +}; + +template +struct SizedSent { + int* end_; + + constexpr bool operator==(const Iter& i) const { return i.it_ == end_; } + + friend constexpr auto operator-(const SizedSent& st, const Iter& it) { return st.end_ - it.it_; } + + friend constexpr auto operator-(const Iter& it, const SizedSent& st) { return it.it_ - st.end_; } +}; + +template +struct CrossSizedSent { + int* end_; + + template + constexpr bool operator==(const Iter& i) const { + return i.it_ == end_; + } + + template + friend constexpr auto operator-(const CrossSizedSent& st, const Iter& it) { + return st.end_ - it.it_; + } + + template + friend constexpr auto operator-(const Iter& it, const CrossSizedSent& st) { + return it.it_ - st.end_; + } +}; + +template