diff options
author | varconst <varconsteq@gmail.com> | 2023-07-14 20:54:38 -0700 |
---|---|---|
committer | Konstantin Varlamov <varconsteq@gmail.com> | 2023-07-20 22:48:18 -0700 |
commit | c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3 (patch) | |
tree | 96b8650cb23a33c90afc616f4e51b9913d05bfea /libcxx | |
parent | 7baf5d3841ee16df66e218b1b892d3d2a3f0680b (diff) | |
download | llvm-c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3.zip llvm-c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3.tar.gz llvm-c3648f37d0ed24e5a783d4ead4c34c9f4796b3e3.tar.bz2 |
[libc++][ranges] Implement `ranges::to`.
Differential Revision: https://reviews.llvm.org/D142335
Diffstat (limited to 'libcxx')
32 files changed, 1645 insertions, 283 deletions
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 79c14e0..1fdc27b 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 a21e428..2bb18c7 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 0324c0f..0cc0667 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 b8ea096..9d10fc0 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 d569f14..259a021 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 2bba97f..8d03e1b 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 0000000..95c300b --- /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 difference_type = ptrdiff_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 f9b2041..9ff8b67 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 3bf9814..523f7cd 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 87b7924..53d40e7 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 7e4c543..cf4c252 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 0000000..c65b07a --- /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 0000000..b252c2f --- /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 0000000..4da7886 --- /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 4a70a98..4a398e2 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 f21cbbf..b163943 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 df13674..48bff77 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 8599c54..2c17d2a 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 78801b8..fdedd27 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 3758c53b..4a439ae 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 241b66b..271df95 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 373fcb4..cc5af8a 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 5902bdb..b5770f8 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 c3dfb78..743f89d 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 d215aec..a0947e9 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 b812671..6eee936 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 94d2838..ec226f3 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 0000000..fafccac --- /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 different 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 0000000..75f55bc --- /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 difference_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 0000000..f84cedb --- /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 0000000..4c19ea4 --- /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 90a0646..8815992 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -841,8 +841,8 @@ feature_test_macros = [ "forward_list", "list", "map", - "priority_queue", "queue", + "ranges", "set", "stack", "string", @@ -850,7 +850,6 @@ feature_test_macros = [ "unordered_set", "vector", ], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_zip", |