diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv index 23adfde5bd4d7..7a4c7087dfd11 100644 --- a/libcxx/docs/Status/Cxx26Issues.csv +++ b/libcxx/docs/Status/Cxx26Issues.csv @@ -336,7 +336,7 @@ "`LWG4549 `__","``vprint_nonunicode_buffered`` ignores its stream parameter","2026-03 (Croydon)","","","`#189878 `__","" "`LWG4550 `__","Need new feature test macros for ```` and ````","2026-03 (Croydon)","","","`#189879 `__","" "`LWG4552 `__","``compare_exchange_weak`` writes a value on spurious failure, not memory contents","2026-03 (Croydon)","","","`#189880 `__","" -"`LWG4553 `__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","","","`#189881 `__","" +"`LWG4553 `__","Wording for FR-025-246 25.7.18.2 Add a ``reserve_hint`` function to ``concat_view``","2026-03 (Croydon)","|Complete|","24","`#189881 `__","" "`LWG4554 `__","Remove undefined behaviour from ``hive`` for invalid limits","2026-03 (Croydon)","","","`#189882 `__","" "`LWG4555 `__","Remove ``is_consteval_only``","2026-03 (Croydon)","","","`#189883 `__","" "`LWG4556 `__","Unclear properties of reflection strings","2026-03 (Croydon)","","","`#189884 `__","" diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv index efc98495d1d5c..d98a62cfa3418 100644 --- a/libcxx/docs/Status/Cxx26Papers.csv +++ b/libcxx/docs/Status/Cxx26Papers.csv @@ -112,7 +112,7 @@ "`P3430R3 `__","simd issues: explicit, unsequenced, identity-element position, and members of disabled simd","2025-02 (Hagenberg)","","","`#127881 `__","" "`P2663R7 `__","Interleaved complex values support in ``std::simd``","2025-02 (Hagenberg)","","","`#127882 `__","" "`P2933R4 `__","Extend ```` header function with overloads for ``std::simd``","2025-02 (Hagenberg)","","","`#127883 `__","" -"`P2846R6 `__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","","","`#127884 `__","" +"`P2846R6 `__","``reserve_hint``: Eagerly reserving memory for not-quite-sized lazy ranges","2025-02 (Hagenberg)","|Partial|","24","`#127884 `__","``__cpp_lib_ranges_reserve_hint`` is not set. Container operations that take a range as input do not respect ``reserve_hint``. ``as_const_view``, ``chunk_view``, and ``slide_view`` are not yet supported." "`P3471R4 `__","Standard Library Hardening","2025-02 (Hagenberg)","","","`#127885 `__","" "`P0447R28 `__","Introduction of ``std::hive`` to the standard library","2025-02 (Hagenberg)","","","`#127886 `__","" "`P3019R14 `__","``indirect`` and ``polymorphic``: Vocabulary Types for Composite Class Design","2025-02 (Hagenberg)","","","`#127887 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index b40f586161e62..97ea9fec9dd77 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -756,6 +756,7 @@ set(files __ranges/ref_view.h __ranges/rend.h __ranges/repeat_view.h + __ranges/reserve_hint.h __ranges/reverse_view.h __ranges/single_view.h __ranges/size.h diff --git a/libcxx/include/__ranges/adjacent_transform_view.h b/libcxx/include/__ranges/adjacent_transform_view.h index e0c20b7a95513..a833c0dee4fd4 100644 --- a/libcxx/include/__ranges/adjacent_transform_view.h +++ b/libcxx/include/__ranges/adjacent_transform_view.h @@ -149,6 +149,22 @@ class adjacent_transform_view : public view_interface= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_InnerView> + { + return __inner_.reserve_hint(); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return __inner_.reserve_hint(); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/adjacent_view.h b/libcxx/include/__ranges/adjacent_view.h index ac1366adb4efc..db9edb5cf46f2 100644 --- a/libcxx/include/__ranges/adjacent_view.h +++ b/libcxx/include/__ranges/adjacent_view.h @@ -33,6 +33,7 @@ #include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__tuple/tuple_transform.h> @@ -139,6 +140,30 @@ class adjacent_view : public view_interface> { __sz -= std::min<_CT>(__sz, _Np - 1); return static_cast<_ST>(__sz); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + using _ST = decltype(ranges::reserve_hint(__base_)); + using _CT = common_type_t<_ST, size_t>; + auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_)); + __sz -= std::min<_CT>(__sz, _Np - 1); + return static_cast<_ST>(__sz); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + using _ST = decltype(ranges::reserve_hint(__base_)); + using _CT = common_type_t<_ST, size_t>; + auto __sz = static_cast<_CT>(ranges::reserve_hint(__base_)); + __sz -= std::min<_CT>(__sz, _Np - 1); + return static_cast<_ST>(__sz); + } + +# endif // _LIBCPP_STD_VER >= 26 }; struct __adjacent_view_iter_access { diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h index a553f39998e0e..ddd2f9a75c267 100644 --- a/libcxx/include/__ranges/as_rvalue_view.h +++ b/libcxx/include/__ranges/as_rvalue_view.h @@ -19,6 +19,7 @@ #include <__ranges/concepts.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__utility/forward.h> @@ -99,6 +100,22 @@ class as_rvalue_view : public view_interface> { { return ranges::size(__base_); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + return ranges::reserve_hint(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__base_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/common_view.h b/libcxx/include/__ranges/common_view.h index eec1045c8a758..c232fb533e1db 100644 --- a/libcxx/include/__ranges/common_view.h +++ b/libcxx/include/__ranges/common_view.h @@ -20,6 +20,7 @@ #include <__ranges/concepts.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__utility/forward.h> @@ -101,6 +102,22 @@ class common_view : public view_interface> { { return ranges::size(__base_); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + return ranges::reserve_hint(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__base_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/concat_view.h b/libcxx/include/__ranges/concat_view.h index 3bbe9db12e0f8..7602720576001 100644 --- a/libcxx/include/__ranges/concat_view.h +++ b/libcxx/include/__ranges/concat_view.h @@ -32,6 +32,7 @@ #include <__ranges/concepts.h> #include <__ranges/movable_box.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__tuple/tuple_transform.h> @@ -176,6 +177,28 @@ class concat_view : public view_interface> { [](auto... __sizes) { return (make_unsigned_t>(__sizes) + ...); }, std::__tuple_transform(ranges::size, __views_)); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires(approximately_sized_range<_Views> && ...) + { + return std::apply( + [](auto... __sizes) static { + using _CT = make_unsigned_t>; + return (_CT(__sizes) + ...); + }, + std::__tuple_transform(ranges::reserve_hint, __views_)); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires(approximately_sized_range && ...) + { + return std::apply( + [](auto... __sizes) static { + using _CT = make_unsigned_t>; + return (_CT(__sizes) + ...); + }, + std::__tuple_transform(ranges::reserve_hint, __views_)); + } }; template diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h index e1c855860c50a..99a2acfe922c5 100644 --- a/libcxx/include/__ranges/concepts.h +++ b/libcxx/include/__ranges/concepts.h @@ -25,6 +25,7 @@ #include <__ranges/data.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__type_traits/add_pointer.h> #include <__type_traits/common_reference.h> @@ -80,10 +81,24 @@ using range_rvalue_reference_t = iter_rvalue_reference_t>; template using range_common_reference_t = iter_common_reference_t>; +# if _LIBCPP_STD_VER >= 26 + +// [range.approximately.sized] +template +concept approximately_sized_range = range<_Tp> && requires(_Tp& __t) { ranges::reserve_hint(__t); }; + +// [range.sized] +template +concept sized_range = approximately_sized_range<_Tp> && requires(_Tp& __t) { ranges::size(__t); }; + +# else + // [range.sized] template concept sized_range = range<_Tp> && requires(_Tp& __t) { ranges::size(__t); }; +# endif + template using range_size_t = decltype(ranges::size(std::declval<_Rp&>())); diff --git a/libcxx/include/__ranges/drop_view.h b/libcxx/include/__ranges/drop_view.h index e3754e64d536f..2776df6175797 100644 --- a/libcxx/include/__ranges/drop_view.h +++ b/libcxx/include/__ranges/drop_view.h @@ -32,6 +32,7 @@ #include <__ranges/non_propagating_cache.h> #include <__ranges/range_adaptor.h> #include <__ranges/repeat_view.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/view_interface.h> @@ -139,6 +140,24 @@ class drop_view : public view_interface> { { return __size(*this); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + const auto __s = static_cast>(ranges::reserve_hint(__base_)); + return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + const auto __s = static_cast>(ranges::reserve_hint(__base_)); + return std::__to_unsigned_like(__s < __count_ ? 0 : __s - __count_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/elements_view.h b/libcxx/include/__ranges/elements_view.h index 33c152bbd03b4..3d91f5e4c1d6e 100644 --- a/libcxx/include/__ranges/elements_view.h +++ b/libcxx/include/__ranges/elements_view.h @@ -24,6 +24,7 @@ #include <__ranges/concepts.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__tuple/tuple_element.h> @@ -133,6 +134,22 @@ class elements_view : public view_interface> { return ranges::size(__base_); } +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + return ranges::reserve_hint(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__base_); + } + +# endif // _LIBCPP_STD_VER >= 26 + private: _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); }; diff --git a/libcxx/include/__ranges/enumerate_view.h b/libcxx/include/__ranges/enumerate_view.h index bb7696897af00..8845e6caa1421 100644 --- a/libcxx/include/__ranges/enumerate_view.h +++ b/libcxx/include/__ranges/enumerate_view.h @@ -22,6 +22,7 @@ #include <__ranges/concepts.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__type_traits/maybe_const.h> @@ -115,6 +116,22 @@ class enumerate_view : public view_interface> { return __base_; } [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + return ranges::reserve_hint(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__base_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h index cb6ea660551ae..e30f6da11f907 100644 --- a/libcxx/include/__ranges/owning_view.h +++ b/libcxx/include/__ranges/owning_view.h @@ -18,6 +18,7 @@ #include <__ranges/data.h> #include <__ranges/empty.h> #include <__ranges/enable_borrowed_range.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__type_traits/remove_cvref.h> @@ -99,6 +100,22 @@ class owning_view : public view_interface> { { return ranges::data(__r_); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_Rp> + { + return ranges::reserve_hint(__r_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__r_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(owning_view); diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h index ea7e9cb3b6da9..9d65a536d5a09 100644 --- a/libcxx/include/__ranges/ref_view.h +++ b/libcxx/include/__ranges/ref_view.h @@ -22,6 +22,7 @@ #include <__ranges/data.h> #include <__ranges/empty.h> #include <__ranges/enable_borrowed_range.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__type_traits/is_object.h> @@ -73,6 +74,15 @@ class ref_view : public view_interface> { { return ranges::data(*__range_); } + +# if _LIBCPP_STD_VER >= 26 + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range<_Range> + { + return ranges::reserve_hint(*__range_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/reserve_hint.h b/libcxx/include/__ranges/reserve_hint.h new file mode 100644 index 0000000000000..c523caf19e77c --- /dev/null +++ b/libcxx/include/__ranges/reserve_hint.h @@ -0,0 +1,80 @@ +// -*- 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_RESERVE_HINT_H +#define _LIBCPP___RANGES_RESERVE_HINT_H + +#include <__concepts/class_or_enum.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__ranges/size.h> +#include <__type_traits/remove_cvref.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 26 + +_LIBCPP_BEGIN_NAMESPACE_STD + +// [range.prim.size.hint] + +namespace ranges { +namespace __reserve_hint { +void reserve_hint() = delete; + +template +concept __sized = requires(_Tp&& __t) { ranges::size(__t); }; + +template +concept __member_reserve_hint = !__sized<_Tp> && requires(_Tp&& __t) { + { auto(__t.reserve_hint()) } -> __integer_like; +}; + +template +concept __unqualified_reserve_hint = + !__sized<_Tp> && !__member_reserve_hint<_Tp> && __class_or_enum> && requires(_Tp&& __t) { + { auto(reserve_hint(__t)) } -> __integer_like; + }; + +struct __fn { + // `[range.prim.size.hint]`: `ranges::size(t)` is a valid expression + template <__sized _Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr __integer_like auto + operator()(_Tp&& __t) noexcept(noexcept(ranges::size(__t))) { + return ranges::size(__t); + } + + // `[range.prim.size.hint]`: `auto(t.reserve_hint())` is a valid expression + template <__member_reserve_hint _Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr __integer_like auto + operator()(_Tp&& __t) noexcept(noexcept(auto(__t.reserve_hint()))) { + return auto(__t.reserve_hint()); + } + + // `[range.prim.size.hint]`: `auto(reserve_hint(t))` is a valid expression + template <__unqualified_reserve_hint _Tp> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr __integer_like auto + operator()(_Tp&& __t) noexcept(noexcept(auto(reserve_hint(__t)))) { + return auto(reserve_hint(__t)); + } +}; +} // namespace __reserve_hint + +inline namespace __cpo { +inline constexpr auto reserve_hint = __reserve_hint::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 26 + +#endif // _LIBCPP___RANGES_RESERVE_HINT_H diff --git a/libcxx/include/__ranges/reverse_view.h b/libcxx/include/__ranges/reverse_view.h index b016cc231f2b5..fe8e40b74e1b7 100644 --- a/libcxx/include/__ranges/reverse_view.h +++ b/libcxx/include/__ranges/reverse_view.h @@ -21,6 +21,7 @@ #include <__ranges/enable_borrowed_range.h> #include <__ranges/non_propagating_cache.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/view_interface.h> @@ -111,6 +112,22 @@ class reverse_view : public view_interface> { { return ranges::size(__base_); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + return ranges::reserve_hint(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__base_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/stride_view.h b/libcxx/include/__ranges/stride_view.h index 780bb25743c15..3f1c841cdd047 100644 --- a/libcxx/include/__ranges/stride_view.h +++ b/libcxx/include/__ranges/stride_view.h @@ -30,6 +30,7 @@ #include <__ranges/concepts.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/view_interface.h> #include <__type_traits/make_unsigned.h> @@ -129,6 +130,24 @@ class stride_view : public view_interface> { { return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __stride_)); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + auto __s = static_cast>(ranges::reserve_hint(__base_)); + return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_)); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + auto __s = static_cast>(ranges::reserve_hint(__base_)); + return std::__to_unsigned_like(ranges::__div_ceil(__s, __stride_)); + } + +# endif // _LIBCPP_STD_VER >= 26 }; // class stride_view template diff --git a/libcxx/include/__ranges/take_view.h b/libcxx/include/__ranges/take_view.h index 999f686537f2c..53a800f7f4b44 100644 --- a/libcxx/include/__ranges/take_view.h +++ b/libcxx/include/__ranges/take_view.h @@ -32,11 +32,13 @@ #include <__ranges/iota_view.h> #include <__ranges/range_adaptor.h> #include <__ranges/repeat_view.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/view_interface.h> #include <__type_traits/decay.h> #include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/make_unsigned.h> #include <__type_traits/maybe_const.h> #include <__type_traits/remove_cvref.h> #include <__utility/auto_cast.h> @@ -155,6 +157,26 @@ class take_view : public view_interface> { auto __n = ranges::size(__base_); return ranges::min(__n, static_cast(__count_)); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() { + if constexpr (approximately_sized_range<_View>) { + auto __n = static_cast>(ranges::reserve_hint(__base_)); + return std::__to_unsigned_like(ranges::min(__n, __count_)); + } + return std::__to_unsigned_like(__count_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const { + if constexpr (approximately_sized_range) { + auto __n = static_cast>(ranges::reserve_hint(__base_)); + return std::__to_unsigned_like(ranges::min(__n, __count_)); + } + return std::__to_unsigned_like(__count_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/__ranges/to.h b/libcxx/include/__ranges/to.h index 3a2bc42969f7b..33e12b9cb410c 100644 --- a/libcxx/include/__ranges/to.h +++ b/libcxx/include/__ranges/to.h @@ -23,6 +23,7 @@ #include <__ranges/from_range.h> #include <__ranges/range_adaptor.h> #include <__ranges/ref_view.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/transform_view.h> #include <__type_traits/add_pointer.h> @@ -107,9 +108,15 @@ template else if constexpr (constructible_from<_Container, _Args...> && __container_appendable<_Container, range_reference_t<_Range>>) { _Container __result(std::forward<_Args>(__args)...); +# if _LIBCPP_STD_VER >= 26 + if constexpr (approximately_sized_range<_Range> && __reservable_container<_Container>) { + __result.reserve(static_cast>(ranges::reserve_hint(__range))); + } +# else if constexpr (sized_range<_Range> && __reservable_container<_Container>) { __result.reserve(static_cast>(ranges::size(__range))); } +# endif for (auto&& __ref : __range) { using _Ref = decltype(__ref); diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h index 1484be006e841..be4ae7da77f4e 100644 --- a/libcxx/include/__ranges/transform_view.h +++ b/libcxx/include/__ranges/transform_view.h @@ -30,6 +30,7 @@ #include <__ranges/empty.h> #include <__ranges/movable_box.h> #include <__ranges/range_adaptor.h> +#include <__ranges/reserve_hint.h> #include <__ranges/size.h> #include <__ranges/view_interface.h> #include <__type_traits/conditional.h> @@ -139,6 +140,22 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac { return ranges::size(__base_); } + +# if _LIBCPP_STD_VER >= 26 + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() + requires approximately_sized_range<_View> + { + return ranges::reserve_hint(__base_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto reserve_hint() const + requires approximately_sized_range + { + return ranges::reserve_hint(__base_); + } + +# endif // _LIBCPP_STD_VER >= 26 }; template diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 39b4e0bb986c6..cb6c057d02657 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1949,6 +1949,7 @@ module std { module ref_view { header "__ranges/ref_view.h" } module rend { header "__ranges/rend.h" } module repeat_view { header "__ranges/repeat_view.h" } + module reserve_hint { header "__ranges/reserve_hint.h" } module reverse_view { header "__ranges/reverse_view.h" } module single_view { header "__ranges/single_view.h" } module size { header "__ranges/size.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges index d82a41942327b..451546335df98 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -26,6 +26,7 @@ namespace std::ranges { inline constexpr unspecified size = unspecified; inline constexpr unspecified ssize = unspecified; + inline constexpr unspecified reserve_hint = unspecified; // Since C++26 } // [range.range], ranges @@ -52,6 +53,10 @@ namespace std::ranges { template using range_common_reference_t = iter_common_reference_t>; + // [range.approximately.sized], approximately sized ranges + template + concept approximately_sized_range = ...; + // [range.sized], sized ranges template inline constexpr bool disable_sized_range = false; @@ -526,6 +531,7 @@ namespace std { # if _LIBCPP_STD_VER >= 26 # include <__ranges/concat_view.h> +# include <__ranges/reserve_hint.h> # endif # include diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index 876942bdffbf5..9e87e2653908a 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -25,6 +25,10 @@ export namespace std { using std::ranges::__cpo::empty; using std::ranges::__cpo::size; using std::ranges::__cpo::ssize; + +#if _LIBCPP_STD_VER >= 26 + using std::ranges::__cpo::reserve_hint; +#endif } // namespace __cpo // [range.range], ranges @@ -50,6 +54,11 @@ export namespace std { using std::ranges::disable_sized_range; using std::ranges::sized_range; +#if _LIBCPP_STD_VER >= 26 + // [range.approximately.sized], approximately sized ranges + using std::ranges::approximately_sized_range; +#endif + // [range.view], views using std::ranges::enable_view; using std::ranges::view; diff --git a/libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.pass.cpp new file mode 100644 index 0000000000000..43486898f18d8 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.access/reserve_hint.static_call_operator.compile.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// Test the libc++-specific behavior that the call operator of the +// std::ranges::reserve_hint customization point object is static. + +#include +#include + +using RangeReserveHintT = decltype(std::ranges::reserve_hint); + +extern int bounded_array[42]; + +struct HasSizeMember { + constexpr std::size_t size() { return 42; } +}; + +struct HasSizeFunction { + friend constexpr std::size_t size(HasSizeFunction) { return 42; } +}; + +struct HasReserveHintMember { + constexpr std::size_t reserve_hint() { return 42; } +}; + +struct HasReserveHintFunction { + friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; } +}; + +static_assert(RangeReserveHintT::operator()(bounded_array) == 42); +static_assert(RangeReserveHintT::operator()(HasSizeMember{}) == 42); +static_assert(RangeReserveHintT::operator()(HasSizeFunction{}) == 42); +static_assert(RangeReserveHintT::operator()(HasReserveHintMember{}) == 42); +static_assert(RangeReserveHintT::operator()(HasReserveHintFunction{}) == 42); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp index 88fb7e3c39281..33d93b92b2691 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent.transform/nodiscard.verify.cpp @@ -14,6 +14,8 @@ #include #include +#include "test_macros.h" + struct View : std::ranges::view_interface { int* begin(); const int* begin() const; @@ -53,6 +55,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.adjacent.transform.iterator] auto it = v.begin(); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp index 36b5ade3a4999..9e00cb392b4dd 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.adjacent/nodiscard.verify.cpp @@ -13,6 +13,8 @@ #include #include +#include "test_macros.h" + struct View : std::ranges::view_interface { int* begin(); const int* begin() const; @@ -49,6 +51,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.adjacent.iterator] auto it = v.begin(); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp index eaa739ca87c13..6471309d39268 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.all/nodiscard.verify.cpp @@ -14,6 +14,8 @@ #include #include +#include "test_macros.h" + void test() { std::vector range; @@ -57,6 +59,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} v.data(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} @@ -77,6 +86,11 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} v.size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); +#endif + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} v.data(); } diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp index 93e0d50886010..c46bbbb9c664b 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.as_rvalue/nodiscard.verify.cpp @@ -13,6 +13,7 @@ #include #include +#include "test_macros.h" #include "test_range.h" struct NonSimpleView : std::ranges::view_base { @@ -53,6 +54,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.as.rvalue.overview] // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp index 3542251b7d436..ad199a06736f6 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.common/nodiscard.verify.cpp @@ -13,6 +13,8 @@ #include #include +#include "test_macros.h" + struct NonCommonView : std::ranges::view_base { int* begin() const; const int* end() const; @@ -51,6 +53,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.common.overview] // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp index b81e646d381c4..f9a3e732d8ac1 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.concat/nodiscard.verify.cpp @@ -36,6 +36,11 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(cv).size(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + cv.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(cv).reserve_hint(); + // [range.concat.iterator] auto it = cv.begin(); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp index 2633c3b778c01..179bc1ab22c1c 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.drop/nodiscard.verify.cpp @@ -45,6 +45,13 @@ void test() { v.size(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); + +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif } // [range.drop.overview] diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp index 088c1dba5af9d..ea58695fed140 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.elements/nodiscard.verify.cpp @@ -15,6 +15,8 @@ #include #include +#include "test_macros.h" + struct View : std::ranges::view_interface { std::tuple* begin(); const std::tuple* begin() const; @@ -66,6 +68,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.elements.iterator] auto it = v.begin(); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp index 06b0d6a8c148b..a2c19427f08dd 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.enumerate/nodiscard.verify.cpp @@ -14,6 +14,8 @@ #include #include +#include "test_macros.h" + void test() { std::vector range; std::ranges::enumerate_view ev{range}; @@ -32,6 +34,12 @@ void test() { ev.size(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(ev).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + ev.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(ev).reserve_hint(); +#endif // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} ev.base(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} @@ -78,4 +86,4 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::views::enumerate(range); -} \ No newline at end of file +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp index 6dfa2f5d17c60..612f8a2bec1b8 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.reverse/nodiscard.verify.cpp @@ -14,6 +14,7 @@ #include #include "test_iterators.h" +#include "test_macros.h" void test() { int range[] = {19, 28, 29, 49, 82, 94}; @@ -41,6 +42,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.reverse.overview] // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp index 9c48ecb68e5e3..e4df227ff44c8 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.stride.view/nodiscard.verify.cpp @@ -13,6 +13,8 @@ #include #include +#include "test_macros.h" + void test() { int range[] = {1, 2, 3}; auto sv = std::ranges::stride_view(range, 2); @@ -27,6 +29,14 @@ void test() { sv.end(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} sv.size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + sv.reserve_hint(); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(sv).reserve_hint(); + +#endif // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} sv.stride(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp index 621b20b29e3a1..457b42296543e 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.take/nodiscard.verify.cpp @@ -46,6 +46,13 @@ void test() { v.size(); // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); + +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif } // [range.take.sentinel] diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp index 76ffa16d233f5..76e4d2dc8d84f 100644 --- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp @@ -15,6 +15,7 @@ #include #include "test_iterators.h" +#include "test_macros.h" struct View : std::ranges::view_interface { int* begin(); @@ -51,6 +52,13 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::as_const(v).size(); +#if TEST_STD_VER >= 26 + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + v.reserve_hint(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(v).reserve_hint(); +#endif + // [range.transform.iterator] auto it = v.begin(); 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 71a0b6ebb804c..4a47108dcb152 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 @@ -82,7 +82,7 @@ static_assert(test(std::ranges::size, a)); static_assert(test(std::ranges::ssize, a)); #if TEST_STD_VER >= 26 -// static_assert(test(std::views::reserve_hint, a)); +static_assert(test(std::ranges::reserve_hint, a)); #endif // [range.factories] diff --git a/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..6b6a8523afbdc --- /dev/null +++ b/libcxx/test/std/ranges/range.access/reserve_hint.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::ranges::reserve_hint + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +using RangeReserveHintT = decltype(std::ranges::reserve_hint); + +struct Incomplete; +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +extern int bounded_array[42]; +extern int unbounded_array[]; + +struct SizedSentinelRange { + int data_[42] = {}; + constexpr int* begin() { return data_; } + constexpr auto end() { return sized_sentinel(data_ + 42); } +}; + +struct HasSizeMember { + constexpr std::size_t size() { return 42; } +}; + +struct HasSizeFunction { + friend constexpr std::size_t size(HasSizeFunction) { return 42; } +}; + +struct HasReserveHintMember { + constexpr std::size_t reserve_hint() { return 42; } +}; + +struct HasReserveHintFunction { + friend constexpr std::size_t reserve_hint(HasReserveHintFunction) { return 42; } +}; + +struct HasReserveHintMemberBool { + constexpr bool reserve_hint() { return false; } +}; + +static_assert(!std::is_invocable_v); + +static_assert(std::ranges::reserve_hint(bounded_array) == 42); +ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(bounded_array)), std::size_t); + +static_assert(!std::is_invocable_v); + +bool constexpr test_sized_sentinel_range() { + SizedSentinelRange b; + assert(std::ranges::reserve_hint(b) == 42); + ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(b)), std::size_t); + + return true; +} + +static_assert(std::ranges::reserve_hint(HasSizeMember{}) == 42); +ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeMember{})), std::size_t); + +static_assert(std::ranges::reserve_hint(HasSizeFunction{}) == 42); +ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasSizeFunction{})), std::size_t); + +static_assert(std::ranges::reserve_hint(HasReserveHintMember{}) == 42); +ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintMember{})), std::size_t); + +static_assert(std::ranges::reserve_hint(HasReserveHintFunction{}) == 42); +ASSERT_SAME_TYPE(decltype(std::ranges::reserve_hint(HasReserveHintFunction{})), std::size_t); + +// test that the order of preference is ranges::size, then member reserve_hint, +// then function reserve_hint +struct HasSizeAndReserveHint { + constexpr std::size_t size() { return 42; } + constexpr std::size_t reserve_hint() { return 0; } + friend constexpr std::size_t reserve_hint(HasSizeAndReserveHint) { return 0; } +}; + +struct HasReserveHintMemberAndFunction { + constexpr std::size_t reserve_hint() { return 42; } + friend constexpr std::size_t reserve_hint(HasReserveHintMemberAndFunction) { return 0; } +}; + +static_assert(std::ranges::reserve_hint(HasSizeAndReserveHint{}) == 42); +static_assert(std::ranges::reserve_hint(HasReserveHintMemberAndFunction{}) == 42); + +int main(int, char**) { + test_sized_sentinel_range(); + static_assert(test_sized_sentinel_range()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..5a5ed8c8a6983 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent.transform/reserve_hint.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++26 + +// constexpr auto reserve_hint() +// requires approximately_sized_range; +// constexpr auto reserve_hint() const +// requires approximately_sized_range; + +#include +#include +#include +#include + +#include "helpers.h" +#include "test_iterators.h" +#include "test_macros.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + +struct NonApproximatelySizedView : std::ranges::view_base { + using iterator = forward_iterator; + iterator begin() const; + iterator end() const; +}; + +struct ApproximatelySizedView : std::ranges::view_base { + unsigned int size_; + constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {} + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + size_); } + constexpr unsigned int reserve_hint() const { return size_; } +}; + +struct ApproximatelySizedNotConstView : std::ranges::view_base { + unsigned int size_; + constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {} + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + size_); } + constexpr unsigned int reserve_hint() { return size_; } +}; + +// Test with different values of N for an approximately sized view +template +constexpr void test_approx_sized_view() { + std::ranges::adjacent_transform_view v(ApproximatelySizedView(5), Fn{}); + static_assert(std::ranges::approximately_sized_range); + static_assert(std::ranges::approximately_sized_range); + + auto expected_hint = 5 - (N - 1); + assert(v.reserve_hint() == expected_hint); + assert(std::as_const(v).reserve_hint() == expected_hint); +} + +// Test with different values of N for a non-const approximately sized view +template +constexpr void test_nonconst_approx_sized() { + // non-const-only reserve_hint + std::ranges::adjacent_transform_view v( + ApproximatelySizedNotConstView(5), Fn{}); + static_assert(std::ranges::approximately_sized_range); + static_assert(!std::ranges::approximately_sized_range); + + auto expected_hint = 5 - (N - 1); + assert(v.reserve_hint() == expected_hint); +} + +template +constexpr void test_empty_range() { + std::ranges::adjacent_transform_view v(ApproximatelySizedView(0), Fn{}); + static_assert(std::ranges::approximately_sized_range); + static_assert(std::ranges::approximately_sized_range); + + assert(v.reserve_hint() == 0); + assert(std::as_const(v).reserve_hint() == 0); +} + +template +constexpr void test_N_greater_than_size() { + if constexpr (N > 2) { + std::ranges::adjacent_transform_view v(ApproximatelySizedView(2), Fn{}); + static_assert(std::ranges::approximately_sized_range); + static_assert(std::ranges::approximately_sized_range); + assert(v.reserve_hint() == 0); + assert(std::as_const(v).reserve_hint() == 0); + } +} + +template +constexpr void test() { + test_approx_sized_view(); + test_nonconst_approx_sized(); + test_empty_range(); + test_N_greater_than_size(); +} + +template +constexpr void test() { + test(); + test(); + test(); + test(); +} + +constexpr bool test() { + // non-approximately-sized range has no reserve_hint + static_assert(!std::ranges::approximately_sized_range< + std::ranges::adjacent_transform_view>); + static_assert(!std::ranges::approximately_sized_range< + const std::ranges::adjacent_transform_view>); + + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..0546d9dbdb794 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.adjacent/reserve_hint.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 + +// constexpr auto reserve_hint() +// requires approximately_sized_range; +// constexpr auto reserve_hint() const +// requires approximately_sized_range; + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + +struct NonApproximatelySizedView : std::ranges::view_base { + using iterator = forward_iterator; + iterator begin() const; + iterator end() const; +}; + +struct ApproximatelySizedView : std::ranges::view_base { + unsigned int size_; + constexpr explicit ApproximatelySizedView(unsigned int size) : size_(size) {} + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + size_); } + constexpr unsigned int reserve_hint() const { return size_; } +}; + +struct ApproximatelySizedNotConstView : std::ranges::view_base { + unsigned int size_; + constexpr explicit ApproximatelySizedNotConstView(unsigned int size) : size_(size) {} + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + size_); } + constexpr unsigned int reserve_hint() { return size_; } +}; + +template +constexpr void test() { + { + // Test with different values of N for an approximately sized view + std::ranges::adjacent_view v(ApproximatelySizedView(5)); + static_assert(std::ranges::approximately_sized_range); + static_assert(std::ranges::approximately_sized_range); + + auto expected_hint = 5 - (N - 1); + assert(v.reserve_hint() == expected_hint); + assert(std::as_const(v).reserve_hint() == expected_hint); + } + { + // Test with different values of N for a non-const approximately sized view + std::ranges::adjacent_view v(ApproximatelySizedNotConstView(5)); + static_assert(std::ranges::approximately_sized_range); + static_assert(!std::ranges::approximately_sized_range); + + auto expected_hint = 5 - (N - 1); + assert(v.reserve_hint() == expected_hint); + } + { + // empty range + std::ranges::adjacent_view v(ApproximatelySizedView(0)); + static_assert(std::ranges::approximately_sized_range); + static_assert(std::ranges::approximately_sized_range); + + assert(v.reserve_hint() == 0); + assert(std::as_const(v).reserve_hint() == 0); + } + { + // N greater than range size + if constexpr (N > 2) { + std::ranges::adjacent_view v(ApproximatelySizedView(2)); + static_assert(std::ranges::approximately_sized_range); + static_assert(std::ranges::approximately_sized_range); + assert(v.reserve_hint() == 0); + assert(std::as_const(v).reserve_hint() == 0); + } + } +} + +constexpr bool test() { + // non-approximately-sized range has no reserve_hint + static_assert(!std::ranges::approximately_sized_range>); + static_assert( + !std::ranges::approximately_sized_range>); + + test<1>(); + test<2>(); + test<3>(); + test<5>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..88fd2bdb589db --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.owning.view/reserve_hint.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 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +int globalBuff[8]; + +template +concept HasReserveHint = requires(T t) { t.reserve_hint(); }; + +constexpr bool test() { + { + struct NoReserveHint { + bidirectional_iterator begin(); + bidirectional_iterator end(); + }; + using OwningView = std::ranges::owning_view; + static_assert(!HasReserveHint); + static_assert(!HasReserveHint); + static_assert(!HasReserveHint); + static_assert(!HasReserveHint); + } + { + struct ReserveHintMember { + bidirectional_iterator begin(); + bidirectional_iterator end(); + int reserve_hint() const; + }; + using OwningView = std::ranges::owning_view; + static_assert(!std::ranges::sized_range); + static_assert(std::ranges::approximately_sized_range); + static_assert(!std::ranges::range); // no begin/end + static_assert(HasReserveHint); + static_assert(HasReserveHint); + static_assert(!HasReserveHint); // not a range, therefore no reserve_hint() + static_assert(!HasReserveHint); + } + { + // Test an empty view. + int a[] = {1}; + auto ov = std::ranges::owning_view(std::ranges::subrange(a, a)); + assert(ov.reserve_hint() == 0); + assert(std::as_const(ov).reserve_hint() == 0); + } + { + // Test a non-empty view. + int a[] = {1}; + auto ov = std::ranges::owning_view(std::ranges::subrange(a, a + 1)); + assert(ov.reserve_hint() == 1); + assert(std::as_const(ov).reserve_hint() == 1); + } + { + // Test a non-view. + std::array a = {1, 2}; + auto ov = std::ranges::owning_view(std::move(a)); + assert(ov.reserve_hint() == 2); + assert(std::as_const(ov).reserve_hint() == 2); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp index 88e7179da5496..d527ead60cfc3 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.all/range.ref.view/range.ref.view.pass.cpp @@ -109,6 +109,20 @@ concept DataIsInvocable = requires (std::ranges::ref_view view) { view.data() static_assert(std::same_as())), std::ranges::ref_view>); +#if TEST_STD_VER >= 26 +template +concept ReserveHintIsInvocable = requires(std::ranges::ref_view view) { view.reserve_hint(); }; + +struct ApproximatelySizedRange { + int start_; + unsigned int reserve_hint_; + + constexpr auto begin() const { return forward_iterator(globalBuff + start_); } + constexpr auto end() const { return forward_iterator(globalBuff + 8); } + constexpr unsigned int reserve_hint() const { return reserve_hint_; } +}; +#endif + constexpr bool test() { { // ref_view::base @@ -184,6 +198,21 @@ constexpr bool test() { static_assert(!SizeIsInvocable); } +#if TEST_STD_VER >= 26 + // ref_view::reserve_hint + { + ApproximatelySizedRange range1{2, 5}; + std::ranges::ref_view view1 = range1; + assert(view1.reserve_hint() == 5); + + ApproximatelySizedRange range2{7, 0}; + std::ranges::ref_view view2 = range2; + assert(view2.reserve_hint() == 0); + + static_assert(!ReserveHintIsInvocable); + } +#endif + { // ref_view::data Range range1; diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..8d2f6cb07afbe --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/reserve_hint.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// constexpr auto reserve_hint() +// requires approximately_sized_range; +// constexpr auto reserve_hint() const +// requires approximately_sized_range; + +#include +#include + +#include "test_iterators.h" + +// forward_iterator + sentinel end so that ranges::size doesn't apply, +// making these ranges approximately_sized only via their member reserve_hint(). + +struct ConstReserveHintView : std::ranges::view_base { + bool* hint_called; + constexpr auto begin() const { return forward_iterator(nullptr); } + constexpr auto end() const { return sentinel_wrapper>(forward_iterator(nullptr)); } + + constexpr unsigned int reserve_hint() const { + *hint_called = true; + return 3; + } +}; + +struct NonConstReserveHintView : std::ranges::view_base { + bool* hint_called; + constexpr auto begin() const { return forward_iterator(nullptr); } + constexpr auto end() const { return sentinel_wrapper>(forward_iterator(nullptr)); } + + constexpr unsigned int reserve_hint() { + *hint_called = true; + return 5; + } +}; + +struct NoReserveHintView : std::ranges::view_base { + constexpr auto begin() const { return forward_iterator(nullptr); } + constexpr auto end() const { return sentinel_wrapper>(forward_iterator(nullptr)); } +}; + +template +concept HasReserveHint = requires(T v) { v.reserve_hint(); }; + +static_assert(!std::ranges::sized_range>); +static_assert(!std::ranges::sized_range>); +static_assert(!std::ranges::sized_range>); + +static_assert(HasReserveHint>); +static_assert(HasReserveHint>); +static_assert(HasReserveHint>); +static_assert(!HasReserveHint>); +static_assert(!HasReserveHint>); +static_assert(!HasReserveHint>); + +constexpr bool test() { + { + bool hint_called = false; + std::ranges::as_rvalue_view view(ConstReserveHintView{{}, &hint_called}); + assert(view.reserve_hint() == 3); + assert(hint_called); + } + + { + bool hint_called = false; + std::ranges::as_rvalue_view view(NonConstReserveHintView{{}, &hint_called}); + assert(view.reserve_hint() == 5); + assert(hint_called); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..9833cb5798c41 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/reserve_hint.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto reserve_hint() +// requires approximately_sized_range; +// constexpr auto reserve_hint() const +// requires approximately_sized_range; + +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept ReserveHintEnabled = requires(View v) { v.reserve_hint(); }; + +constexpr bool test() { + int buf[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + static_assert(ReserveHintEnabled&>); + static_assert(ReserveHintEnabled const&>); + static_assert(!ReserveHintEnabled&>); + static_assert(!ReserveHintEnabled const&>); + static_assert(ReserveHintEnabled&>); + static_assert(!ReserveHintEnabled const&>); + } + + { + ApproximatelySizedView view(buf, buf + 8, 5); + std::ranges::common_view common(view); + assert(common.reserve_hint() == 5); + } + + { + ApproximatelySizedView view(buf, buf + 8, 5); + const std::ranges::common_view common(view); + assert(common.reserve_hint() == 5); + } + + { + NonConstApproximatelySizedView view(buf, buf + 8, 5); + std::ranges::common_view common(view); + assert(common.reserve_hint() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h index 56f2f6ad5fcbf..670837aa47744 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.common.view/types.h @@ -12,6 +12,7 @@ #include #include "test_iterators.h" +#include "test_macros.h" struct DefaultConstructibleView : std::ranges::view_base { int* begin_ = nullptr; @@ -123,4 +124,35 @@ static_assert(HasConstAndNonConstBegin>); static_assert(HasOnlyConstBegin>); +#if TEST_STD_VER >= 26 + +struct ApproximatelySizedView : std::ranges::view_base { + int* begin_; + int* end_; + unsigned int hint_; + constexpr explicit ApproximatelySizedView(int* b, int* e, unsigned int hint) : begin_(b), end_(e), hint_(hint) {} + constexpr auto begin() const { return forward_iterator(begin_); } + constexpr auto end() const { return sentinel_wrapper>(forward_iterator(end_)); } + constexpr unsigned int reserve_hint() const { return hint_; } +}; +static_assert(!std::ranges::common_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::approximately_sized_range); + +struct NonConstApproximatelySizedView : std::ranges::view_base { + int* begin_; + int* end_; + unsigned int hint_; + constexpr explicit NonConstApproximatelySizedView(int* b, int* e, unsigned int hint) + : begin_(b), end_(e), hint_(hint) {} + constexpr auto begin() const { return forward_iterator(begin_); } + constexpr auto end() const { return sentinel_wrapper>(forward_iterator(end_)); } + constexpr unsigned int reserve_hint() { return hint_; } +}; +static_assert(!std::ranges::common_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +#endif // TEST_STD_VER >= 26 + #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_COMMON_VIEW_TYPES_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..ede9050823165 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.concat/reserve_hint.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto reserve_hint() +// requires (approximately_sized_range && ...); +// constexpr auto reserve_hint() const +// requires (approximately_sized_range && ...); + +#include +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" + +// All views use forward_iterator as their iterator so that ranges::size doesn't apply, +// making approximately_sized_range depend solely on the reserve_hint() member. + +int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + +struct ApproximatelySizedView : std::ranges::view_base { + unsigned int hint_; + constexpr explicit ApproximatelySizedView(unsigned int hint) : hint_(hint) {} + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + hint_); } + constexpr unsigned int reserve_hint() const { return hint_; } +}; + +struct ApproximatelySizedNotConstView : std::ranges::view_base { + unsigned int hint_; + constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {} + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + hint_); } + constexpr unsigned int reserve_hint() { return hint_; } +}; + +struct IntHintView : std::ranges::view_base { + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + 4); } + constexpr int reserve_hint() const { return 4; } +}; + +struct UnsignedHintView : std::ranges::view_base { + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + 5); } + constexpr unsigned int reserve_hint() const { return 5; } +}; + +struct NoHintView : std::ranges::view_base { + constexpr auto begin() const { return forward_iterator(buffer); } + constexpr auto end() const { return forward_iterator(buffer + 3); } +}; + +constexpr bool test() { + { + // single range + std::ranges::concat_view v(ApproximatelySizedView(8)); + assert(v.reserve_hint() == 8); + assert(std::as_const(v).reserve_hint() == 8); + } + + { + // multiple ranges same type + std::ranges::concat_view v(ApproximatelySizedView(2), ApproximatelySizedView(3)); + assert(v.reserve_hint() == 5); + assert(std::as_const(v).reserve_hint() == 5); + } + + { + // const-view non-approximately-sized range + std::ranges::concat_view v(ApproximatelySizedNotConstView(2), ApproximatelySizedView(3)); + assert(v.reserve_hint() == 5); + static_assert(std::ranges::approximately_sized_range); + static_assert(!std::ranges::approximately_sized_range); + } + + { + // underlying range not approximately-sized + std::ranges::concat_view v(NoHintView{}, ApproximatelySizedView(8)); + static_assert(!std::ranges::approximately_sized_range); + static_assert(!std::ranges::approximately_sized_range); + } + + { + // two ranges with different hint types: common type of int and unsigned int is unsigned int + std::ranges::concat_view v(IntHintView{}, UnsignedHintView{}); + assert(v.reserve_hint() == 9); + ASSERT_SAME_TYPE(decltype(v.reserve_hint()), unsigned int); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..33045591c412d --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.drop/reserve_hint.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include + +#include "test_macros.h" +#include "types.h" + +template +concept ReserveHintInvocable = requires(std::ranges::drop_view t) { t.reserve_hint(); }; + +constexpr bool test() { + // approximately_sized_range + std::ranges::drop_view dropView1(MoveOnlyView(), 4); + assert(dropView1.reserve_hint() == 4); + + // approximately_sized_range + std::ranges::drop_view dropView2(MoveOnlyView(), 0); + assert(dropView2.reserve_hint() == 8); + + // approximately_sized_range + const std::ranges::drop_view dropView3(MoveOnlyView(), 8); + assert(dropView3.reserve_hint() == 0); + + // approximately_sized_range + const std::ranges::drop_view dropView4(MoveOnlyView(), 10); + assert(dropView4.reserve_hint() == 0); + + // mutable-only approximately_sized_range + std::ranges::drop_view dropView5(ApproximatelySizedNotConstView(8), 3); + assert(dropView5.reserve_hint() == 5); + static_assert(ReserveHintInvocable); + static_assert(!ReserveHintInvocable); + + // Because ForwardView is not approximately_sized_range. + static_assert(!ReserveHintInvocable); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h index 73d1e5045ad22..c0bb72a138d8b 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.drop/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.drop/types.h @@ -141,4 +141,18 @@ struct View : std::ranges::view_base { int* end_; }; +#if TEST_STD_VER >= 26 + +struct ApproximatelySizedNotConstView : std::ranges::view_base { + unsigned int hint_; + constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : hint_(hint) {} + constexpr forward_iterator begin() const { return forward_iterator(globalBuff); } + constexpr forward_iterator end() const { return forward_iterator(globalBuff + 8); } + constexpr unsigned int reserve_hint() { return hint_; } +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +#endif // TEST_STD_VER >= 26 + #endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_DROP_TYPES_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..6c888f2ae3827 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.elements/reserve_hint.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include +#include +#include +#include + +#include "types.h" + +template +concept HasReserveHint = requires(T t) { t.reserve_hint(); }; + +struct NonApproximatelySized : std::ranges::view_base { + using iterator = forward_iterator*>; + iterator begin() const; + iterator end() const; +}; +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +static_assert(!HasReserveHint>); +static_assert(!HasReserveHint>); + +struct ApproximatelySizedTupleView : TupleBufferView { + unsigned int hint_; + + template + constexpr ApproximatelySizedTupleView(std::tuple (&buf)[N], unsigned int hint) + : TupleBufferView(buf), hint_(hint) {} + + constexpr auto begin() const { return forward_iterator*>(buffer_); } + constexpr auto end() const { return forward_iterator*>(buffer_ + size_); } + constexpr unsigned int reserve_hint() const { return hint_; } +}; +static_assert(HasReserveHint>); +static_assert(HasReserveHint>); + +struct ApproximatelySizedNotConstTupleView : TupleBufferView { + unsigned int hint_; + + template + constexpr ApproximatelySizedNotConstTupleView(std::tuple (&buf)[N], unsigned int hint) + : TupleBufferView(buf), hint_(hint) {} + + constexpr auto begin() const { return forward_iterator*>(buffer_); } + constexpr auto end() const { return forward_iterator*>(buffer_ + size_); } + constexpr unsigned int reserve_hint() { return hint_; } +}; +static_assert(HasReserveHint>); +static_assert(!HasReserveHint>); + +constexpr bool test() { + std::tuple buffer[] = {{1}, {2}, {3}}; + + // non-const and const are approximately_sized + { + auto ev = std::views::elements<0>(ApproximatelySizedTupleView(buffer, 5)); + assert(ev.reserve_hint() == 5); + assert(std::as_const(ev).reserve_hint() == 5); + } + + { + // mutable-only approximately_sized_range + auto ev = std::views::elements<0>(ApproximatelySizedNotConstTupleView(buffer, 5)); + assert(ev.reserve_hint() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..56f267414f5eb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.enumerate/reserve_hint.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// 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 enumerate_view + +// constexpr auto reserve_hint() +// requires approximately_sized_range; +// constexpr auto reserve_hint() const +// requires approximately_sized_range; + +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept HasMemberReserveHint = requires(T t) { t.reserve_hint(); }; + +static constexpr int globalBuff[8] = {}; + +struct NonApproximatelySizedView : std::ranges::view_base { + using iterator = forward_iterator; + iterator begin() const; + iterator end() const; +}; + +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +static_assert(!HasMemberReserveHint>); +static_assert(!HasMemberReserveHint>); + +struct ApproximatelySizedView : std::ranges::view_base { + unsigned int size_; + constexpr explicit ApproximatelySizedView(unsigned int hint) : size_(hint) {} + constexpr auto begin() const { return forward_iterator(globalBuff); } + constexpr auto end() const { return forward_iterator(globalBuff + 8); } + constexpr unsigned int reserve_hint() const { return size_; } +}; + +static_assert(HasMemberReserveHint>); +static_assert(HasMemberReserveHint>); + +struct ApproximatelySizedNotConstView : std::ranges::view_base { + unsigned int size_; + constexpr explicit ApproximatelySizedNotConstView(unsigned int hint) : size_(hint) {} + constexpr auto begin() const { return forward_iterator(globalBuff); } + constexpr auto end() const { return forward_iterator(globalBuff + 8); } + constexpr unsigned int reserve_hint() { return size_; } +}; + +static_assert(HasMemberReserveHint>); +static_assert(!HasMemberReserveHint>); + +constexpr bool test() { + // Non-const and const are reserve_hint-able + { + auto view = std::views::enumerate(ApproximatelySizedView{5}); + assert(view.reserve_hint() == 5); + assert(std::as_const(view).reserve_hint() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..e241639d0f6ac --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.reverse/reserve_hint.pass.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include +#include +#include + +#include "test_macros.h" +#include "types.h" + +// end - begin = 8, but size may return something else. +template +struct BidirApproxSizedRange : std::ranges::view_base { + int* ptr_; + std::size_t reserve_hint_; + + constexpr BidirApproxSizedRange(int* ptr, std::size_t reserve_hint) : ptr_(ptr), reserve_hint_(reserve_hint) {} + constexpr BidirApproxSizedRange(const BidirApproxSizedRange&) + requires(CC == Copyable) + = default; + constexpr BidirApproxSizedRange(BidirApproxSizedRange&&) + requires(CC == MoveOnly) + = default; + constexpr BidirApproxSizedRange& operator=(const BidirApproxSizedRange&) + requires(CC == Copyable) + = default; + constexpr BidirApproxSizedRange& operator=(BidirApproxSizedRange&&) + requires(CC == MoveOnly) + = default; + + constexpr bidirectional_iterator begin() { return bidirectional_iterator{ptr_}; } + constexpr bidirectional_iterator begin() const { return bidirectional_iterator{ptr_}; } + constexpr bidirectional_iterator end() { return bidirectional_iterator{ptr_ + 8}; } + constexpr bidirectional_iterator end() const { return bidirectional_iterator{ptr_ + 8}; } + + constexpr std::size_t size() const { return reserve_hint_; } +}; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + // Non-common, non-const bidirectional range. + { + auto rev = std::ranges::reverse_view(BidirApproxSizedRange{buffer, 4}); + assert(std::ranges::reserve_hint(rev) == 4); + assert(rev.reserve_hint() == 4); + assert(std::move(rev).reserve_hint() == 4); + + ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t); + ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t); + } + // Non-common, const bidirectional range. + { + const auto rev = std::ranges::reverse_view(BidirApproxSizedRange{buffer, 4}); + assert(std::ranges::reserve_hint(rev) == 4); + assert(rev.reserve_hint() == 4); + assert(std::move(rev).reserve_hint() == 4); + + ASSERT_SAME_TYPE(decltype(rev.reserve_hint()), std::size_t); + ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t); + } + // Non-common, non-const (move only) bidirectional range. + { + auto rev = std::ranges::reverse_view(BidirApproxSizedRange{buffer, 4}); + assert(std::move(rev).reserve_hint() == 4); + + ASSERT_SAME_TYPE(decltype(std::move(rev).reserve_hint()), std::size_t); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..97b4dcfc8b4a4 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/reserve_hint.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include + +#include "types.h" + +// There is no reserve_hint function on a stride view over a view that +// is *not* an approximately sized range +static_assert(!std::ranges::approximately_sized_range>>); +static_assert( + !std::ranges::approximately_sized_range>>>); + +// There is a reserve_hint function on a stride view over a view that +// *is *an approximately sized range +static_assert(std::ranges::approximately_sized_range< + BasicTestView, /*IsSized=*/false, /*IsApproximatelySized=*/true>>); +static_assert(std::ranges::approximately_sized_range, /*IsSized=*/false, /*IsApproximatelySized=*/true>>>); + +using Iota = std::ranges::iota_view; +using ApproximatelySizedView = + BasicTestView, + std::ranges::sentinel_t, + /*IsSized=*/false, + /*IsApproximatelySized=*/true>; + +constexpr ApproximatelySizedView make_approximately_sized(Iota&& iota) { + return ApproximatelySizedView(std::ranges::begin(iota), std::ranges::end(iota)); +} + +constexpr bool test() { + { + // Test with stride as exact multiple of number of elements in view strided over. + auto view = make_approximately_sized(std::views::iota(0, 12)); + auto strided = std::views::stride(view, 3); + static_assert(std::ranges::approximately_sized_range); + assert(strided.reserve_hint() == 4); + } + + { + // Test with stride as inexact multiple of number of elements in view strided over. + auto view = make_approximately_sized(std::views::iota(0, 22)); + static_assert(std::ranges::approximately_sized_range); + auto strided = std::views::stride(view, 3); + static_assert(std::ranges::approximately_sized_range); + assert(strided.size() == 8); + } + + { + // Empty range. + auto view = make_approximately_sized(std::views::iota(0, 0)); + auto strided = view | std::views::stride(3); + assert(strided.reserve_hint() == 0); + } + + { + // Stride larger than range size. + auto view = make_approximately_sized(std::views::iota(0, 3)); + auto strided = view | std::views::stride(10); + assert(strided.reserve_hint() == 1); + } + + { + // Stride equal to range size. + auto view = make_approximately_sized(std::views::iota(0, 3)); + auto strided = view | std::views::stride(5); + assert(strided.reserve_hint() == 1); + } + + { + // Stride of 1. + auto view = make_approximately_sized(std::views::iota(0, 7)); + auto strided = view | std::views::stride(1); + assert(strided.size() == 7); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h index 5a8d6b6ca2f71..49da1473dc4e1 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.stride.view/types.h @@ -172,8 +172,9 @@ struct MaybeView : std::ranges::view_base {}; template Sent = sentinel_wrapper, bool IsSized = false, + bool IsApproximatelySized = false, bool IsView = false, - bool IsCopyable = false > + bool IsCopyable = false> requires((!IsSized) || (IsSized && IterDifferable)) struct BasicTestViewOrRange : MaybeView { Iter begin_{}; @@ -192,6 +193,12 @@ struct BasicTestViewOrRange : MaybeView { return begin_ - end_; } + constexpr auto reserve_hint() const + requires IsApproximatelySized + { + return begin_ - end_; + } + constexpr BasicTestViewOrRange(BasicTestViewOrRange&& other) = default; constexpr BasicTestViewOrRange& operator=(BasicTestViewOrRange&&) = default; @@ -210,12 +217,16 @@ struct BasicTestViewOrRange : MaybeView { = default; }; -template Sent = sentinel_wrapper, bool IsSized = false> +template Sent = sentinel_wrapper, + bool IsSized = false, + bool IsApproximatelySized = false> requires((!IsSized) || (IsSized && IterDifferable)) -using BasicTestView = BasicTestViewOrRange; +using BasicTestView = + BasicTestViewOrRange; template Sent = sentinel_wrapper, bool IsCopyable = true> -using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange; +using MaybeCopyableAlwaysMoveableView = BasicTestViewOrRange; static_assert(std::ranges::view>>); static_assert(std::ranges::view, diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..f82cf4f143a3e --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take/reserve_hint.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++26 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include + +#include "test_iterators.h" +#include "test_macros.h" +#include "test_range.h" +#include "types.h" + +template +concept ReserveHintEnabled = requires(const std::ranges::take_view& tv) { tv.reserve_hint(); }; + +constexpr bool test() { + int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + { + static_assert(ReserveHintEnabled); + } + + { + std::ranges::take_view tv(SimpleViewNonSized{buffer, buffer + 8}, 100); + assert(tv.reserve_hint() == 100); + } + + { + std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 0); + assert(tv.reserve_hint() == 0); + } + + { + const std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 2); + assert(tv.reserve_hint() == 2); + } + + { + std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 4); + assert(tv.reserve_hint() == 4); + } + + { + const std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 6); + assert(tv.reserve_hint() == 6); + } + + { + std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 8); + assert(tv.reserve_hint() == 8); + } + + { + const std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 8); + assert(tv.reserve_hint() == 8); + } + + { + std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 10); + assert(tv.reserve_hint() == 8); + } + + { + const std::ranges::take_view tv(ApproximatelySizedForwardView{buffer}, 10); + assert(tv.reserve_hint() == 8); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take/types.h b/libcxx/test/std/ranges/range.adaptors/range.take/types.h index 7590ce33bffc1..38b2f8f6ce3b8 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.take/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.take/types.h @@ -1,6 +1,7 @@ #ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H #define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_TAKE_TYPES_H +#include #include #include "test_macros.h" @@ -54,6 +55,20 @@ static_assert(std::ranges::view); static_assert(std::ranges::random_access_range); static_assert(std::ranges::sized_range); +#if TEST_STD_VER >= 26 +using ForwardIter = forward_iterator; +struct ApproximatelySizedForwardView : std::ranges::view_base { + int* ptr_; + constexpr explicit ApproximatelySizedForwardView(int* ptr) : ptr_(ptr) {} + constexpr auto begin() const { return ForwardIter(ptr_); } + constexpr auto end() const { return ForwardIter(ptr_ + 8); } + constexpr std::size_t reserve_hint() const { return 8; } +}; +static_assert(std::ranges::view); +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::approximately_sized_range); +#endif + struct View : std::ranges::view_base { constexpr explicit View(int* b, int* e) : begin_(b), end_(e) { } diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp new file mode 100644 index 0000000000000..78ae794f82915 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/reserve_hint.pass.cpp @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 + +// constexpr auto reserve_hint() +// requires approximately_sized_range +// constexpr auto reserve_hint() const +// requires approximately_sized_range + +#include +#include + +#include "types.h" + +template +concept ReserveHintInvocable = requires(T t) { t.reserve_hint(); }; + +constexpr bool test() { + { + std::ranges::transform_view transformView(ApproximatelySizedView{5}, PlusOne{}); + assert(transformView.reserve_hint() == 5); + } + + static_assert(ReserveHintInvocable>); + static_assert(ReserveHintInvocable>); + + static_assert(ReserveHintInvocable>); + static_assert(!ReserveHintInvocable>); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h index e94e7dfcdaeb7..0d94fc02c1f2e 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h @@ -114,6 +114,39 @@ struct SizedSentinelNotConstView : std::ranges::view_base { bool operator==(const ForwardIter &lhs, int* rhs); bool operator==(int* lhs, const ForwardIter &rhs); +#if TEST_STD_VER >= 26 +struct ApproximatelySizedView : std::ranges::view_base { + int start_; + unsigned int reserve_hint_; + + constexpr explicit ApproximatelySizedView(unsigned int hint, int start = 0) : start_(start), reserve_hint_(hint) {} + constexpr auto begin() const { return forward_iterator(globalBuff + start_); } + constexpr auto end() const { return forward_iterator(globalBuff + 8); } + constexpr unsigned int reserve_hint() const { return reserve_hint_; } +}; + +static_assert(std::ranges::view); +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::approximately_sized_range); + +struct ApproximatelySizedNotConstView : std::ranges::view_base { + int start_; + unsigned int reserve_hint_; + + constexpr explicit ApproximatelySizedNotConstView(unsigned int hint, int start = 0) + : start_(start), reserve_hint_(hint) {} + constexpr auto begin() const { return forward_iterator(globalBuff + start_); } + constexpr auto end() const { return forward_iterator(globalBuff + 8); } + constexpr unsigned int reserve_hint() { return reserve_hint_; } +}; + +static_assert(std::ranges::view); +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); +#endif + struct Range { int *begin() const; int *end() const; diff --git a/libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp new file mode 100644 index 0000000000000..f6e59e305740e --- /dev/null +++ b/libcxx/test/std/ranges/range.req/range.approximately.sized/approximately_sized_range.compile.pass.cpp @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// concept approximately_sized_range; + +#include + +#include "test_iterators.h" + +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +struct range_has_reserve_hint { + bidirectional_iterator begin(); + bidirectional_iterator end(); + int reserve_hint(); +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +struct range_has_const_reserve_hint { + bidirectional_iterator begin(); + bidirectional_iterator end(); + int reserve_hint() const; +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +struct const_range_has_reserve_hint { + bidirectional_iterator begin() const; + bidirectional_iterator end() const; + int reserve_hint(); +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::range); +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +struct const_range_has_const_reserve_hint { + bidirectional_iterator begin() const; + bidirectional_iterator end() const; + int reserve_hint() const; +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +struct sized_sentinel_range_is_approximately_sized { + int* begin(); + int* end(); +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +struct const_sized_sentinel_range_is_approximately_sized { + int* begin() const; + int* end() const; +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::approximately_sized_range); + +struct non_range_has_reserve_hint { + int reserve_hint() const; +}; +static_assert(requires(const non_range_has_reserve_hint x) { std::ranges::reserve_hint(x); }); +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); + +struct range_has_unqualified_reserve_hint { + bidirectional_iterator begin() const; + bidirectional_iterator end() const; + + friend int reserve_hint(range_has_unqualified_reserve_hint&); +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +struct range_has_const_unqualified_reserve_hint { + bidirectional_iterator begin() const; + bidirectional_iterator end() const; + + friend int reserve_hint(const range_has_const_unqualified_reserve_hint&); +}; +static_assert(std::ranges::approximately_sized_range); +static_assert(std::ranges::approximately_sized_range); +static_assert(!std::ranges::sized_range); +static_assert(!std::ranges::sized_range); + +struct range_has_bool_reserve_hint { + bidirectional_iterator begin() const; + bidirectional_iterator end() const; + + bool reserve_hint(); +}; +static_assert(!std::ranges::approximately_sized_range); +static_assert(!std::ranges::approximately_sized_range); diff --git a/libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp b/libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp new file mode 100644 index 0000000000000..f9d43cba2ae22 --- /dev/null +++ b/libcxx/test/std/ranges/range.req/range.approximately.sized/subsumption.compile.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// concept approximately_sized_range; + +#include + +// approximately_sized_range subsumes range + +template +consteval bool check_subsumption_1() { + return false; +} + +template +consteval bool check_subsumption_1() { + return true; +} + +static_assert(check_subsumption_1()); + +// sized_range subsumes approximately_sized_range + +template +consteval bool check_subsumption_2() { + return false; +} + +template +consteval bool check_subsumption_2() { + return true; +} + +static_assert(check_subsumption_2()); diff --git a/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp index a983745fd636e..4d36b678acee8 100644 --- a/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp +++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp @@ -156,6 +156,20 @@ struct MaybeSizedRange { static_assert(std::ranges::sized_range>); static_assert(!std::ranges::sized_range>); +#if TEST_STD_VER >= 26 + +struct ApproxSizedRange { + int x = 0; + constexpr forward_iterator begin() { return forward_iterator(&x); } + constexpr forward_iterator end() { return begin(); } + + constexpr std::size_t reserve_hint() const { return 0; } +}; +static_assert(!std::ranges::sized_range); +static_assert(std::ranges::approximately_sized_range); + +#endif + template struct Reservable : Fallback { @@ -275,6 +289,14 @@ constexpr void test_constraints() { assert(result.reserve_called); } +#if TEST_STD_VER >= 26 + { // approximately_sized_range + using C = Reservable<>; + auto result = std::ranges::to(ApproxSizedRange()); + assert(result.reserve_called); + } +#endif + { // !sized_range using C = Reservable<>; auto result = std::ranges::to(MaybeSizedRange()); diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp index 1bfe96b414b7c..16c45a524811e 100644 --- a/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp +++ b/libcxx/test/tools/clang_tidy_checks/robust_against_adl.cpp @@ -20,7 +20,8 @@ AST_MATCHER(clang::CallExpr, isOperator) { return llvm::isa