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