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