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