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

source code of libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp