xref: /llvm-project/libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp (revision 7c721999ca81f22a12ff671291ec0b51397d8ba9)
1c3648f37Svarconst //===----------------------------------------------------------------------===//
2c3648f37Svarconst //
3c3648f37Svarconst // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c3648f37Svarconst // See https://llvm.org/LICENSE.txt for license information.
5c3648f37Svarconst // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c3648f37Svarconst //
7c3648f37Svarconst //===----------------------------------------------------------------------===//
8c3648f37Svarconst 
964addd65SStephan T. Lavavej // MSVC warning C4244: 'argument': conversion from '_Ty' to 'int', possible loss of data
1064addd65SStephan T. Lavavej // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
1164addd65SStephan T. Lavavej 
12c3648f37Svarconst // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
13c3648f37Svarconst 
14c3648f37Svarconst // template<class C, input_range R, class... Args> requires (!view<C>)
15c3648f37Svarconst //   constexpr C to(R&& r, Args&&... args);     // Since C++23
16c3648f37Svarconst 
17c3648f37Svarconst #include <ranges>
18c3648f37Svarconst 
19c3648f37Svarconst #include <algorithm>
20c3648f37Svarconst #include <array>
21c3648f37Svarconst #include <cassert>
22c3648f37Svarconst #include <vector>
23c3648f37Svarconst #include "container.h"
24c3648f37Svarconst #include "test_iterators.h"
258b9a9866SKonstantin Varlamov #include "test_macros.h"
26c3648f37Svarconst #include "test_range.h"
27c3648f37Svarconst 
28c3648f37Svarconst template <class Container, class Range, class... Args>
29c3648f37Svarconst concept HasTo = requires (Range&& range, Args ...args) {
30c3648f37Svarconst   std::ranges::to<Container>(std::forward<Range>(range), std::forward<Args>(args)...);
31c3648f37Svarconst };
32c3648f37Svarconst 
33c3648f37Svarconst struct InputRange {
34c3648f37Svarconst   int x = 0;
35c3648f37Svarconst   constexpr cpp20_input_iterator<int*> begin() {
36c3648f37Svarconst     return cpp20_input_iterator<int*>(&x);
37c3648f37Svarconst   }
38c3648f37Svarconst   constexpr sentinel_wrapper<cpp20_input_iterator<int*>> end() {
39c3648f37Svarconst     return sentinel_wrapper<cpp20_input_iterator<int*>>(begin());
40c3648f37Svarconst   }
41c3648f37Svarconst };
42c3648f37Svarconst static_assert(std::ranges::input_range<InputRange>);
43c3648f37Svarconst 
44c3648f37Svarconst struct common_cpp20_input_iterator {
45c3648f37Svarconst   using value_type = int;
46c3648f37Svarconst   using difference_type = long long;
47c3648f37Svarconst   using iterator_concept = std::input_iterator_tag;
48c3648f37Svarconst   // Deliberately not defining `iterator_category` to make sure this class satisfies the `input_iterator` concept but
49c3648f37Svarconst   // would fail `derived_from<iterator_category, input_iterator_tag>`.
50c3648f37Svarconst 
51c3648f37Svarconst   int x = 0;
52c3648f37Svarconst 
53c3648f37Svarconst   // Copyable so that it can be used as a sentinel against itself.
54c3648f37Svarconst   constexpr decltype(auto) operator*() const { return x; }
55c3648f37Svarconst   constexpr common_cpp20_input_iterator& operator++() { return *this; }
56c3648f37Svarconst   constexpr void operator++(int) {}
57c3648f37Svarconst   constexpr friend bool operator==(common_cpp20_input_iterator, common_cpp20_input_iterator) { return true; }
58c3648f37Svarconst };
59c3648f37Svarconst static_assert(std::input_iterator<common_cpp20_input_iterator>);
60c3648f37Svarconst static_assert(std::sentinel_for<common_cpp20_input_iterator, common_cpp20_input_iterator>);
61c3648f37Svarconst template <class T>
62c3648f37Svarconst concept HasIteratorCategory = requires {
63c3648f37Svarconst   typename std::iterator_traits<T>::iterator_category;
64c3648f37Svarconst };
65c3648f37Svarconst static_assert(!HasIteratorCategory<common_cpp20_input_iterator>);
66c3648f37Svarconst 
67c3648f37Svarconst struct CommonInputRange {
68c3648f37Svarconst   int x = 0;
69c3648f37Svarconst   constexpr common_cpp20_input_iterator begin() { return {}; }
70c3648f37Svarconst   constexpr common_cpp20_input_iterator end() { return begin(); }
71c3648f37Svarconst };
72c3648f37Svarconst static_assert(std::ranges::input_range<CommonInputRange>);
73c3648f37Svarconst static_assert(std::ranges::common_range<CommonInputRange>);
74c3648f37Svarconst 
75c3648f37Svarconst struct CommonRange {
76c3648f37Svarconst   int x = 0;
77c3648f37Svarconst   constexpr forward_iterator<int*> begin() {
78c3648f37Svarconst     return forward_iterator<int*>(&x);
79c3648f37Svarconst   }
80c3648f37Svarconst   constexpr forward_iterator<int*> end() {
81c3648f37Svarconst     return begin();
82c3648f37Svarconst   }
83c3648f37Svarconst };
84c3648f37Svarconst static_assert(std::ranges::input_range<CommonRange>);
85c3648f37Svarconst static_assert(std::ranges::common_range<CommonRange>);
86c3648f37Svarconst 
87c3648f37Svarconst struct NonCommonRange {
88c3648f37Svarconst   int x = 0;
89c3648f37Svarconst   constexpr forward_iterator<int*> begin() {
90c3648f37Svarconst     return forward_iterator<int*>(&x);
91c3648f37Svarconst   }
92c3648f37Svarconst   constexpr sentinel_wrapper<forward_iterator<int*>> end() {
93c3648f37Svarconst     return sentinel_wrapper<forward_iterator<int*>>(begin());
94c3648f37Svarconst   }
95c3648f37Svarconst };
96c3648f37Svarconst static_assert(std::ranges::input_range<NonCommonRange>);
97c3648f37Svarconst static_assert(!std::ranges::common_range<NonCommonRange>);
98c3648f37Svarconst static_assert(std::derived_from<
99c3648f37Svarconst     typename std::iterator_traits<std::ranges::iterator_t<NonCommonRange>>::iterator_category,
100c3648f37Svarconst     std::input_iterator_tag>);
101c3648f37Svarconst 
102c3648f37Svarconst using ContainerT = int;
103c3648f37Svarconst static_assert(!std::ranges::view<ContainerT>);
104c3648f37Svarconst static_assert(HasTo<ContainerT, InputRange>);
105c3648f37Svarconst static_assert(!HasTo<test_view<forward_iterator>, InputRange>);
106c3648f37Svarconst 
107c3648f37Svarconst // Note: it's not possible to check the `input_range` constraint because if it's not satisfied, the pipe adaptor
108c3648f37Svarconst // overload hijacks the call (it takes unconstrained variadic arguments).
109c3648f37Svarconst 
110c3648f37Svarconst // Check the exact constraints for each one of the cases inside `ranges::to`.
111c3648f37Svarconst 
112c3648f37Svarconst struct Empty {};
113c3648f37Svarconst 
114c3648f37Svarconst struct Fallback {
115c3648f37Svarconst   using value_type = int;
116c3648f37Svarconst 
117c3648f37Svarconst   CtrChoice ctr_choice = CtrChoice::Invalid;
118c3648f37Svarconst   int x = 0;
119c3648f37Svarconst 
120c3648f37Svarconst   constexpr Fallback() : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
121c3648f37Svarconst   constexpr Fallback(Empty) : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
122c3648f37Svarconst 
123c3648f37Svarconst   constexpr void push_back(value_type) {}
124c3648f37Svarconst   constexpr value_type* begin() { return &x; }
125c3648f37Svarconst   constexpr value_type* end() { return &x; }
1268b9a9866SKonstantin Varlamov   std::size_t size() const { return 0; }
127c3648f37Svarconst };
128c3648f37Svarconst 
129c3648f37Svarconst struct CtrDirectOrFallback : Fallback {
130c3648f37Svarconst   using Fallback::Fallback;
131c3648f37Svarconst   constexpr CtrDirectOrFallback(InputRange&&, int = 0) { ctr_choice = CtrChoice::DirectCtr; }
132c3648f37Svarconst };
133c3648f37Svarconst 
134c3648f37Svarconst struct CtrFromRangeTOrFallback : Fallback {
135c3648f37Svarconst   using Fallback::Fallback;
136c3648f37Svarconst   constexpr CtrFromRangeTOrFallback(std::from_range_t, InputRange&&, int = 0) { ctr_choice = CtrChoice::FromRangeT; }
137c3648f37Svarconst };
138c3648f37Svarconst 
139c3648f37Svarconst struct CtrBeginEndPairOrFallback : Fallback {
140c3648f37Svarconst   using Fallback::Fallback;
141c3648f37Svarconst   template <class Iter>
142c3648f37Svarconst   constexpr CtrBeginEndPairOrFallback(Iter, Iter, int = 0) { ctr_choice = CtrChoice::BeginEndPair; }
143c3648f37Svarconst };
144c3648f37Svarconst 
145c3648f37Svarconst template <bool HasSize>
146c3648f37Svarconst struct MaybeSizedRange {
147c3648f37Svarconst   int x = 0;
148c3648f37Svarconst   constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&x); }
149c3648f37Svarconst   constexpr forward_iterator<int*> end() { return begin(); }
150c3648f37Svarconst 
151c3648f37Svarconst   constexpr std::size_t size() const
152c3648f37Svarconst   requires HasSize {
153c3648f37Svarconst     return 0;
154c3648f37Svarconst   }
155c3648f37Svarconst };
156c3648f37Svarconst static_assert(std::ranges::sized_range<MaybeSizedRange<true>>);
157c3648f37Svarconst static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>);
158c3648f37Svarconst 
159c3648f37Svarconst template <bool HasCapacity = true, bool CapacityReturnsSizeT = true,
160c3648f37Svarconst           bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true>
161c3648f37Svarconst struct Reservable : Fallback {
162c3648f37Svarconst   bool reserve_called = false;
163c3648f37Svarconst 
164c3648f37Svarconst   using Fallback::Fallback;
165c3648f37Svarconst 
166c3648f37Svarconst   constexpr std::size_t capacity() const
167c3648f37Svarconst   requires (HasCapacity && CapacityReturnsSizeT) {
168c3648f37Svarconst     return 0;
169c3648f37Svarconst   }
170c3648f37Svarconst   constexpr int capacity() const
171c3648f37Svarconst   requires (HasCapacity && !CapacityReturnsSizeT) {
172c3648f37Svarconst     return 0;
173c3648f37Svarconst   }
174c3648f37Svarconst 
175c3648f37Svarconst   constexpr std::size_t max_size() const
176c3648f37Svarconst   requires (HasMaxSize && MaxSizeReturnsSizeT) {
177c3648f37Svarconst     return 0;
178c3648f37Svarconst   }
179c3648f37Svarconst   constexpr int max_size() const
180c3648f37Svarconst   requires (HasMaxSize && !MaxSizeReturnsSizeT) {
181c3648f37Svarconst     return 0;
182c3648f37Svarconst   }
183c3648f37Svarconst 
184c3648f37Svarconst   constexpr void reserve(std::size_t) {
185c3648f37Svarconst     reserve_called = true;
186c3648f37Svarconst   }
187c3648f37Svarconst };
1888b9a9866SKonstantin Varlamov LIBCPP_STATIC_ASSERT(std::ranges::__reservable_container<Reservable<>>);
189c3648f37Svarconst 
190c3648f37Svarconst constexpr void test_constraints() {
191c3648f37Svarconst   { // Case 1 -- construct directly from the range.
192c3648f37Svarconst     { // (range)
193c3648f37Svarconst       auto result = std::ranges::to<CtrDirectOrFallback>(InputRange());
194c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DirectCtr);
195c3648f37Svarconst     }
196c3648f37Svarconst 
197c3648f37Svarconst     { // (range, arg)
198c3648f37Svarconst       auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1);
199c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DirectCtr);
200c3648f37Svarconst     }
201c3648f37Svarconst 
202c3648f37Svarconst     { // (range, convertible-to-arg)
203c3648f37Svarconst       auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1.0);
204c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DirectCtr);
205c3648f37Svarconst     }
206c3648f37Svarconst 
207c3648f37Svarconst     { // (range, BAD_arg)
208c3648f37Svarconst       auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), Empty());
209c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
210c3648f37Svarconst     }
211c3648f37Svarconst   }
212c3648f37Svarconst 
213c3648f37Svarconst   { // Case 2 -- construct using the `from_range_t` tagged constructor.
214c3648f37Svarconst     { // (range)
215c3648f37Svarconst       auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange());
216c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::FromRangeT);
217c3648f37Svarconst     }
218c3648f37Svarconst 
219c3648f37Svarconst     { // (range, arg)
220c3648f37Svarconst       auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1);
221c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::FromRangeT);
222c3648f37Svarconst     }
223c3648f37Svarconst 
224c3648f37Svarconst     { // (range, convertible-to-arg)
225c3648f37Svarconst       auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1.0);
226c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::FromRangeT);
227c3648f37Svarconst     }
228c3648f37Svarconst 
229c3648f37Svarconst     { // (range, BAD_arg)
230c3648f37Svarconst       auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), Empty());
231c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
232c3648f37Svarconst     }
233c3648f37Svarconst   }
234c3648f37Svarconst 
235c3648f37Svarconst   { // Case 3 -- construct from a begin-end iterator pair.
236c3648f37Svarconst     { // (range)
237c3648f37Svarconst       auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange());
238c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::BeginEndPair);
239c3648f37Svarconst     }
240c3648f37Svarconst 
241c3648f37Svarconst     { // (range, arg)
242c3648f37Svarconst       auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1);
243c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::BeginEndPair);
244c3648f37Svarconst     }
245c3648f37Svarconst 
246c3648f37Svarconst     { // (range, convertible-to-arg)
247c3648f37Svarconst       auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1.0);
248c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::BeginEndPair);
249c3648f37Svarconst     }
250c3648f37Svarconst 
251c3648f37Svarconst     { // (BAD_range) -- not a common range.
252c3648f37Svarconst       auto result = std::ranges::to<CtrBeginEndPairOrFallback>(NonCommonRange());
253c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
254c3648f37Svarconst     }
255c3648f37Svarconst 
256c3648f37Svarconst     { // (BAD_range) -- iterator type not derived from `input_iterator_tag`.
257c3648f37Svarconst       auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonInputRange());
258c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
259c3648f37Svarconst     }
260c3648f37Svarconst 
261c3648f37Svarconst     { // (range, BAD_arg)
262c3648f37Svarconst       auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), Empty());
263c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
264c3648f37Svarconst     }
265c3648f37Svarconst   }
266c3648f37Svarconst 
267c3648f37Svarconst   { // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
268c3648f37Svarconst     // Note: it's not possible to check the constraints on the default constructor using this approach because there is
269c3648f37Svarconst     // nothing to fall back to -- the call will result in a hard error.
270c3648f37Svarconst     // However, it's possible to check the constraints on reserving the capacity.
271c3648f37Svarconst 
272c3648f37Svarconst     { // All constraints satisfied.
273c3648f37Svarconst       using C = Reservable<>;
274c3648f37Svarconst       auto result = std::ranges::to<C>(MaybeSizedRange<true>());
275c3648f37Svarconst       assert(result.reserve_called);
276c3648f37Svarconst     }
277c3648f37Svarconst 
278c3648f37Svarconst     { // !sized_range
279c3648f37Svarconst       using C = Reservable<>;
280c3648f37Svarconst       auto result = std::ranges::to<C>(MaybeSizedRange<false>());
281c3648f37Svarconst       assert(!result.reserve_called);
282c3648f37Svarconst     }
283c3648f37Svarconst 
284c3648f37Svarconst     { // Missing `capacity`.
285c3648f37Svarconst       using C = Reservable</*HasCapacity=*/false>;
286c3648f37Svarconst       auto result = std::ranges::to<C>(MaybeSizedRange<true>());
287c3648f37Svarconst       assert(!result.reserve_called);
288c3648f37Svarconst     }
289c3648f37Svarconst 
290c3648f37Svarconst     { // `capacity` doesn't return `size_type`.
291c3648f37Svarconst       using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/false>;
292c3648f37Svarconst       auto result = std::ranges::to<C>(MaybeSizedRange<true>());
293c3648f37Svarconst       assert(!result.reserve_called);
294c3648f37Svarconst     }
295c3648f37Svarconst 
296c3648f37Svarconst     { // Missing `max_size`.
297c3648f37Svarconst       using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/false>;
298c3648f37Svarconst       auto result = std::ranges::to<C>(MaybeSizedRange<true>());
299c3648f37Svarconst       assert(!result.reserve_called);
300c3648f37Svarconst     }
301c3648f37Svarconst 
302c3648f37Svarconst     { // `max_size` doesn't return `size_type`.
303c3648f37Svarconst       using C = Reservable<
304c3648f37Svarconst         /*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/true, /*MaxSizeReturnsSizeT=*/false>;
305c3648f37Svarconst       auto result = std::ranges::to<C>(MaybeSizedRange<true>());
306c3648f37Svarconst       assert(!result.reserve_called);
307c3648f37Svarconst     }
308c3648f37Svarconst   }
309c3648f37Svarconst }
310c3648f37Svarconst 
311c3648f37Svarconst constexpr void test_ctr_choice_order() {
312c3648f37Svarconst   std::array in = {1, 2, 3, 4, 5};
313c3648f37Svarconst   int arg1 = 42;
314c3648f37Svarconst   char arg2 = 'a';
315c3648f37Svarconst 
316c3648f37Svarconst   { // Case 1 -- construct directly from the given range.
317c3648f37Svarconst     {
318c3648f37Svarconst       using C = Container<int, CtrChoice::DirectCtr>;
319c3648f37Svarconst       std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
320c3648f37Svarconst 
321c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DirectCtr);
322c3648f37Svarconst       assert(std::ranges::equal(result, in));
323c3648f37Svarconst       assert((in | std::ranges::to<C>()) == result);
324c3648f37Svarconst       auto closure = std::ranges::to<C>();
325c3648f37Svarconst       assert((in | closure) == result);
326c3648f37Svarconst     }
327c3648f37Svarconst 
328c3648f37Svarconst     { // Extra arguments.
329c3648f37Svarconst       using C = Container<int, CtrChoice::DirectCtr>;
330c3648f37Svarconst       std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
331c3648f37Svarconst 
332c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::DirectCtr);
333c3648f37Svarconst       assert(std::ranges::equal(result, in));
334c3648f37Svarconst       assert(result.extra_arg1 == arg1);
335c3648f37Svarconst       assert(result.extra_arg2 == arg2);
336c3648f37Svarconst       assert((in | std::ranges::to<C>(arg1, arg2)) == result);
337c3648f37Svarconst       auto closure = std::ranges::to<C>(arg1, arg2);
338c3648f37Svarconst       assert((in | closure) == result);
339c3648f37Svarconst     }
340c3648f37Svarconst   }
341c3648f37Svarconst 
342c3648f37Svarconst   { // Case 2 -- construct using the `from_range_t` tag.
343c3648f37Svarconst     {
344c3648f37Svarconst       using C = Container<int, CtrChoice::FromRangeT>;
345c3648f37Svarconst       std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
346c3648f37Svarconst 
347c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::FromRangeT);
348c3648f37Svarconst       assert(std::ranges::equal(result, in));
349c3648f37Svarconst       assert((in | std::ranges::to<C>()) == result);
350c3648f37Svarconst       auto closure = std::ranges::to<C>();
351c3648f37Svarconst       assert((in | closure) == result);
352c3648f37Svarconst     }
353c3648f37Svarconst 
354c3648f37Svarconst     { // Extra arguments.
355c3648f37Svarconst       using C = Container<int, CtrChoice::FromRangeT>;
356c3648f37Svarconst       std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
357c3648f37Svarconst 
358c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::FromRangeT);
359c3648f37Svarconst       assert(std::ranges::equal(result, in));
360c3648f37Svarconst       assert(result.extra_arg1 == arg1);
361c3648f37Svarconst       assert(result.extra_arg2 == arg2);
362c3648f37Svarconst       assert((in | std::ranges::to<C>(arg1, arg2)) == result);
363c3648f37Svarconst       auto closure = std::ranges::to<C>(arg1, arg2);
364c3648f37Svarconst       assert((in | closure) == result);
365c3648f37Svarconst     }
366c3648f37Svarconst   }
367c3648f37Svarconst 
368c3648f37Svarconst   { // Case 3 -- construct from a begin-end pair.
369c3648f37Svarconst     {
370c3648f37Svarconst       using C = Container<int, CtrChoice::BeginEndPair>;
371c3648f37Svarconst       std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
372c3648f37Svarconst 
373c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::BeginEndPair);
374c3648f37Svarconst       assert(std::ranges::equal(result, in));
375c3648f37Svarconst       assert((in | std::ranges::to<C>()) == result);
376c3648f37Svarconst       auto closure = std::ranges::to<C>();
377c3648f37Svarconst       assert((in | closure) == result);
378c3648f37Svarconst     }
379c3648f37Svarconst 
380c3648f37Svarconst     { // Extra arguments.
381c3648f37Svarconst       using C = Container<int, CtrChoice::BeginEndPair>;
382c3648f37Svarconst       std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
383c3648f37Svarconst 
384c3648f37Svarconst       assert(result.ctr_choice == CtrChoice::BeginEndPair);
385c3648f37Svarconst       assert(std::ranges::equal(result, in));
386c3648f37Svarconst       assert(result.extra_arg1 == arg1);
387c3648f37Svarconst       assert(result.extra_arg2 == arg2);
388c3648f37Svarconst       assert((in | std::ranges::to<C>(arg1, arg2)) == result);
389c3648f37Svarconst       auto closure = std::ranges::to<C>(arg1, arg2);
390c3648f37Svarconst       assert((in | closure) == result);
391c3648f37Svarconst     }
392c3648f37Svarconst   }
393c3648f37Svarconst 
394c3648f37Svarconst   { // Case 4 -- default-construct then insert elements.
395*7c721999SXiaoyang Liu     auto case_4 = [in, arg1, arg2]<auto InserterChoice, bool CanReserve>() {
396*7c721999SXiaoyang Liu       using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice, CanReserve>;
397c3648f37Svarconst       {
398*7c721999SXiaoyang Liu         [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
399c3648f37Svarconst 
400c3648f37Svarconst         assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
401*7c721999SXiaoyang Liu         assert(result.inserter_choice == InserterChoice);
402c3648f37Svarconst         assert(std::ranges::equal(result, in));
403c3648f37Svarconst 
404*7c721999SXiaoyang Liu         if constexpr (CanReserve) {
405c3648f37Svarconst           assert(result.called_reserve);
406*7c721999SXiaoyang Liu         } else {
407c3648f37Svarconst           assert(!result.called_reserve);
408*7c721999SXiaoyang Liu         }
409*7c721999SXiaoyang Liu 
410c3648f37Svarconst         assert((in | std::ranges::to<C>()) == result);
411*7c721999SXiaoyang Liu         [[maybe_unused]] auto closure = std::ranges::to<C>();
412c3648f37Svarconst         assert((in | closure) == result);
413c3648f37Svarconst       }
414c3648f37Svarconst 
415*7c721999SXiaoyang Liu       { // Extra arguments
416*7c721999SXiaoyang Liu         [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
417c3648f37Svarconst 
418c3648f37Svarconst         assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
419*7c721999SXiaoyang Liu         assert(result.inserter_choice == InserterChoice);
420c3648f37Svarconst         assert(std::ranges::equal(result, in));
421c3648f37Svarconst         assert(result.extra_arg1 == arg1);
422c3648f37Svarconst         assert(result.extra_arg2 == arg2);
423*7c721999SXiaoyang Liu 
424*7c721999SXiaoyang Liu         if constexpr (CanReserve) {
425*7c721999SXiaoyang Liu           assert(result.called_reserve);
426*7c721999SXiaoyang Liu         } else {
427*7c721999SXiaoyang Liu           assert(!result.called_reserve);
428*7c721999SXiaoyang Liu         }
429*7c721999SXiaoyang Liu 
430c3648f37Svarconst         assert((in | std::ranges::to<C>(arg1, arg2)) == result);
431*7c721999SXiaoyang Liu         [[maybe_unused]] auto closure = std::ranges::to<C>(arg1, arg2);
432c3648f37Svarconst         assert((in | closure) == result);
433c3648f37Svarconst       }
434*7c721999SXiaoyang Liu     };
435*7c721999SXiaoyang Liu 
436*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::Insert, false>();
437*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::Insert, true>();
438*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::Emplace, false>();
439*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::Emplace, true>();
440*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::PushBack, false>();
441*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::PushBack, true>();
442*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::EmplaceBack, false>();
443*7c721999SXiaoyang Liu     case_4.operator()<InserterChoice::EmplaceBack, true>();
444c3648f37Svarconst   }
445c3648f37Svarconst }
446c3648f37Svarconst 
447c3648f37Svarconst template <CtrChoice Rank>
448c3648f37Svarconst struct NotARange {
449c3648f37Svarconst   using value_type = int;
450c3648f37Svarconst 
451c3648f37Svarconst   constexpr NotARange(std::ranges::input_range auto&&)
452c3648f37Svarconst   requires (Rank >= CtrChoice::DirectCtr)
453c3648f37Svarconst   {}
454c3648f37Svarconst 
455c3648f37Svarconst   constexpr NotARange(std::from_range_t, std::ranges::input_range auto&&)
456c3648f37Svarconst   requires (Rank >= CtrChoice::FromRangeT)
457c3648f37Svarconst   {}
458c3648f37Svarconst 
459c3648f37Svarconst   template <class Iter>
460c3648f37Svarconst   constexpr NotARange(Iter, Iter)
461c3648f37Svarconst   requires (Rank >= CtrChoice::BeginEndPair)
462c3648f37Svarconst   {}
463c3648f37Svarconst 
464c3648f37Svarconst   constexpr NotARange()
465c3648f37Svarconst   requires (Rank >= CtrChoice::DefaultCtrAndInsert)
466c3648f37Svarconst   = default;
467c3648f37Svarconst 
468c3648f37Svarconst   constexpr void push_back(int) {}
469c3648f37Svarconst };
470c3648f37Svarconst 
471c3648f37Svarconst static_assert(!std::ranges::range<NotARange<CtrChoice::DirectCtr>>);
472c3648f37Svarconst 
473c3648f37Svarconst constexpr void test_lwg_3785() {
474c3648f37Svarconst   // Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible
475c3648f37Svarconst   // to convert the given input range to a non-range type.
476c3648f37Svarconst   std::array in = {1, 2, 3, 4, 5};
477c3648f37Svarconst 
478c3648f37Svarconst   {
479c3648f37Svarconst     using C = NotARange<CtrChoice::DirectCtr>;
480c3648f37Svarconst     [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
481c3648f37Svarconst   }
482c3648f37Svarconst 
483c3648f37Svarconst   {
484c3648f37Svarconst     using C = NotARange<CtrChoice::FromRangeT>;
485c3648f37Svarconst     [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
486c3648f37Svarconst   }
487c3648f37Svarconst 
488c3648f37Svarconst   {
489c3648f37Svarconst     using C = NotARange<CtrChoice::BeginEndPair>;
490c3648f37Svarconst     [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
491c3648f37Svarconst   }
492c3648f37Svarconst 
493c3648f37Svarconst   {
494c3648f37Svarconst     using C = NotARange<CtrChoice::DefaultCtrAndInsert>;
495c3648f37Svarconst     [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
496c3648f37Svarconst   }
497c3648f37Svarconst }
498c3648f37Svarconst 
499c3648f37Svarconst constexpr void test_recursive() {
500c3648f37Svarconst   using C1 = Container<int, CtrChoice::DirectCtr>;
501c3648f37Svarconst   using C2 = Container<C1, CtrChoice::FromRangeT>;
502c3648f37Svarconst   using C3 = Container<C2, CtrChoice::BeginEndPair>;
503c3648f37Svarconst   using C4 = Container<C3, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack>;
504c3648f37Svarconst   using A1 = std::array<int, 4>;
505c3648f37Svarconst   using A2 = std::array<A1, 3>;
506c3648f37Svarconst   using A3 = std::array<A2, 2>;
507c3648f37Svarconst   using A4 = std::array<A3, 2>;
508c3648f37Svarconst 
509c3648f37Svarconst   A4 in = {};
510c3648f37Svarconst   { // Fill the nested array with incremental values.
511c3648f37Svarconst     int x = 0;
512c3648f37Svarconst     for (auto& a3 : in) {
513c3648f37Svarconst       for (auto& a2 : a3) {
514c3648f37Svarconst         for (auto& a1 : a2) {
515c3648f37Svarconst           for (int& el : a1) {
516c3648f37Svarconst             el = x++;
517c3648f37Svarconst           }
518c3648f37Svarconst         }
519c3648f37Svarconst       }
520c3648f37Svarconst     }
521c3648f37Svarconst   }
522c3648f37Svarconst 
523c3648f37Svarconst   std::same_as<C4> decltype(auto) result = std::ranges::to<C4>(in);
524c3648f37Svarconst 
525c3648f37Svarconst   assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
526c3648f37Svarconst 
527c3648f37Svarconst   int expected_value = 0;
528c3648f37Svarconst   for (auto& c3 : result) {
529c3648f37Svarconst     assert(c3.ctr_choice == CtrChoice::BeginEndPair);
530c3648f37Svarconst 
531c3648f37Svarconst     for (auto& c2 : c3) {
532c3648f37Svarconst       assert(c2.ctr_choice == CtrChoice::FromRangeT);
533c3648f37Svarconst 
534c3648f37Svarconst       for (auto& c1 : c2) {
535c3648f37Svarconst         assert(c1.ctr_choice == CtrChoice::DirectCtr);
536c3648f37Svarconst 
537c3648f37Svarconst         for (int el : c1) {
538c3648f37Svarconst           assert(el == expected_value);
539c3648f37Svarconst           ++expected_value;
540c3648f37Svarconst         }
541c3648f37Svarconst       }
542c3648f37Svarconst     }
543c3648f37Svarconst   }
544c3648f37Svarconst 
545c3648f37Svarconst   assert((in | std::ranges::to<C4>()) == result);
546e74be35cSXiaoyang Liu 
547e74be35cSXiaoyang Liu   // LWG3984: ranges::to's recursion branch may be ill-formed
548e74be35cSXiaoyang Liu   auto in_owning_view = std::views::all(std::move(in));
549e74be35cSXiaoyang Liu   static_assert(!std::ranges::viewable_range<decltype((in_owning_view))>);
550e74be35cSXiaoyang Liu   assert(std::ranges::to<C4>(in_owning_view) == result);
551c3648f37Svarconst }
552c3648f37Svarconst 
553c3648f37Svarconst constexpr bool test() {
554c3648f37Svarconst   test_constraints();
555c3648f37Svarconst   test_ctr_choice_order();
556c3648f37Svarconst   test_lwg_3785();
557c3648f37Svarconst   test_recursive();
558c3648f37Svarconst 
559c3648f37Svarconst   return true;
560c3648f37Svarconst }
561c3648f37Svarconst 
562c3648f37Svarconst int main(int, char**) {
563c3648f37Svarconst   test();
564c3648f37Svarconst   static_assert(test());
565c3648f37Svarconst 
566c3648f37Svarconst   return 0;
567c3648f37Svarconst }
568