From f78d23df9caf1738e7c2ead38bd2c63f50b256cd Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Wed, 9 Jul 2025 19:25:52 +0300 Subject: [PATCH] [libc++][ranges] P3137R3: `views::to_input` Implements: https://wg21.link/P3137R3 Closes #127873 https://wg21.link/range.to.input --- libcxx/docs/FeatureTestMacroTable.rst | 2 + libcxx/docs/ReleaseNotes/22.rst | 1 + libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/CMakeLists.txt | 1 + libcxx/include/__ranges/to_input_view.h | 224 +++++++++++++++ libcxx/include/module.modulemap.in | 3 + libcxx/include/ranges | 15 + libcxx/include/version | 2 + libcxx/modules/std/ranges.inc | 10 + .../ranges.version.compile.pass.cpp | 27 ++ .../version.version.compile.pass.cpp | 27 ++ .../cpo.compile.pass.cpp | 2 +- .../range.to_input/adaptor.pass.cpp | 257 ++++++++++++++++++ .../range.to_input/base.pass.cpp | 85 ++++++ .../range.to_input/begin.pass.cpp | 25 ++ .../range.to_input/ctad.compile.pass.cpp | 30 ++ .../range.to_input/ctor.default.pass.cpp | 81 ++++++ .../range.to_input/ctor.view.pass.cpp | 24 ++ .../enable_borrowed_range.compile.pass.cpp | 37 +++ .../range.to_input/end.pass.cpp | 25 ++ .../range.to_input/general.pass.cpp | 67 +++++ .../iterator/ctor.default.pass.cpp | 80 ++++++ .../range.to_input/size.pass.cpp | 109 ++++++++ .../generate_feature_test_macro_components.py | 5 + 24 files changed, 1139 insertions(+), 2 deletions(-) create mode 100644 libcxx/include/__ranges/to_input_view.h create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/adaptor.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/base.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/begin.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/ctad.compile.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.default.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.view.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/enable_borrowed_range.compile.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/end.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/general.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/iterator/ctor.default.pass.cpp create mode 100644 libcxx/test/std/ranges/range.adaptors/range.to_input/size.pass.cpp diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 358889d8dbc37..8251b6ded1e01 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -488,6 +488,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_concat`` *unimplemented* ---------------------------------------------------------- ----------------- + ``__cpp_lib_ranges_to_input`` ``202502L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_ratio`` ``202306L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_rcu`` *unimplemented* diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index e56f0a88db138..346563e4cf44c 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) +- P3137R3: ``views::to_input`` (`Github `__) - P3168R2: Give ``std::optional`` Range Support (`Github `__) Improvements and New Features diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index f873d16808afe..4b70fb56df0c1 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -101,7 +101,7 @@ "`P2900R14 `__","Contracts for C++","2025-02 (Hagenberg)","","","`#127870 `__","" "`P3475R2 `__","Defang and deprecate ``memory_order::consume``","2025-02 (Hagenberg)","","","`#127871 `__","" "`P2786R13 `__","Trivial Relocatability For C++26","2025-02 (Hagenberg)","","","`#127872 `__","" -"`P3137R3 `__","``views::to_input``","2025-02 (Hagenberg)","","","`#127873 `__","" +"`P3137R3 `__","``views::to_input``","2025-02 (Hagenberg)","|Complete|","22","`#127873 `__","" "`P0472R3 `__","Put ``std::monostate`` in ````","2025-02 (Hagenberg)","|Complete|","21","`#127874 `__","" "`P3349R1 `__","Converting contiguous iterators to pointers","2025-02 (Hagenberg)","","","`#127875 `__","" "`P3372R3 `__","constexpr containers and adaptors","2025-02 (Hagenberg)","|In Progress|","","`#127876 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index db918a16e9a61..b7b347fe3ea70 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -734,6 +734,7 @@ set(files __ranges/take_view.h __ranges/take_while_view.h __ranges/to.h + __ranges/to_input_view.h __ranges/transform_view.h __ranges/view_interface.h __ranges/views.h diff --git a/libcxx/include/__ranges/to_input_view.h b/libcxx/include/__ranges/to_input_view.h new file mode 100644 index 0000000000000..3639abb2531f4 --- /dev/null +++ b/libcxx/include/__ranges/to_input_view.h @@ -0,0 +1,224 @@ +// -*- 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_TO_INPUT_VIEW_H +#define _LIBCPP___RANGES_TO_INPUT_VIEW_H + +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/iter_move.h> +#include <__iterator/iter_swap.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> + +#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 >= 26 + +namespace ranges { + +// [range.to.input.view + +template + requires view<_View> +class to_input_view : public view_interface> { + _View __base_ = _View(); // exposition only + + // [range.to.input.iterator], class template to_input_view::iterator + template + class iterator; // exposition only + +public: + _LIBCPP_HIDE_FROM_ABI to_input_view() + requires default_initializable<_View> + = default; + _LIBCPP_HIDE_FROM_ABI constexpr explicit to_input_view(_View __base) : __base_(std::move(__base)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return iterator{ranges::begin(__base_)}; + } + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return iterator{ranges::begin(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + return ranges::end(__base_); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range + { + return ranges::end(__base_); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return ranges::size(__base_); + } + + // TODO: Implement when P2846R6 is available. + // constexpr auto reserve_hint() + // requires approximately_sized_range<_View> + // { + // return ranges::reserve_hint(__base_); + // } + // constexpr auto reserve_hint() const + // requires approximately_sized_range + // { + // return ranges::reserve_hint(__base_); + // } +}; + +template +to_input_view(_Range&&) -> to_input_view>; + +// [range.to.input.iterator] + +template + requires view<_View> +template +class to_input_view<_View>::iterator { + using _Base _LIBCPP_NODEBUG = __maybe_const<_Const, _View>; // exposition only + + iterator_t<_Base> __current_ = iterator_t<_Base>(); // exposition only + + _LIBCPP_HIDE_FROM_ABI constexpr explicit iterator(iterator_t<_Base> __current) + : __current_(std::move(__current)) {} // exposition only + + friend class to_input_view<_View>; + +public: + using difference_type = range_difference_t<_Base>; + using value_type = range_value_t<_Base>; + using iterator_concept = input_iterator_tag; + + _LIBCPP_HIDE_FROM_ABI iterator() + requires default_initializable> + = default; + + _LIBCPP_HIDE_FROM_ABI iterator(iterator&&) = default; + _LIBCPP_HIDE_FROM_ABI iterator& operator=(iterator&&) = default; + + _LIBCPP_HIDE_FROM_ABI constexpr iterator(iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> + : __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); } + _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const { return *__current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr iterator& operator++() { + ++__current_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const iterator& __x, const sentinel_t<_Base>& __y) { + return __x.__current_ == __y; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const sentinel_t<_Base>& __y, const iterator& __x) + requires sized_sentinel_for, iterator_t<_Base>> + { + return __y - __x.__current_; + } + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const iterator& __x, const sentinel_t<_Base>& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + return __x.__current_ - __y; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr range_rvalue_reference_t<_Base> _LIBCPP_HIDE_FROM_ABI + iter_move(const iterator& __i) noexcept(noexcept(ranges::iter_move(__i.__current_))) { + return ranges::iter_move(__i.__current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void + iter_swap(const iterator& __x, + const iterator& __y) noexcept(noexcept(ranges::iter_swap(__x.__current_, __y.__current_))) + requires indirectly_swappable> + { + ranges::iter_swap(__x.__current_, __y.__current_); + } +}; + +template +constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; + +namespace views { +namespace __to_input_view { + +struct __fn : __range_adaptor_closure<__fn> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto + operator()(_Range&& __range) noexcept(noexcept(/**/ to_input_view(std::forward<_Range>(__range)))) + -> decltype(/*--*/ to_input_view(std::forward<_Range>(__range))) { + return /*---------*/ to_input_view(std::forward<_Range>(__range)); + } + + template + requires(!common_range<_Range> && !forward_range<_Range>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto + operator()(_Range&& __range) noexcept(noexcept(views::all(std::forward<_Range>(__range)))) + -> decltype(/*--------------------------*/ views::all(std::forward<_Range>(__range))) { + return /*---------------------------------*/ views::all(std::forward<_Range>(__range)); + } +}; + +} // namespace __to_input_view + +inline namespace __cpo { +inline constexpr auto to_input = __to_input_view::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 26 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_TO_INPUT_VIEW_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 63cf8e847751f..75cb28ff59d81 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1929,6 +1929,9 @@ module std [system] { header "__ranges/to.h" export std.functional.bind_back } + module to_input_view { + header "__ranges/to_input_view.h" + } module transform_view { header "__ranges/transform_view.h" export std.functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 96d7a6b897188..2855377f8165a 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -362,6 +362,17 @@ namespace std::ranges { class chunk_by_view; // C++23 namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23 + + // [range.to.input], to input view + template + requires view + class to_input_view; // C++26 + + template + constexpr bool enable_borrowed_range> = + enable_borrowed_range; // C++26 + + namespace views { inline constexpr unspecified to_input = unspecified; } // C++26 } namespace std { @@ -453,6 +464,10 @@ namespace std { # include <__ranges/zip_view.h> # endif +# if _LIBCPP_STD_VER >= 26 +# include <__ranges/to_input_view.h> +# endif + # include // standard-mandated includes diff --git a/libcxx/include/version b/libcxx/include/version index 16917a3bd9ddd..7c4d0bb37253a 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -211,6 +211,7 @@ __cpp_lib_ranges_repeat 202207L __cpp_lib_ranges_slide 202202L __cpp_lib_ranges_starts_ends_with 202106L __cpp_lib_ranges_to_container 202202L +__cpp_lib_ranges_to_input 202502L __cpp_lib_ranges_zip 202110L __cpp_lib_ratio 202306L __cpp_lib_raw_memory_algorithms 201606L @@ -590,6 +591,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_out_ptr 202311L // # define __cpp_lib_philox_engine 202406L // # define __cpp_lib_ranges_concat 202403L +# define __cpp_lib_ranges_to_input 202502L # define __cpp_lib_ratio 202306L // # define __cpp_lib_rcu 202306L # define __cpp_lib_reference_wrapper 202403L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 7ede42e4f7b0a..fb79dcfd35019 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -351,6 +351,16 @@ export namespace std { using std::ranges::views::cartesian_product; } #endif + +#if _LIBCPP_STD_VER >= 26 + // [range.to.input], to input view + using std::ranges::to_input_view; + + namespace views { + using std::ranges::views::to_input; + } +#endif + } // 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 index df19f03e7dba1..4235bd3ca2383 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 @@ -64,6 +64,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -114,6 +118,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -164,6 +172,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -217,6 +229,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -312,6 +328,10 @@ # error "__cpp_lib_ranges_to_container should have the value 202202L in c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should be defined in c++23" @@ -434,6 +454,13 @@ # error "__cpp_lib_ranges_to_container should have the value 202202L in c++26" # endif +# ifndef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should be defined in c++26" +# endif +# if __cpp_lib_ranges_to_input != 202502L +# error "__cpp_lib_ranges_to_input should have the value 202502L in c++26" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip 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 index cde2f258b7732..c99a826ebb31c 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 @@ -688,6 +688,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -1628,6 +1632,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -2739,6 +2747,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -4123,6 +4135,10 @@ # error "__cpp_lib_ranges_to_container should not be defined before c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # ifdef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should not be defined before c++23" # endif @@ -5726,6 +5742,10 @@ # error "__cpp_lib_ranges_to_container should have the value 202202L in c++23" # endif +# ifdef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should not be defined before c++26" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should be defined in c++23" @@ -7638,6 +7658,13 @@ # error "__cpp_lib_ranges_to_container should have the value 202202L in c++26" # endif +# ifndef __cpp_lib_ranges_to_input +# error "__cpp_lib_ranges_to_input should be defined in c++26" +# endif +# if __cpp_lib_ranges_to_input != 202502L +# error "__cpp_lib_ranges_to_input should have the value 202502L in c++26" +# endif + # if !defined(_LIBCPP_VERSION) # ifndef __cpp_lib_ranges_zip # error "__cpp_lib_ranges_zip should be defined in c++26" 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..dbd16789696fa 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 @@ -134,5 +134,5 @@ static_assert(test(std::views::zip, a, a)); #if TEST_STD_VER >= 26 // static_assert(test(std::views::cache_latest, a)); // static_assert(test(std::views::concat, a, a)); -// static_assert(test(std::views::to_input, a)); +static_assert(test(std::views::to_input, a)); #endif diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/adaptor.pass.cpp new file mode 100644 index 0000000000000..675d561682e5c --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/adaptor.pass.cpp @@ -0,0 +1,257 @@ +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +static_assert(std::is_same_v); + +struct NonView {}; + +static_assert(std::default_initializable); + +struct DefaultInitializableView : std::ranges::view_base { + int i_; + + int* begin(); + int* end(); +}; + +static_assert(std::default_initializable); +static_assert(std::ranges::common_range); +static_assert(std::ranges::input_range); + +struct CommonView : std::ranges::view_base { + int i_; + + constexpr forward_iterator begin() { return forward_iterator(&i_); } + constexpr forward_iterator end() { return begin(); } +}; + +static_assert(std::ranges::common_range); +static_assert(std::ranges::forward_range); +static_assert(std::ranges::input_range); + +struct NonCommonView : std::ranges::view_base { + int i_; + + constexpr forward_iterator begin() { return forward_iterator(&i_); } + constexpr sentinel_wrapper> end() { return sentinel_wrapper>(begin()); } +}; + +static_assert(!std::ranges::common_range); +static_assert(std::ranges::forward_range); +static_assert(std::ranges::input_range); +static_assert( + std::derived_from< typename std::iterator_traits>::iterator_category, + std::input_iterator_tag>); + +// Check that the `to_input` adaptor can be used with a view. + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); + +static_assert(!CanBePiped); +static_assert(CanBePiped); +static_assert(CanBePiped); +static_assert(CanBePiped); + +constexpr bool test() { + { // view | views::to_input + { + DefaultInitializableView view{{}, 94}; + std::same_as> decltype(auto) v = view | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + } + { + CommonView view{{}, 94}; + std::same_as> decltype(auto) v = view | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + } + { + NonCommonView view{{}, 94}; + std::same_as> decltype(auto) v = view | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + } + } + + { // adaptor | views::to_input + { + DefaultInitializableView view{{}, 94}; + const auto partial = std::views::transform(std::identity{}) | std::views::to_input; + std::same_as>> decltype(auto) v = partial(view); + assert(v.base().base().i_ == 94); + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + } + { + CommonView view{{}, 94}; + const auto partial = std::views::transform(std::identity{}) | std::views::to_input; + std::same_as>> decltype(auto) + v = partial(view); + assert(v.base().base().i_ == 94); + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + } + { + NonCommonView view{{}, 94}; + const auto partial = std::views::transform(std::identity{}) | std::views::to_input; + std::same_as< + std::ranges::to_input_view< std::ranges::transform_view>> decltype(auto) v = + partial(view); + assert(v.base().base().i_ == 94); + + static_assert(!std::ranges::common_range); + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::input_range); + } + } + + { // views::to_input | adaptor + { + DefaultInitializableView view{{}, 94}; + const auto partial = std::views::to_input | std::views::transform(std::identity{}); + std::same_as, + std::identity>> decltype(auto) v = partial(view); + assert(v.base().base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + { + CommonView view{{}, 94}; + const auto partial = std::views::to_input | std::views::transform(std::identity{}); + std::same_as, std::identity>> decltype(auto) + v = partial(view); + assert(v.base().base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + { + NonCommonView view{{}, 94}; + const auto partial = std::views::to_input | std::views::transform(std::identity{}); + std::same_as, std::identity>> decltype(auto) + v = partial(view); + assert(v.base().base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + } + + { // views::to_input | views::all + { + DefaultInitializableView view{{}, 94}; + std::same_as> decltype(auto) v = + std::views::all(view) | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + { + CommonView view{{}, 94}; + std::same_as> decltype(auto) v = + std::views::all(view) | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + { + NonCommonView view{{}, 94}; + std::same_as> decltype(auto) v = + std::views::all(view) | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + } + + { // views::to_input | views::all_t + { + DefaultInitializableView view{{}, 94}; + std::same_as> decltype(auto) v = + std::views::all_t(view) | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + { + CommonView view{{}, 94}; + std::same_as> decltype(auto) v = + std::views::all_t(view) | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + { + NonCommonView view{{}, 94}; + std::same_as> decltype(auto) v = + std::views::all_t(view) | std::views::to_input; + assert(v.base().i_ == 94); + + static_assert(std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert(!std::ranges::common_range); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/base.pass.cpp new file mode 100644 index 0000000000000..e66d762ca23c9 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/base.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++26 + +// + +// class to_input_view + +// constexpr V base() const & requires copy_constructible { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include +#include + +#include + +#include "MoveOnly.h" + +struct SimpleView : std::ranges::view_base { + int i_; + + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : public SimpleView { + MoveOnly m_; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + { // & + std::ranges::to_input_view view(SimpleView{{}, 94}); + std::same_as decltype(auto) v = view.base(); + assert(v.i_ == 94); + } + + { // const & + const std::ranges::to_input_view view(SimpleView{{}, 94}); + std::same_as decltype(auto) v = view.base(); + assert(v.i_ == 94); + } + + { // && + std::ranges::to_input_view view(SimpleView{{}, 94}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i_ == 94); + } + + { // const && + const std::ranges::to_input_view view(SimpleView{{}, 94}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i_ == 94); + } + + { // move only + std::ranges::to_input_view view(MoveOnlyView{{}, 94}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.m_.get() == 94); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/begin.pass.cpp new file mode 100644 index 0000000000000..795e442eebfbd --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/begin.pass.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +// constexpr auto begin() requires (!simple-view); +// constexpr auto begin() const requires range; + +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.to_input/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/ctad.compile.pass.cpp new file mode 100644 index 0000000000000..e7ef8418c0bba --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/ctad.compile.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// class to_input_view + +// template +// to_input_view(R&&) -> to_input_view>; + +#include +#include +#include +#include + +static_assert(std::same_as{})), + std::ranges::to_input_view>>>); + +static_assert(std::same_as&>())), + std::ranges::to_input_view&>>>); + +static_assert(std::same_as{})), + std::ranges::to_input_view>>); \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.default.pass.cpp new file mode 100644 index 0000000000000..ef901628beebe --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/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++26 + +// + +// class to_input_view + +// to_input_view() requires default_initializable = default; + +#include +#include +#include + +struct DefaultInitializableView : std::ranges::view_base { + int i_ = 94; + + int* begin(); + int* end(); +}; + +static_assert(std::default_initializable); +static_assert(std::default_initializable>); + +struct NoDefaultInitializableView : std::ranges::view_base { + NoDefaultInitializableView() = delete; + + int* begin(); + int* end(); +}; + +static_assert(!std::default_initializable); +static_assert(!std::default_initializable>); + +struct NoexceptView : std::ranges::view_base { + NoexceptView() noexcept; + + int const* begin() const; + int const* end() const; +}; + +static_assert(noexcept(std::ranges::to_input_view())); + +struct NoNoexceptView : std::ranges::view_base { + NoNoexceptView() noexcept(false); + + int const* begin() const; + int const* end() const; +}; + +static_assert(!noexcept(std::ranges::to_input_view())); + +constexpr bool test() { + { // value-initialized (i.e., whether T() is well-formed). + std::ranges::to_input_view to_input_view{}; + assert(to_input_view.base().i_ == 94); + } + { // direct-list-initialized from an empty initializer list (i.e., whether T{} is well-formed). + std::ranges::to_input_view to_input_view = {}; + assert(to_input_view.base().i_ == 94); + } + { // default-initialized (i.e., whether T t; is well-formed). + std::ranges::to_input_view to_input_view; + assert(to_input_view.base().i_ == 94); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.view.pass.cpp new file mode 100644 index 0000000000000..9a532891b9446 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/ctor.view.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +// constexpr explicit to_input_view(V base); + +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.to_input/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/enable_borrowed_range.compile.pass.cpp new file mode 100644 index 0000000000000..15710b8b57281 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/enable_borrowed_range.compile.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// class to_input_view + +// template +// constexpr bool enable_borrowed_range> = +// enable_borrowed_range; + +#include +#include + +struct BorrowedView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(std::ranges::borrowed_range>); + +struct NonBorrowedView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +static_assert(!std::ranges::borrowed_range>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/end.pass.cpp new file mode 100644 index 0000000000000..98ba9515daa04 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/end.pass.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +// constexpr auto end() requires (!simple-view); +// constexpr auto end() const requires range; + +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.to_input/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/general.pass.cpp new file mode 100644 index 0000000000000..ebfc3e166b214 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/general.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +// Functional tests of std::ranges::to_input_view. + +#include +#include +#include +#include +#include + +template +constexpr bool isEqual(R& r, I i) { + for (auto e : r) + if (e != *i++) + return false; + + return true; +} + +constexpr bool test() { + std::vector vec{"Hello", ",", " ", "World", "!"}; + std::string expectedStr = "Hello, World!"; + + { + auto view = vec | std::views::join; + assert(isEqual(view, expectedStr.begin())); + } + { // Test to_input_view with a vector of strings. + auto view = vec | std::views::to_input | std::views::join; + assert(isEqual(view, expectedStr.begin())); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/iterator/ctor.default.pass.cpp new file mode 100644 index 0000000000000..233607517052e --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/iterator/ctor.default.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// + +// template +// requires view +// template +// class to_input_view::iterator + +// iterator() requires default_initializable> = default; + +#include +#include +#include + +#include "test_iterators.h" + +struct DefaultInitializableIterator { + int i_; // Deliberately uninitialized. + + using difference_type = std::intptr_t; + using value_type = int; + using iterator_category = std::random_access_iterator_tag; + + constexpr int operator*() const { return i_; } + + constexpr DefaultInitializableIterator& operator++() { return *this; } + constexpr void operator++(int) {} + + friend constexpr bool operator==(const DefaultInitializableIterator&, const DefaultInitializableIterator&) = default; +}; + +static_assert(std::default_initializable); + +struct DefaultInitializableIteratorView : std::ranges::view_base { + DefaultInitializableIterator begin() const; + DefaultInitializableIterator end() const; +}; + +struct NonDefaultInitializableIteratorView : std::ranges::view_base { + static_assert(!std::default_initializable>); + + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; +}; + +template +using ToInputViewIteratorT = std::ranges::iterator_t>; + +// Check that the to_input_view's iterator is default initializable when the underlying iterator is. +static_assert(std::default_initializable>); +static_assert(!std::default_initializable>); + +constexpr bool test() { + { + ToInputViewIteratorT it; + assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0. + } + { + ToInputViewIteratorT it = {}; + assert(*it == 0); // DefaultInitializableIterator has to be initialized to have value 0. + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.to_input/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.to_input/size.pass.cpp new file mode 100644 index 0000000000000..40ab3362076bd --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.to_input/size.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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++26 + +// + +// class to_input_view + +// constexpr auto size() requires sized_range; +// constexpr auto size() const requires sized_range; + +#include +#include +#include +#include + +#include "test_iterators.h" + +template +concept HasSize = requires(T t) { t.size(); }; + +constexpr bool test() { + { + struct SubtractableIteratorsView : std::ranges::view_base { + forward_iterator begin(); + sized_sentinel> end(); + }; + + using ToInputViewT = std::ranges::to_input_view; + + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::range); // no begin()/end() + + static_assert(HasSize); + static_assert(HasSize); + static_assert(!HasSize); + static_assert(!HasSize); + } + { + struct NonSizedView : std::ranges::view_base { + bidirectional_iterator begin(); + bidirectional_iterator end(); + }; + + using ToInputViewT = std::ranges::to_input_view; + + static_assert(!HasSize); + static_assert(!HasSize); + static_assert(!HasSize); + static_assert(!HasSize); + } + { + struct SizedView : std::ranges::view_base { + bidirectional_iterator begin(); + bidirectional_iterator end(); + + int size() const; + }; + + using ToInputViewT = std::ranges::to_input_view; + + static_assert(std::ranges::sized_range); + static_assert(!std::ranges::range); // no begin()/end() + + static_assert(HasSize); + static_assert(HasSize); + static_assert(!HasSize); // not a view, therefore no size() + static_assert(!HasSize); + } + { + // Test an empty view. + int arr[] = {94}; + auto view = std::ranges::to_input_view(std::ranges::subrange(arr, arr)); + + assert(view.size() == 0); + assert(std::as_const(view).size() == 0); + } + { + // Test a non-empty view. + int arr[] = {94}; + auto view = std::ranges::to_input_view(std::ranges::subrange(arr, arr + 1)); + + assert(view.size() == 1); + assert(std::as_const(view).size() == 1); + } + { + // Test a non-view. + std::array arr = {94, 82}; + auto view = std::ranges::to_input_view(std::move(arr)); + + assert(view.size() == 2); + assert(std::as_const(view).size() == 2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index c1e579c775746..5176c86628c05 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1144,6 +1144,11 @@ def add_version_header(tc): "values": {"c++23": 202202}, "headers": ["ranges"], }, + { + "name": "__cpp_lib_ranges_to_input", + "values": {"c++26": 202502}, + "headers": ["ranges"], + }, { "name": "__cpp_lib_ranges_zip", "values": {"c++23": 202110},