[libcxx-commits] [libcxx] c3648f3 - [libc++][ranges] Implement `ranges::to`.
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jul 20 22:48:36 PDT 2023
Author: varconst
Date: 2023-07-20T22:48:18-07:00
New Revision: c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3
URL: https://github.com/llvm/llvm-project/commit/c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3
DIFF: https://github.com/llvm/llvm-project/commit/c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3.diff
LOG: [libc++][ranges] Implement `ranges::to`.
Differential Revision: https://reviews.llvm.org/D142335
Added:
libcxx/include/__ranges/to.h
libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.internal_constraints.verify.cpp
libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.nodiscard.verify.cpp
libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.static_assert.verify.cpp
libcxx/test/std/ranges/range.utility/range.utility.conv/container.h
libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
libcxx/test/std/ranges/range.utility/range.utility.conv/to_deduction.pass.cpp
libcxx/test/std/ranges/range.utility/range.utility.conv/to_std_containers.pass.cpp
Modified:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/ReleaseNotes/17.rst
libcxx/docs/Status/Cxx23Issues.csv
libcxx/docs/Status/Cxx23Papers.csv
libcxx/docs/Status/RangesMajorFeatures.csv
libcxx/include/CMakeLists.txt
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/include/version
libcxx/modules/std/ranges.cppm
libcxx/test/std/language.support/support.limits/support.limits.general/deque.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/forward_list.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/list.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/queue.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/stack.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/vector.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
libcxx/utils/generate_feature_test_macro_components.py
Removed:
################################################################################
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 79c14e08b40fff..1fdc27b8b97da0 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -358,7 +358,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_starts_ends_with`` *unimplemented*
--------------------------------------------------- -----------------
- ``__cpp_lib_ranges_to_container`` *unimplemented*
+ ``__cpp_lib_ranges_to_container`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_zip`` *unimplemented*
--------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/17.rst b/libcxx/docs/ReleaseNotes/17.rst
index a21e428c156a67..2bb18c705b0a82 100644
--- a/libcxx/docs/ReleaseNotes/17.rst
+++ b/libcxx/docs/ReleaseNotes/17.rst
@@ -55,6 +55,7 @@ Work has started on the C++17 Parallel STL. This feature is experimental, see
Implemented Papers
------------------
+- P1206R7 - ``ranges::to``: A function to convert any range to a container
- P2520R0 - ``move_iterator<T*>`` should be a random access iterator
- P1328R1 - ``constexpr type_info::operator==()``
- P1413R3 - Formatting ``thread::id`` (the ``stacktrace`` is not done yet)
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0324c0f4ced422..0cc06674bda390 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -258,7 +258,7 @@
"`3820 <https://wg21.link/LWG3820>`__","``cartesian_product_view::iterator::prev`` is not quite right","February 2023","","","|ranges|"
"`3825 <https://wg21.link/LWG3825>`__","Missing compile-time argument ``id`` check in ``basic_format_parse_context::next_arg_id``","February 2023","|Complete|","17.0","|format|"
"`3204 <https://wg21.link/LWG3204>`__","``sub_match::swap`` only swaps the base class","February 2023","|Complete|","17.0",""
-"`3733 <https://wg21.link/LWG3733>`__","``ranges::to`` misuses ``cpp17-input-iterator``","February 2023","","","|ranges|"
+"`3733 <https://wg21.link/LWG3733>`__","``ranges::to`` misuses ``cpp17-input-iterator``","February 2023","|Complete|","17.0","|ranges|"
"`3742 <https://wg21.link/LWG3742>`__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|"
"`3790 <https://wg21.link/LWG3790>`__","`P1467 <https://wg21.link/P1467>`__ accidentally changed ``nexttoward``'s signature","February 2023","","",""
"`3819 <https://wg21.link/LWG3819>`__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","",""
@@ -292,7 +292,7 @@
"`3833 <https://wg21.link/LWG3833>`__","Remove specialization ``template<size_t N> struct formatter<const charT[N], charT>``","February 2023","|Complete|","17.0","|format|"
"`3836 <https://wg21.link/LWG3836>`__","``std::expected<bool, E1>`` conversion constructor ``expected(const expected<U, G>&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","","",""
"`3843 <https://wg21.link/LWG3843>`__","``std::expected<T,E>::value() &`` assumes ``E`` is copy constructible","February 2023","|Complete|","17.0",""
-"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","","","|ranges|"
+"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","|Complete|","17.0","|ranges|"
"`3862 <https://wg21.link/LWG3862>`__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","February 2023","","",""
"`3865 <https://wg21.link/LWG3865>`__","Sorting a range of ``pairs``","February 2023","|Complete|","17.0","|ranges|"
"`3869 <https://wg21.link/LWG3869>`__","Deprecate ``std::errc`` constants related to UNIX STREAMS","February 2023","","",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index b8ea09637e0191..9d10fc06b2c953 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -41,7 +41,7 @@
"`P0323R12 <https://wg21.link/P0323R12>`__","LWG","``std::expected``","February 2022","|Complete|","16.0"
"`P0533R9 <https://wg21.link/P0533R9>`__","LWG","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","February 2022","|In progress| [#note-P0533R9]_",""
"`P0627R6 <https://wg21.link/P0627R6>`__","LWG","Function to mark unreachable code","February 2022","|Complete|","15.0"
-"`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","|In Progress|","","|ranges|"
+"`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","|Complete|","17.0","|ranges|"
"`P1413R3 <https://wg21.link/P1413R3>`__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_",""
"`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv
index d569f144e4ec29..259a0218ce15e3 100644
--- a/libcxx/docs/Status/RangesMajorFeatures.csv
+++ b/libcxx/docs/Status/RangesMajorFeatures.csv
@@ -1,4 +1,4 @@
Standard,Name,Assignee,CL,Status
-C++23,`ranges::to <https://wg21.link/P1206R7>`_,Unassigned,No patch yet,Not started
+C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2bba97fdf15dab..8d03e1b8f43c30 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -646,6 +646,7 @@ set(files
__ranges/subrange.h
__ranges/take_view.h
__ranges/take_while_view.h
+ __ranges/to.h
__ranges/transform_view.h
__ranges/view_interface.h
__ranges/views.h
diff --git a/libcxx/include/__ranges/to.h b/libcxx/include/__ranges/to.h
new file mode 100644
index 00000000000000..95c300bfa6f204
--- /dev/null
+++ b/libcxx/include/__ranges/to.h
@@ -0,0 +1,247 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_TO_H
+#define _LIBCPP___RANGES_TO_H
+
+#include <__algorithm/ranges_copy.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/back_insert_iterator.h>
+#include <__iterator/insert_iterator.h>
+#include <__iterator/iterator_traits.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/from_range.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/size.h>
+#include <__ranges/transform_view.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/type_identity.h>
+#include <__utility/declval.h>
+#include <__utility/forward.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+// TODO(clang-15): in the Standard, it's a `constexpr bool` variable, not a concept, but constexpr variables don't
+// short-circuit properly on Clang 15 (fixed in later versions), so use a concept as a workaround.
+template <class _Container>
+concept __reservable_container = sized_range<_Container> && requires(_Container& __c, range_size_t<_Container> __n) {
+ __c.reserve(__n);
+ { __c.capacity() } -> same_as<decltype(__n)>;
+ { __c.max_size() } -> same_as<decltype(__n)>;
+};
+
+template <class _Container, class _Ref>
+constexpr bool __container_insertable = requires(_Container& __c, _Ref&& __ref) {
+ requires(
+ requires { __c.push_back(std::forward<_Ref>(__ref)); } ||
+ requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); });
+};
+
+template <class _Ref, class _Container>
+_LIBCPP_HIDE_FROM_ABI constexpr auto __container_inserter(_Container& __c) {
+ if constexpr (requires { __c.push_back(std::declval<_Ref>()); }) {
+ return std::back_inserter(__c);
+ } else {
+ return std::inserter(__c, __c.end());
+ }
+}
+
+// Note: making this a concept allows short-circuiting the second condition.
+template <class _Container, class _Range>
+concept __try_non_recursive_conversion =
+ !input_range<_Container> || convertible_to<range_reference_t<_Range>, range_value_t<_Container>>;
+
+template <class _Container, class _Range, class... _Args>
+concept __constructible_from_iter_pair =
+ common_range<_Range> && requires { typename iterator_traits<iterator_t<_Range>>::iterator_category; } &&
+ derived_from<typename iterator_traits<iterator_t<_Range>>::iterator_category, input_iterator_tag> &&
+ constructible_from<_Container, iterator_t<_Range>, sentinel_t<_Range>, _Args...>;
+
+template <class>
+concept __always_false = false;
+
+// `ranges::to` base template -- the `_Container` type is a simple type template parameter.
+template <class _Container, input_range _Range, class... _Args>
+ requires(!view<_Container>)
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _Container to(_Range&& __range, _Args&&... __args) {
+ // Mandates: C is a cv-unqualified class type.
+ static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
+ static_assert(
+ !is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
+
+ // First see if the non-recursive case applies -- the conversion target is either:
+ // - a range with a convertible value type;
+ // - a non-range type which might support being created from the input argument(s) (e.g. an `optional`).
+ if constexpr (__try_non_recursive_conversion<_Container, _Range>) {
+ // Case 1 -- construct directly from the given range.
+ if constexpr (constructible_from<_Container, _Range, _Args...>) {
+ return _Container(std::forward<_Range>(__range), std::forward<_Args>(__args)...);
+ }
+
+ // Case 2 -- construct using the `from_range_t` tagged constructor.
+ else if constexpr (constructible_from<_Container, from_range_t, _Range, _Args...>) {
+ return _Container(from_range, std::forward<_Range>(__range), std::forward<_Args>(__args)...);
+ }
+
+ // Case 3 -- construct from a begin-end iterator pair.
+ else if constexpr (__constructible_from_iter_pair<_Container, _Range, _Args...>) {
+ return _Container(ranges::begin(__range), ranges::end(__range), std::forward<_Args>(__args)...);
+ }
+
+ // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
+ else if constexpr (constructible_from<_Container, _Args...> &&
+ __container_insertable<_Container, range_reference_t<_Range>>) {
+ _Container __result(std::forward<_Args>(__args)...);
+ if constexpr (sized_range<_Range> && __reservable_container<_Container>) {
+ __result.reserve(static_cast<range_size_t<_Container>>(ranges::size(__range)));
+ }
+
+ ranges::copy(__range, ranges::__container_inserter<range_reference_t<_Range>>(__result));
+
+ return __result;
+
+ } else {
+ static_assert(__always_false<_Container>, "ranges::to: unable to convert to the given container type.");
+ }
+
+ // Try the recursive case.
+ } else if constexpr (input_range<range_reference_t<_Range>>) {
+ return ranges::to<_Container>(
+ __range | views::transform([](auto&& __elem) {
+ return ranges::to<range_value_t<_Container>>(std::forward<decltype(__elem)>(__elem));
+ }),
+ std::forward<_Args>(__args)...);
+
+ } else {
+ static_assert(__always_false<_Container>, "ranges::to: unable to convert to the given container type.");
+ }
+}
+
+template <class _Range>
+struct __minimal_input_iterator {
+ using iterator_category = input_iterator_tag;
+ using value_type = range_value_t<_Range>;
+ using
diff erence_type = ptr
diff _t;
+ using pointer = add_pointer_t<range_reference_t<_Range>>;
+ using reference = range_reference_t<_Range>;
+
+ reference operator*() const;
+ pointer operator->() const;
+ __minimal_input_iterator& operator++();
+ __minimal_input_iterator operator++(int);
+ bool operator==(const __minimal_input_iterator&) const;
+};
+
+// Deduces the full type of the container from the given template template parameter.
+template <template <class...> class _Container, input_range _Range, class... _Args>
+struct _Deducer {
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __deduce_func() {
+ using _InputIter = __minimal_input_iterator<_Range>;
+
+ // Case 1 -- can construct directly from the given range.
+ if constexpr (requires { _Container(std::declval<_Range>(), std::declval<_Args>()...); }) {
+ using _Result = decltype( //
+ _Container(std::declval<_Range>(), std::declval<_Args>()...));
+ return type_identity<_Result>{};
+
+ // Case 2 -- can construct from the given range using the `from_range_t` tagged constructor.
+ } else if constexpr ( //
+ requires { _Container(from_range, std::declval<_Range>(), std::declval<_Args>()...); }) {
+ using _Result = //
+ decltype(_Container(from_range, std::declval<_Range>(), std::declval<_Args>()...));
+ return type_identity<_Result>{};
+
+ // Case 3 -- can construct from a begin-end iterator pair.
+ } else if constexpr ( //
+ requires { _Container(std::declval<_InputIter>(), std::declval<_InputIter>(), std::declval<_Args>()...); }) {
+ using _Result =
+ decltype(_Container(std::declval<_InputIter>(), std::declval<_InputIter>(), std::declval<_Args>()...));
+ return type_identity<_Result>{};
+
+ } else {
+ static_assert(__always_false<_Range>,
+ "ranges::to: unable to deduce the container type from the template template argument.");
+ }
+ }
+
+ using type = typename decltype(__deduce_func())::type;
+};
+
+// `ranges::to` specialization -- `_Container` is a template template parameter requiring deduction to figure out the
+// container element type.
+template <template <class...> class _Container, input_range _Range, class... _Args>
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Range&& __range, _Args&&... __args) {
+ using _DeduceExpr = typename _Deducer<_Container, _Range, _Args...>::type;
+ return ranges::to<_DeduceExpr>(std::forward<_Range>(__range), std::forward<_Args>(__args)...);
+}
+
+// Range adaptor closure object 1 -- wrapping the `ranges::to` version where `_Container` is a simple type template
+// parameter.
+template <class _Container, class... _Args>
+ requires(!view<_Container>)
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Args&&... __args) {
+ // Mandates: C is a cv-unqualified class type.
+ static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
+ static_assert(
+ !is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
+
+ auto __to_func = []<input_range _Range, class... _Tail>(_Range && __range, _Tail && ... __tail)
+ requires requires { //
+ /**/ ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
+ }
+ {
+ return ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
+ };
+
+ return __range_adaptor_closure_t(std::__bind_back(__to_func, std::forward<_Args>(__args)...));
+}
+
+// Range adaptor closure object 2 -- wrapping the `ranges::to` version where `_Container` is a template template
+// parameter.
+template <template <class...> class _Container, class... _Args>
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Args&&... __args) {
+ // clang-format off
+ auto __to_func = []<input_range _Range, class... _Tail,
+ class _DeducedExpr = typename _Deducer<_Container, _Range, _Tail...>::type>
+ (_Range&& __range, _Tail&& ... __tail)
+ requires requires { //
+ /**/ ranges::to<_DeducedExpr>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
+ }
+ {
+ return ranges::to<_DeducedExpr>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
+ };
+ // clang-format on
+
+ return __range_adaptor_closure_t(std::__bind_back(__to_func, std::forward<_Args>(__args)...));
+}
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_TO_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index f9b20416f5e7ab..9ff8b67a6a20fd 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1718,6 +1718,7 @@ module std_private_ranges_subrange_fwd [system] {
}
module std_private_ranges_take_view [system] { header "__ranges/take_view.h" }
module std_private_ranges_take_while_view [system] { header "__ranges/take_while_view.h" }
+module std_private_ranges_to [system] { header "__ranges/to.h" }
module std_private_ranges_transform_view [system] {
header "__ranges/transform_view.h"
export std_private_functional_bind_back
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 3bf9814e958769..523f7cdcb360fa 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -138,6 +138,16 @@ namespace std::ranges {
inline constexpr auto values = elements<1>;
}
+ // [range.utility.conv], range conversions
+ template<class C, input_range R, class... Args> requires (!view<C>)
+ constexpr C to(R&& r, Args&&... args); // Since C++23
+ template<template<class...> class C, input_range R, class... Args>
+ constexpr auto to(R&& r, Args&&... args); // Since C++23
+ template<class C, class... Args> requires (!view<C>)
+ constexpr auto to(Args&&... args); // Since C++23
+ template<template<class...> class C, class... Args>
+ constexpr auto to(Args&&... args); // Since C++23
+
// [range.empty], empty view
template<class T>
requires is_object_v<T>
@@ -391,6 +401,7 @@ namespace std {
#include <__ranges/subrange.h>
#include <__ranges/take_view.h>
#include <__ranges/take_while_view.h>
+#include <__ranges/to.h>
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <__ranges/views.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index 87b792467fcebd..53d40e7bb2b1db 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -162,7 +162,7 @@ __cpp_lib_ranges_repeat 202207L <ranges>
__cpp_lib_ranges_slide 202202L <ranges>
__cpp_lib_ranges_starts_ends_with 202106L <algorithm>
__cpp_lib_ranges_to_container 202202L <deque> <forward_list> <list>
- <map> <priority_queue> <queue>
+ <map> <queue> <ranges>
<set> <stack> <string>
<unordered_map> <unordered_set> <vector>
__cpp_lib_ranges_zip 202110L <ranges> <tuple> <utility>
@@ -440,7 +440,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_ranges_repeat 202207L
// # define __cpp_lib_ranges_slide 202202L
// # define __cpp_lib_ranges_starts_ends_with 202106L
-// # define __cpp_lib_ranges_to_container 202202L
+# define __cpp_lib_ranges_to_container 202202L
// # define __cpp_lib_ranges_zip 202110L
// # define __cpp_lib_reference_from_temporary 202202L
// # define __cpp_lib_spanstream 202106L
diff --git a/libcxx/modules/std/ranges.cppm b/libcxx/modules/std/ranges.cppm
index 7e4c5435ea11c3..cf4c252834f0d7 100644
--- a/libcxx/modules/std/ranges.cppm
+++ b/libcxx/modules/std/ranges.cppm
@@ -95,7 +95,7 @@ export namespace std {
using std::ranges::borrowed_subrange_t;
// [range.utility.conv], range conversions
- // using std::ranges::to;
+ using std::ranges::to;
// [range.empty], empty view
using std::ranges::empty_view;
diff --git a/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.internal_constraints.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.internal_constraints.verify.cpp
new file mode 100644
index 00000000000000..c65b07ac3470a3
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.internal_constraints.verify.cpp
@@ -0,0 +1,94 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Test that the "mandates" requirements on the given container are checked correctly using `static_assert`.
+
+#include <ranges>
+#include <vector>
+
+template <bool HasDefaultCtr = true, bool HasSingleArgCtr = true,
+ bool HasInsert = true, bool HasInsertWithRightSignature = true,
+ bool HasPushBack = true, bool HasPushBackWithRightSignature = true>
+struct Container {
+ using value_type = int;
+
+ int* begin() const { return nullptr; }
+ int* end() const { return nullptr; }
+
+ Container()
+ requires HasDefaultCtr = default;
+
+ Container(int)
+ requires HasSingleArgCtr {
+ }
+
+ int* insert(int*, int)
+ requires (HasInsert && HasInsertWithRightSignature) {
+ return nullptr;
+ }
+
+ int* insert()
+ requires (HasInsert && !HasInsertWithRightSignature) {
+ return nullptr;
+ }
+
+ void push_back(int)
+ requires (HasPushBack && HasPushBackWithRightSignature) {
+ }
+
+ void push_back()
+ requires (HasPushBack && !HasPushBackWithRightSignature) {
+ }
+
+};
+
+void test() {
+ using R = std::vector<int>;
+ R in = {1, 2, 3};
+
+ // Case 4 -- default-construct (or construct from the extra arguments) and insert.
+ { // All constraints satisfied.
+ using C = Container<>;
+ (void)std::ranges::to<C>(in);
+ (void)std::ranges::to<C>(in, 1);
+ (void)std::ranges::to<C>(in, 1.0);
+ }
+
+ { // No default constructor.
+ using C = Container</*HasDefaultCtr=*/false>;
+ (void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
+ }
+
+ { // No single-argument constructor.
+ using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/false>;
+ (void)std::ranges::to<C>(in, 1); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
+ }
+
+ { // No `insert` and no `push_back`.
+ using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/true,
+ /*HasInsert=*/false, /*HasInsertWithRightSignature=*/false,
+ /*HasPushBack=*/false, /*HasPushBackWithRightSignature=*/false>;
+ (void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
+ }
+
+ { // No `push_back`, `insert` has a wrong signature.
+ using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/true,
+ /*HasInsert=*/true, /*HasInsertWithRightSignature=*/false,
+ /*HasPushBack=*/false, /*HasPushBackWithRightSignature=*/false>;
+ (void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
+ }
+
+ { // No `insert`, `push_back` has a wrong signature.
+ using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/true,
+ /*HasInsert=*/false, /*HasInsertWithRightSignature=*/false,
+ /*HasPushBack=*/true, /*HasPushBackWithRightSignature=*/false>;
+ (void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
+ }
+}
diff --git a/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..b252c2ffa3b49b
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Test the libc++ extension that `std::ranges::to` is marked as [[nodiscard]].
+
+#include <ranges>
+#include <vector>
+
+void test() {
+ using R = std::vector<int>;
+ R in = {1, 2, 3};
+ std::allocator<int> alloc;
+
+ std::ranges::to<R>(in); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::to<R>(in, alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::to<std::vector>(in); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::to<std::vector>(in, alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ in | std::ranges::to<R>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ in | std::ranges::to<R>(alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ in | std::ranges::to<std::vector>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ in | std::ranges::to<std::vector>(alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.static_assert.verify.cpp b/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.static_assert.verify.cpp
new file mode 100644
index 00000000000000..4da78860c82e73
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.utility/range.utility.conv/to.static_assert.verify.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile
+
+// Test that the "mandates" requirements on the given container are checked using `static_assert`.
+
+#include <ranges>
+#include <vector>
+
+void test() {
+ using R = std::vector<int>;
+ R in = {1, 2, 3};
+
+ (void)std::ranges::to<const R>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be const-qualified, please remove the const}}
+ (void)(in | std::ranges::to<const R>()); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be const-qualified, please remove the const}}
+ (void)std::ranges::to<volatile R>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
+ (void)(in | std::ranges::to<volatile R>()); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
+}
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/deque.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/deque.version.compile.pass.cpp
index 4a70a98b4f4c09..4a398e23063422 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/deque.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/deque.version.compile.pass.cpp
@@ -135,17 +135,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -171,17 +165,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/forward_list.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/forward_list.version.compile.pass.cpp
index f21cbbf6e91310..b163943114f776 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/forward_list.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/forward_list.version.compile.pass.cpp
@@ -192,17 +192,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -242,17 +236,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/list.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/list.version.compile.pass.cpp
index df136743fc6eda..48bff77f91d96f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/list.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/list.version.compile.pass.cpp
@@ -192,17 +192,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -242,17 +236,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
index 8599c54e249232..2c17d2acf4940f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
@@ -279,17 +279,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -362,17 +356,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/queue.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/queue.version.compile.pass.cpp
index 78801b8cc275fd..fdedd27bd46bcc 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/queue.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/queue.version.compile.pass.cpp
@@ -72,17 +72,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -94,17 +88,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 3758c53bd34613..4a439ae90fcc0f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -15,15 +15,16 @@
// Test the feature test macros defined by <ranges>
-/* Constant Value
- __cpp_lib_ranges 202207L [C++20]
- __cpp_lib_ranges_as_rvalue 202207L [C++23]
- __cpp_lib_ranges_chunk 202202L [C++23]
- __cpp_lib_ranges_chunk_by 202202L [C++23]
- __cpp_lib_ranges_join_with 202202L [C++23]
- __cpp_lib_ranges_repeat 202207L [C++23]
- __cpp_lib_ranges_slide 202202L [C++23]
- __cpp_lib_ranges_zip 202110L [C++23]
+/* Constant Value
+ __cpp_lib_ranges 202207L [C++20]
+ __cpp_lib_ranges_as_rvalue 202207L [C++23]
+ __cpp_lib_ranges_chunk 202202L [C++23]
+ __cpp_lib_ranges_chunk_by 202202L [C++23]
+ __cpp_lib_ranges_join_with 202202L [C++23]
+ __cpp_lib_ranges_repeat 202207L [C++23]
+ __cpp_lib_ranges_slide 202202L [C++23]
+ __cpp_lib_ranges_to_container 202202L [C++23]
+ __cpp_lib_ranges_zip 202110L [C++23]
*/
#include <ranges>
@@ -59,6 +60,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@@ -93,6 +98,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@@ -127,6 +136,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@@ -164,6 +177,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
+# ifdef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should not be defined before c++23"
+# endif
+
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@@ -243,6 +260,13 @@
# endif
# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
+# endif
+
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should be defined in c++23"
@@ -331,6 +355,13 @@
# endif
# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
+# endif
+
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should be defined in c++26"
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index 241b66b2bfc319..271df95b81496c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -249,17 +249,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -325,17 +319,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stack.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stack.version.compile.pass.cpp
index 373fcb432961d4..cc5af8a4df609a 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stack.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stack.version.compile.pass.cpp
@@ -72,17 +72,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -94,17 +88,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
index 5902bdbd155217..b5770f8fbe65d3 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
@@ -322,17 +322,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# ifndef __cpp_lib_starts_ends_with
@@ -426,17 +420,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# ifndef __cpp_lib_starts_ends_with
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
index c3dfb78cfadd1a..743f89dddfba0c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
@@ -266,17 +266,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# ifndef __cpp_lib_unordered_map_try_emplace
@@ -349,17 +343,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# ifndef __cpp_lib_unordered_map_try_emplace
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
index d215aec8615648..a0947e995a28fc 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
@@ -243,17 +243,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -319,17 +313,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/vector.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/vector.version.compile.pass.cpp
index b81267176ab34e..6eee936b4ed88f 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/vector.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/vector.version.compile.pass.cpp
@@ -192,17 +192,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@@ -242,17 +236,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 94d28382049898..ec226f3a5fa055 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5042,17 +5042,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
@@ -6601,17 +6595,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_to_container != 202202L
-# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_to_container
-# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_to_container
+# error "__cpp_lib_ranges_to_container should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_to_container != 202202L
+# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/ranges/range.utility/range.utility.conv/container.h b/libcxx/test/std/ranges/range.utility/range.utility.conv/container.h
new file mode 100644
index 00000000000000..fafccacd456d5b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/container.h
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
+#define RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
+
+#include <algorithm>
+#include <concepts>
+#include <cstddef>
+
+enum class CtrChoice { Invalid, DefaultCtrAndInsert, BeginEndPair, FromRangeT, DirectCtr };
+
+enum class InserterChoice { Invalid, Insert, PushBack };
+
+// Allows checking that `ranges::to` correctly follows the order of priority of
diff erent constructors -- e.g., if
+// 3 constructors are available, the `from_range_t` constructor is chosen in favor of the constructor taking two
+// iterators, etc.
+template <class ElementType, CtrChoice Rank, InserterChoice Inserter = InserterChoice::Insert, bool CanReserve = false>
+struct Container {
+ CtrChoice ctr_choice = CtrChoice::Invalid;
+ InserterChoice inserter_choice = InserterChoice::Invalid;
+ bool called_reserve = false;
+
+ int extra_arg1 = 0;
+ char extra_arg2 = 0;
+
+ using value_type = ElementType;
+ static constexpr int Capacity = 8;
+ int size_ = 0;
+ ElementType buffer_[Capacity] = {};
+
+ // Case 1 -- construct directly from the range.
+
+ constexpr explicit Container(std::ranges::input_range auto&& in)
+ requires(Rank >= CtrChoice::DirectCtr)
+ : ctr_choice(CtrChoice::DirectCtr), size_(std::ranges::size(in)) {
+ std::ranges::copy(in, begin());
+ }
+
+ // Check that `ranges::to` can also pass extra parameters.
+ constexpr explicit Container(std::ranges::input_range auto&& in, int arg1, char arg2)
+ requires(Rank >= CtrChoice::DirectCtr)
+ : Container(in) {
+ extra_arg1 = arg1;
+ extra_arg2 = arg2;
+ }
+
+ // Case 2 -- use `from_range_t` constructor.
+
+ constexpr Container(std::from_range_t, std::ranges::input_range auto&& in)
+ requires(Rank >= CtrChoice::FromRangeT)
+ : ctr_choice(CtrChoice::FromRangeT), size_(std::ranges::size(in)) {
+ std::ranges::copy(in, begin());
+ }
+
+ constexpr Container(std::from_range_t, std::ranges::input_range auto&& in, int arg1, char arg2)
+ requires(Rank >= CtrChoice::FromRangeT)
+ : Container(std::from_range, in) {
+ extra_arg1 = arg1;
+ extra_arg2 = arg2;
+ }
+
+ // Case 3 -- use begin-end pair.
+
+ template <class Iter>
+ constexpr Container(Iter b, Iter e)
+ requires(Rank >= CtrChoice::BeginEndPair)
+ : ctr_choice(CtrChoice::BeginEndPair), size_(e - b) {
+ std::ranges::copy(b, e, begin());
+ }
+
+ template <class Iter>
+ constexpr Container(Iter b, Iter e, int arg1, char arg2)
+ requires(Rank >= CtrChoice::BeginEndPair)
+ : Container(b, e) {
+ extra_arg1 = arg1;
+ extra_arg2 = arg2;
+ }
+
+ // Case 4 -- default-construct and insert, reserving the size if possible.
+
+ constexpr Container()
+ requires(Rank >= CtrChoice::DefaultCtrAndInsert)
+ : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
+
+ constexpr Container(int arg1, char arg2)
+ requires(Rank >= CtrChoice::DefaultCtrAndInsert)
+ : ctr_choice(CtrChoice::DefaultCtrAndInsert), extra_arg1(arg1), extra_arg2(arg2) {}
+
+ constexpr ElementType* begin() { return buffer_; }
+ constexpr ElementType* end() { return buffer_ + size_; }
+ constexpr std::size_t size() const { return size_; }
+
+ template <class T>
+ constexpr void push_back(T val)
+ requires(Inserter >= InserterChoice::PushBack)
+ {
+ inserter_choice = InserterChoice::PushBack;
+ buffer_[size_] = val;
+ ++size_;
+ }
+
+ template <class T>
+ constexpr ElementType* insert(ElementType* where, T val)
+ requires(Inserter >= InserterChoice::Insert)
+ {
+ assert(size() + 1 <= Capacity);
+
+ inserter_choice = InserterChoice::Insert;
+
+ std::shift_right(where, end(), 1);
+ *where = val;
+ ++size_;
+
+ return where;
+ }
+
+ constexpr void reserve(size_t)
+ requires CanReserve
+ {
+ called_reserve = true;
+ }
+
+ constexpr std::size_t capacity() const
+ requires CanReserve
+ {
+ return Capacity;
+ }
+
+ constexpr std::size_t max_size() const
+ requires CanReserve
+ {
+ return Capacity;
+ }
+
+ friend constexpr bool operator==(const Container&, const Container&) = default;
+};
+
+#endif // RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
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
new file mode 100644
index 00000000000000..75f55bc420d0eb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp
@@ -0,0 +1,574 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template<class C, input_range R, class... Args> requires (!view<C>)
+// constexpr C to(R&& r, Args&&... args); // Since C++23
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <vector>
+#include "container.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+template <class Container, class Range, class... Args>
+concept HasTo = requires (Range&& range, Args ...args) {
+ std::ranges::to<Container>(std::forward<Range>(range), std::forward<Args>(args)...);
+};
+
+struct InputRange {
+ int x = 0;
+ constexpr cpp20_input_iterator<int*> begin() {
+ return cpp20_input_iterator<int*>(&x);
+ }
+ constexpr sentinel_wrapper<cpp20_input_iterator<int*>> end() {
+ return sentinel_wrapper<cpp20_input_iterator<int*>>(begin());
+ }
+};
+static_assert(std::ranges::input_range<InputRange>);
+
+struct common_cpp20_input_iterator {
+ using value_type = int;
+ using
diff erence_type = long long;
+ using iterator_concept = std::input_iterator_tag;
+ // Deliberately not defining `iterator_category` to make sure this class satisfies the `input_iterator` concept but
+ // would fail `derived_from<iterator_category, input_iterator_tag>`.
+
+ int x = 0;
+
+ // Copyable so that it can be used as a sentinel against itself.
+ constexpr decltype(auto) operator*() const { return x; }
+ constexpr common_cpp20_input_iterator& operator++() { return *this; }
+ constexpr void operator++(int) {}
+ constexpr friend bool operator==(common_cpp20_input_iterator, common_cpp20_input_iterator) { return true; }
+};
+static_assert(std::input_iterator<common_cpp20_input_iterator>);
+static_assert(std::sentinel_for<common_cpp20_input_iterator, common_cpp20_input_iterator>);
+template <class T>
+concept HasIteratorCategory = requires {
+ typename std::iterator_traits<T>::iterator_category;
+};
+static_assert(!HasIteratorCategory<common_cpp20_input_iterator>);
+
+struct CommonInputRange {
+ int x = 0;
+ constexpr common_cpp20_input_iterator begin() { return {}; }
+ constexpr common_cpp20_input_iterator end() { return begin(); }
+};
+static_assert(std::ranges::input_range<CommonInputRange>);
+static_assert(std::ranges::common_range<CommonInputRange>);
+
+struct CommonRange {
+ int x = 0;
+ constexpr forward_iterator<int*> begin() {
+ return forward_iterator<int*>(&x);
+ }
+ constexpr forward_iterator<int*> end() {
+ return begin();
+ }
+};
+static_assert(std::ranges::input_range<CommonRange>);
+static_assert(std::ranges::common_range<CommonRange>);
+
+struct NonCommonRange {
+ int x = 0;
+ constexpr forward_iterator<int*> begin() {
+ return forward_iterator<int*>(&x);
+ }
+ constexpr sentinel_wrapper<forward_iterator<int*>> end() {
+ return sentinel_wrapper<forward_iterator<int*>>(begin());
+ }
+};
+static_assert(std::ranges::input_range<NonCommonRange>);
+static_assert(!std::ranges::common_range<NonCommonRange>);
+static_assert(std::derived_from<
+ typename std::iterator_traits<std::ranges::iterator_t<NonCommonRange>>::iterator_category,
+ std::input_iterator_tag>);
+
+using ContainerT = int;
+static_assert(!std::ranges::view<ContainerT>);
+static_assert(HasTo<ContainerT, InputRange>);
+static_assert(!HasTo<test_view<forward_iterator>, InputRange>);
+
+// Note: it's not possible to check the `input_range` constraint because if it's not satisfied, the pipe adaptor
+// overload hijacks the call (it takes unconstrained variadic arguments).
+
+// Check the exact constraints for each one of the cases inside `ranges::to`.
+
+struct Empty {};
+
+struct Fallback {
+ using value_type = int;
+
+ CtrChoice ctr_choice = CtrChoice::Invalid;
+ int x = 0;
+
+ constexpr Fallback() : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
+ constexpr Fallback(Empty) : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
+
+ constexpr void push_back(value_type) {}
+ constexpr value_type* begin() { return &x; }
+ constexpr value_type* end() { return &x; }
+};
+
+struct CtrDirectOrFallback : Fallback {
+ using Fallback::Fallback;
+ constexpr CtrDirectOrFallback(InputRange&&, int = 0) { ctr_choice = CtrChoice::DirectCtr; }
+};
+
+struct CtrFromRangeTOrFallback : Fallback {
+ using Fallback::Fallback;
+ constexpr CtrFromRangeTOrFallback(std::from_range_t, InputRange&&, int = 0) { ctr_choice = CtrChoice::FromRangeT; }
+};
+
+struct CtrBeginEndPairOrFallback : Fallback {
+ using Fallback::Fallback;
+ template <class Iter>
+ constexpr CtrBeginEndPairOrFallback(Iter, Iter, int = 0) { ctr_choice = CtrChoice::BeginEndPair; }
+};
+
+template <bool HasSize>
+struct MaybeSizedRange {
+ int x = 0;
+ constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&x); }
+ constexpr forward_iterator<int*> end() { return begin(); }
+
+ constexpr std::size_t size() const
+ requires HasSize {
+ return 0;
+ }
+};
+static_assert(std::ranges::sized_range<MaybeSizedRange<true>>);
+static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>);
+
+template <bool HasCapacity = true, bool CapacityReturnsSizeT = true,
+ bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true>
+struct Reservable : Fallback {
+ bool reserve_called = false;
+
+ using Fallback::Fallback;
+
+ constexpr std::size_t capacity() const
+ requires (HasCapacity && CapacityReturnsSizeT) {
+ return 0;
+ }
+ constexpr int capacity() const
+ requires (HasCapacity && !CapacityReturnsSizeT) {
+ return 0;
+ }
+
+ constexpr std::size_t max_size() const
+ requires (HasMaxSize && MaxSizeReturnsSizeT) {
+ return 0;
+ }
+ constexpr int max_size() const
+ requires (HasMaxSize && !MaxSizeReturnsSizeT) {
+ return 0;
+ }
+
+ constexpr void reserve(std::size_t) {
+ reserve_called = true;
+ }
+};
+static_assert(std::ranges::__reservable_container<Reservable<>>);
+
+constexpr void test_constraints() {
+ { // Case 1 -- construct directly from the range.
+ { // (range)
+ auto result = std::ranges::to<CtrDirectOrFallback>(InputRange());
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ }
+
+ { // (range, arg)
+ auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1);
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ }
+
+ { // (range, convertible-to-arg)
+ auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1.0);
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ }
+
+ { // (range, BAD_arg)
+ auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), Empty());
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ }
+ }
+
+ { // Case 2 -- construct using the `from_range_t` tagged constructor.
+ { // (range)
+ auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange());
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ }
+
+ { // (range, arg)
+ auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1);
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ }
+
+ { // (range, convertible-to-arg)
+ auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1.0);
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ }
+
+ { // (range, BAD_arg)
+ auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), Empty());
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ }
+ }
+
+ { // Case 3 -- construct from a begin-end iterator pair.
+ { // (range)
+ auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange());
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ }
+
+ { // (range, arg)
+ auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1);
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ }
+
+ { // (range, convertible-to-arg)
+ auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1.0);
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ }
+
+ { // (BAD_range) -- not a common range.
+ auto result = std::ranges::to<CtrBeginEndPairOrFallback>(NonCommonRange());
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ }
+
+ { // (BAD_range) -- iterator type not derived from `input_iterator_tag`.
+ auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonInputRange());
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ }
+
+ { // (range, BAD_arg)
+ auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), Empty());
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ }
+ }
+
+ { // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
+ // Note: it's not possible to check the constraints on the default constructor using this approach because there is
+ // nothing to fall back to -- the call will result in a hard error.
+ // However, it's possible to check the constraints on reserving the capacity.
+
+ { // All constraints satisfied.
+ using C = Reservable<>;
+ auto result = std::ranges::to<C>(MaybeSizedRange<true>());
+ assert(result.reserve_called);
+ }
+
+ { // !sized_range
+ using C = Reservable<>;
+ auto result = std::ranges::to<C>(MaybeSizedRange<false>());
+ assert(!result.reserve_called);
+ }
+
+ { // Missing `capacity`.
+ using C = Reservable</*HasCapacity=*/false>;
+ auto result = std::ranges::to<C>(MaybeSizedRange<true>());
+ assert(!result.reserve_called);
+ }
+
+ { // `capacity` doesn't return `size_type`.
+ using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/false>;
+ auto result = std::ranges::to<C>(MaybeSizedRange<true>());
+ assert(!result.reserve_called);
+ }
+
+ { // Missing `max_size`.
+ using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/false>;
+ auto result = std::ranges::to<C>(MaybeSizedRange<true>());
+ assert(!result.reserve_called);
+ }
+
+ { // `max_size` doesn't return `size_type`.
+ using C = Reservable<
+ /*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/true, /*MaxSizeReturnsSizeT=*/false>;
+ auto result = std::ranges::to<C>(MaybeSizedRange<true>());
+ assert(!result.reserve_called);
+ }
+ }
+}
+
+constexpr void test_ctr_choice_order() {
+ std::array in = {1, 2, 3, 4, 5};
+ int arg1 = 42;
+ char arg2 = 'a';
+
+ { // Case 1 -- construct directly from the given range.
+ {
+ using C = Container<int, CtrChoice::DirectCtr>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ assert(std::ranges::equal(result, in));
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ { // Extra arguments.
+ using C = Container<int, CtrChoice::DirectCtr>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ assert(std::ranges::equal(result, in));
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<C>(arg1, arg2)) == result);
+ auto closure = std::ranges::to<C>(arg1, arg2);
+ assert((in | closure) == result);
+ }
+ }
+
+ { // Case 2 -- construct using the `from_range_t` tag.
+ {
+ using C = Container<int, CtrChoice::FromRangeT>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ assert(std::ranges::equal(result, in));
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ { // Extra arguments.
+ using C = Container<int, CtrChoice::FromRangeT>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ assert(std::ranges::equal(result, in));
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<C>(arg1, arg2)) == result);
+ auto closure = std::ranges::to<C>(arg1, arg2);
+ assert((in | closure) == result);
+ }
+ }
+
+ { // Case 3 -- construct from a begin-end pair.
+ {
+ using C = Container<int, CtrChoice::BeginEndPair>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ assert(std::ranges::equal(result, in));
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ { // Extra arguments.
+ using C = Container<int, CtrChoice::BeginEndPair>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ assert(std::ranges::equal(result, in));
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<C>(arg1, arg2)) == result);
+ auto closure = std::ranges::to<C>(arg1, arg2);
+ assert((in | closure) == result);
+ }
+ }
+
+ { // Case 4 -- default-construct then insert elements.
+ {
+ using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/false>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ assert(result.inserter_choice == InserterChoice::Insert);
+ assert(std::ranges::equal(result, in));
+ assert(!result.called_reserve);
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ {
+ using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/true>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ assert(result.inserter_choice == InserterChoice::Insert);
+ assert(std::ranges::equal(result, in));
+ assert(result.called_reserve);
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ {
+ using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, /*CanReserve=*/false>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ assert(result.inserter_choice == InserterChoice::PushBack);
+ assert(std::ranges::equal(result, in));
+ assert(!result.called_reserve);
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ {
+ using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, /*CanReserve=*/true>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ assert(result.inserter_choice == InserterChoice::PushBack);
+ assert(std::ranges::equal(result, in));
+ assert(result.called_reserve);
+ assert((in | std::ranges::to<C>()) == result);
+ auto closure = std::ranges::to<C>();
+ assert((in | closure) == result);
+ }
+
+ { // Extra arguments.
+ using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/false>;
+ std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+ assert(result.inserter_choice == InserterChoice::Insert);
+ assert(std::ranges::equal(result, in));
+ assert(!result.called_reserve);
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<C>(arg1, arg2)) == result);
+ auto closure = std::ranges::to<C>(arg1, arg2);
+ assert((in | closure) == result);
+ }
+ }
+}
+
+template <CtrChoice Rank>
+struct NotARange {
+ using value_type = int;
+
+ constexpr NotARange(std::ranges::input_range auto&&)
+ requires (Rank >= CtrChoice::DirectCtr)
+ {}
+
+ constexpr NotARange(std::from_range_t, std::ranges::input_range auto&&)
+ requires (Rank >= CtrChoice::FromRangeT)
+ {}
+
+ template <class Iter>
+ constexpr NotARange(Iter, Iter)
+ requires (Rank >= CtrChoice::BeginEndPair)
+ {}
+
+ constexpr NotARange()
+ requires (Rank >= CtrChoice::DefaultCtrAndInsert)
+ = default;
+
+ constexpr void push_back(int) {}
+};
+
+static_assert(!std::ranges::range<NotARange<CtrChoice::DirectCtr>>);
+
+constexpr void test_lwg_3785() {
+ // Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible
+ // to convert the given input range to a non-range type.
+ std::array in = {1, 2, 3, 4, 5};
+
+ {
+ using C = NotARange<CtrChoice::DirectCtr>;
+ [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+ }
+
+ {
+ using C = NotARange<CtrChoice::FromRangeT>;
+ [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+ }
+
+ {
+ using C = NotARange<CtrChoice::BeginEndPair>;
+ [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+ }
+
+ {
+ using C = NotARange<CtrChoice::DefaultCtrAndInsert>;
+ [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
+ }
+}
+
+constexpr void test_recursive() {
+ using C1 = Container<int, CtrChoice::DirectCtr>;
+ using C2 = Container<C1, CtrChoice::FromRangeT>;
+ using C3 = Container<C2, CtrChoice::BeginEndPair>;
+ using C4 = Container<C3, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack>;
+ using A1 = std::array<int, 4>;
+ using A2 = std::array<A1, 3>;
+ using A3 = std::array<A2, 2>;
+ using A4 = std::array<A3, 2>;
+
+ A4 in = {};
+ { // Fill the nested array with incremental values.
+ int x = 0;
+ for (auto& a3 : in) {
+ for (auto& a2 : a3) {
+ for (auto& a1 : a2) {
+ for (int& el : a1) {
+ el = x++;
+ }
+ }
+ }
+ }
+ }
+
+ std::same_as<C4> decltype(auto) result = std::ranges::to<C4>(in);
+
+ assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
+
+ int expected_value = 0;
+ for (auto& c3 : result) {
+ assert(c3.ctr_choice == CtrChoice::BeginEndPair);
+
+ for (auto& c2 : c3) {
+ assert(c2.ctr_choice == CtrChoice::FromRangeT);
+
+ for (auto& c1 : c2) {
+ assert(c1.ctr_choice == CtrChoice::DirectCtr);
+
+ for (int el : c1) {
+ assert(el == expected_value);
+ ++expected_value;
+ }
+ }
+ }
+ }
+
+ assert((in | std::ranges::to<C4>()) == result);
+}
+
+constexpr bool test() {
+ test_constraints();
+ test_ctr_choice_order();
+ test_lwg_3785();
+ test_recursive();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.utility/range.utility.conv/to_deduction.pass.cpp b/libcxx/test/std/ranges/range.utility/range.utility.conv/to_deduction.pass.cpp
new file mode 100644
index 00000000000000..f84cedbc122a1c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/to_deduction.pass.cpp
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// There is a bug in older versions of Clang that causes trouble with constraints in classes like
+// `ContainerWithDirectCtr`.
+// XFAIL: clang-16, apple-clang-15
+
+// template<template<class...> class C, input_range R, class... Args>
+// constexpr auto to(R&& r, Args&&... args); // Since C++23
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include "container.h"
+
+template <class ElementType>
+struct ContainerWithDirectCtr : Container<ElementType, CtrChoice::DirectCtr> {
+ using Container<ElementType, CtrChoice::DirectCtr>::Container;
+};
+
+template <std::ranges::input_range Range>
+ContainerWithDirectCtr(Range&&) -> ContainerWithDirectCtr<std::ranges::range_value_t<Range>>;
+
+template <std::ranges::input_range Range>
+ContainerWithDirectCtr(Range&&, int, char) -> ContainerWithDirectCtr<std::ranges::range_value_t<Range>>;
+
+template <class ElementType>
+struct ContainerWithFromRangeT : Container<ElementType, CtrChoice::FromRangeT> {
+ using Container<ElementType, CtrChoice::FromRangeT>::Container;
+};
+
+template <std::ranges::input_range Range>
+ContainerWithFromRangeT(std::from_range_t, Range&&) -> ContainerWithFromRangeT<std::ranges::range_value_t<Range>>;
+
+template <std::ranges::input_range Range>
+ContainerWithFromRangeT(std::from_range_t, Range&&, int, char) ->
+ ContainerWithFromRangeT<std::ranges::range_value_t<Range>>;
+
+template <class ElementType>
+struct ContainerWithBeginEndPair : Container<ElementType, CtrChoice::BeginEndPair> {
+ using Container<ElementType, CtrChoice::BeginEndPair>::Container;
+};
+
+template <class Iter>
+ContainerWithBeginEndPair(Iter, Iter) -> ContainerWithBeginEndPair<std::iter_value_t<Iter>>;
+
+template <class Iter>
+ContainerWithBeginEndPair(Iter, Iter, int, char) -> ContainerWithBeginEndPair<std::iter_value_t<Iter>>;
+
+constexpr bool test() {
+ std::array in = {1, 2, 3, 4, 5};
+ int arg1 = 42;
+ char arg2 = 'a';
+
+ { // Case 1 -- can construct directly from the given range.
+ {
+ std::same_as<ContainerWithDirectCtr<int>> decltype(auto) result = std::ranges::to<ContainerWithDirectCtr>(in);
+
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ assert(std::ranges::equal(result, in));
+ assert((in | std::ranges::to<ContainerWithDirectCtr>()) == result);
+ }
+
+ { // Extra arguments.
+ std::same_as<ContainerWithDirectCtr<int>> decltype(auto) result =
+ std::ranges::to<ContainerWithDirectCtr>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::DirectCtr);
+ assert(std::ranges::equal(result, in));
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<ContainerWithDirectCtr>(arg1, arg2)) == result);
+ }
+ }
+
+ { // Case 2 -- can construct from the given range using the `from_range_t` tagged constructor.
+ {
+ std::same_as<ContainerWithFromRangeT<int>> decltype(auto) result = std::ranges::to<ContainerWithFromRangeT>(in);
+
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ assert(std::ranges::equal(result, in));
+ assert((in | std::ranges::to<ContainerWithFromRangeT>()) == result);
+ }
+
+ { // Extra arguments.
+ std::same_as<ContainerWithFromRangeT<int>> decltype(auto) result =
+ std::ranges::to<ContainerWithFromRangeT>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::FromRangeT);
+ assert(std::ranges::equal(result, in));
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<ContainerWithFromRangeT>(arg1, arg2)) == result);
+ }
+ }
+
+ { // Case 3 -- can construct from a begin-end iterator pair.
+ {
+ std::same_as<ContainerWithBeginEndPair<int>> decltype(auto) result =
+ std::ranges::to<ContainerWithBeginEndPair>(in);
+
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ assert(std::ranges::equal(result, in));
+ assert((in | std::ranges::to<ContainerWithBeginEndPair>()) == result);
+ }
+
+ { // Extra arguments.
+ std::same_as<ContainerWithBeginEndPair<int>> decltype(auto) result =
+ std::ranges::to<ContainerWithBeginEndPair>(in, arg1, arg2);
+
+ assert(result.ctr_choice == CtrChoice::BeginEndPair);
+ assert(std::ranges::equal(result, in));
+ assert(result.extra_arg1 == arg1);
+ assert(result.extra_arg2 == arg2);
+ assert((in | std::ranges::to<ContainerWithBeginEndPair>(arg1, arg2)) == result);
+ }
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.utility/range.utility.conv/to_std_containers.pass.cpp b/libcxx/test/std/ranges/range.utility/range.utility.conv/to_std_containers.pass.cpp
new file mode 100644
index 00000000000000..4c19ea45b9a2cf
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.utility.conv/to_std_containers.pass.cpp
@@ -0,0 +1,214 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Test that `ranges::to` can be used to convert between arbitrary standard containers.
+
+#include <ranges>
+
+#include <algorithm>
+#include <cassert>
+#include <deque>
+#include <forward_list>
+#include <list>
+#include <map>
+#include <queue>
+#include <set>
+#include <stack>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_range.h"
+#include "type_algorithms.h"
+#include "unwrap_container_adaptor.h"
+
+std::vector<std::vector<int>> ints = {
+ {5, 1, 3, 4, 2},
+ {3},
+ {}
+};
+
+std::vector<std::vector<char>> chars = {
+ {'a', 'b', 'c'},
+ {'a'},
+ {}
+};
+
+std::vector<std::vector<std::pair<const int, int>>> pairs = {
+ {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}},
+ {{1, 2}},
+ {}
+};
+
+template <class From, class To>
+void test_is_equal(std::vector<std::vector<typename From::value_type>> inputs) {
+ for (const auto& in : inputs) {
+ From from(in.begin(), in.end());
+ std::same_as<To> decltype(auto) result = std::ranges::to<To>(from);
+ assert(std::ranges::equal(in, result));
+ }
+}
+
+template <class From, class To>
+void test_is_permutation(std::vector<std::vector<typename From::value_type>> inputs) {
+ for (const auto& in : inputs) {
+ From from(in.begin(), in.end());
+ std::same_as<To> decltype(auto) result = std::ranges::to<To>(in);
+ assert(std::ranges::is_permutation(in, result));
+ }
+}
+
+template <class From, class To>
+void test_is_equal_for_adaptors(std::vector<std::vector<typename From::value_type>> inputs) {
+ for (const auto& in : inputs) {
+ From from(in.begin(), in.end());
+ std::same_as<To> decltype(auto) result = std::ranges::to<To>(in);
+
+ UnwrapAdaptor<From> unwrap_from(std::move(from));
+ UnwrapAdaptor<To> unwrap_to(std::move(result));
+ assert(std::ranges::is_permutation(unwrap_from.get_container(), unwrap_to.get_container()));
+ }
+}
+
+template <class T>
+using sequence_containers = types::type_list<
+ std::vector<T>,
+ std::deque<T>,
+ std::list<T>,
+ std::forward_list<T>
+>;
+
+template <class T>
+using associative_sets = types::type_list<
+ std::set<T>,
+ std::multiset<T>
+>;
+
+template <class K, class V>
+using associative_maps = types::type_list<
+ std::map<K, V>,
+ std::multimap<K, V>
+>;
+
+template <class T>
+using unordered_sets = types::type_list<
+ std::unordered_set<T>,
+ std::unordered_multiset<T>
+>;
+
+template <class K, class V>
+using unordered_maps = types::type_list<
+ std::unordered_map<K, V>,
+ std::unordered_multimap<K, V>
+>;
+
+template <class T>
+using container_adaptors = types::type_list<
+ std::stack<T>,
+ std::queue<T>,
+ std::priority_queue<T>
+>;
+
+template <class T>
+using sequences_and_sets = types::concatenate_t<sequence_containers<T>, associative_sets<T>, unordered_sets<T>>;
+
+template <class K, class V>
+using all_containers = types::concatenate_t<
+ sequence_containers<std::pair<const K, V>>,
+ associative_sets<std::pair<const K, V>>,
+ associative_maps<K, V>,
+ unordered_sets<std::pair<const K, V>>,
+ unordered_maps<K, V>>;
+
+// This is necessary to be able to use `pair`s with unordered sets.
+template <class K, class V>
+struct std::hash<std::pair<const K, V>> {
+ std::size_t operator()(const std::pair<const K, V>& p) const {
+ std::size_t h1 = std::hash<K>{}(p.first);
+ std::size_t h2 = std::hash<V>{}(p.second);
+ return h1 ^ (h2 << 1);
+ }
+};
+
+void test() {
+ { // Conversions always preserving equality.
+ { // sequences <-> sequences
+ types::for_each(sequence_containers<int>{}, []<class From>() {
+ types::for_each(sequence_containers<int>{}, []<class To>() {
+ test_is_equal<From, To>(ints);
+ });
+ });
+
+ types::for_each(sequence_containers<int>{}, []<class From>() {
+ types::for_each(sequence_containers<double>{}, []<class To>() {
+ test_is_equal<From, To>(ints);
+ });
+ });
+ }
+
+ { // sequences <-> string
+ types::for_each(sequence_containers<char>{}, []<class Seq>() {
+ test_is_equal<Seq, std::basic_string<char>>(chars);
+ test_is_equal<std::basic_string<char>, Seq>(chars);
+ });
+ }
+ }
+
+ { // sequences/sets <-> sequences/sets
+ types::for_each(sequences_and_sets<int>{}, []<class From>() {
+ types::for_each(sequences_and_sets<int>{}, []<class To>() {
+ test_is_permutation<From, To>(ints);
+ });
+ });
+
+ types::for_each(sequences_and_sets<int>{}, []<class From>() {
+ types::for_each(sequences_and_sets<double>{}, []<class To>() {
+ test_is_permutation<From, To>(ints);
+ });
+ });
+ }
+
+ { // sequences/sets/maps <-> sequences/sets/maps. Uses `pair` for non-map containers to allow mutual conversion with
+ // map types.
+ types::for_each(all_containers<int, int>{}, []<class From>() {
+ types::for_each(all_containers<int, int>{}, []<class To>() {
+ test_is_permutation<From, To>(pairs);
+ });
+ });
+
+ types::for_each(all_containers<int, int>{}, []<class From>() {
+ types::for_each(all_containers<long, double>{}, []<class To>() {
+ test_is_permutation<From, To>(pairs);
+ });
+ });
+ }
+
+ { // adaptors <-> adaptors
+ types::for_each(container_adaptors<int>{}, []<class From>() {
+ types::for_each(container_adaptors<int>{}, []<class To>() {
+ test_is_equal_for_adaptors<From, To>(ints);
+ });
+ });
+
+ types::for_each(container_adaptors<int>{}, []<class From>() {
+ types::for_each(container_adaptors<double>{}, []<class To>() {
+ test_is_equal_for_adaptors<From, To>(ints);
+ });
+ });
+ }
+}
+
+int main(int, char**) {
+ test();
+
+ return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 90a064625c93e9..8815992ec2515f 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -841,8 +841,8 @@ def add_version_header(tc):
"forward_list",
"list",
"map",
- "priority_queue",
"queue",
+ "ranges",
"set",
"stack",
"string",
@@ -850,7 +850,6 @@ def add_version_header(tc):
"unordered_set",
"vector",
],
- "unimplemented": True,
},
{
"name": "__cpp_lib_ranges_zip",
More information about the libcxx-commits
mailing list