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 28 template <class Container, class Range, class... Args> 29 concept HasTo = requires (Range&& range, Args ...args) { 30 std::ranges::to<Container>(std::forward<Range>(range), std::forward<Args>(args)...); 31 }; 32 33 struct 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 }; 42 static_assert(std::ranges::input_range<InputRange>); 43 44 struct 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 }; 59 static_assert(std::input_iterator<common_cpp20_input_iterator>); 60 static_assert(std::sentinel_for<common_cpp20_input_iterator, common_cpp20_input_iterator>); 61 template <class T> 62 concept HasIteratorCategory = requires { 63 typename std::iterator_traits<T>::iterator_category; 64 }; 65 static_assert(!HasIteratorCategory<common_cpp20_input_iterator>); 66 67 struct CommonInputRange { 68 int x = 0; 69 constexpr common_cpp20_input_iterator begin() { return {}; } 70 constexpr common_cpp20_input_iterator end() { return begin(); } 71 }; 72 static_assert(std::ranges::input_range<CommonInputRange>); 73 static_assert(std::ranges::common_range<CommonInputRange>); 74 75 struct 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 }; 84 static_assert(std::ranges::input_range<CommonRange>); 85 static_assert(std::ranges::common_range<CommonRange>); 86 87 struct 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 }; 96 static_assert(std::ranges::input_range<NonCommonRange>); 97 static_assert(!std::ranges::common_range<NonCommonRange>); 98 static_assert(std::derived_from< 99 typename std::iterator_traits<std::ranges::iterator_t<NonCommonRange>>::iterator_category, 100 std::input_iterator_tag>); 101 102 using ContainerT = int; 103 static_assert(!std::ranges::view<ContainerT>); 104 static_assert(HasTo<ContainerT, InputRange>); 105 static_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 112 struct Empty {}; 113 114 struct 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 129 struct CtrDirectOrFallback : Fallback { 130 using Fallback::Fallback; 131 constexpr CtrDirectOrFallback(InputRange&&, int = 0) { ctr_choice = CtrChoice::DirectCtr; } 132 }; 133 134 struct CtrFromRangeTOrFallback : Fallback { 135 using Fallback::Fallback; 136 constexpr CtrFromRangeTOrFallback(std::from_range_t, InputRange&&, int = 0) { ctr_choice = CtrChoice::FromRangeT; } 137 }; 138 139 struct CtrBeginEndPairOrFallback : Fallback { 140 using Fallback::Fallback; 141 template <class Iter> 142 constexpr CtrBeginEndPairOrFallback(Iter, Iter, int = 0) { ctr_choice = CtrChoice::BeginEndPair; } 143 }; 144 145 template <bool HasSize> 146 struct 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 }; 156 static_assert(std::ranges::sized_range<MaybeSizedRange<true>>); 157 static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>); 158 159 template <bool HasCapacity = true, bool CapacityReturnsSizeT = true, 160 bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true> 161 struct 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 }; 188 LIBCPP_STATIC_ASSERT(std::ranges::__reservable_container<Reservable<>>); 189 190 constexpr 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 311 constexpr 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 auto case_4 = [in, arg1, arg2]<auto InserterChoice, bool CanReserve>() { 396 using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice, CanReserve>; 397 { 398 [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 399 400 assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 401 assert(result.inserter_choice == InserterChoice); 402 assert(std::ranges::equal(result, in)); 403 404 if constexpr (CanReserve) { 405 assert(result.called_reserve); 406 } else { 407 assert(!result.called_reserve); 408 } 409 410 assert((in | std::ranges::to<C>()) == result); 411 [[maybe_unused]] auto closure = std::ranges::to<C>(); 412 assert((in | closure) == result); 413 } 414 415 { // Extra arguments 416 [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2); 417 418 assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 419 assert(result.inserter_choice == InserterChoice); 420 assert(std::ranges::equal(result, in)); 421 assert(result.extra_arg1 == arg1); 422 assert(result.extra_arg2 == arg2); 423 424 if constexpr (CanReserve) { 425 assert(result.called_reserve); 426 } else { 427 assert(!result.called_reserve); 428 } 429 430 assert((in | std::ranges::to<C>(arg1, arg2)) == result); 431 [[maybe_unused]] auto closure = std::ranges::to<C>(arg1, arg2); 432 assert((in | closure) == result); 433 } 434 }; 435 436 case_4.operator()<InserterChoice::Insert, false>(); 437 case_4.operator()<InserterChoice::Insert, true>(); 438 case_4.operator()<InserterChoice::Emplace, false>(); 439 case_4.operator()<InserterChoice::Emplace, true>(); 440 case_4.operator()<InserterChoice::PushBack, false>(); 441 case_4.operator()<InserterChoice::PushBack, true>(); 442 case_4.operator()<InserterChoice::EmplaceBack, false>(); 443 case_4.operator()<InserterChoice::EmplaceBack, true>(); 444 } 445 } 446 447 template <CtrChoice Rank> 448 struct NotARange { 449 using value_type = int; 450 451 constexpr NotARange(std::ranges::input_range auto&&) 452 requires (Rank >= CtrChoice::DirectCtr) 453 {} 454 455 constexpr NotARange(std::from_range_t, std::ranges::input_range auto&&) 456 requires (Rank >= CtrChoice::FromRangeT) 457 {} 458 459 template <class Iter> 460 constexpr NotARange(Iter, Iter) 461 requires (Rank >= CtrChoice::BeginEndPair) 462 {} 463 464 constexpr NotARange() 465 requires (Rank >= CtrChoice::DefaultCtrAndInsert) 466 = default; 467 468 constexpr void push_back(int) {} 469 }; 470 471 static_assert(!std::ranges::range<NotARange<CtrChoice::DirectCtr>>); 472 473 constexpr void test_lwg_3785() { 474 // Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible 475 // to convert the given input range to a non-range type. 476 std::array in = {1, 2, 3, 4, 5}; 477 478 { 479 using C = NotARange<CtrChoice::DirectCtr>; 480 [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 481 } 482 483 { 484 using C = NotARange<CtrChoice::FromRangeT>; 485 [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 486 } 487 488 { 489 using C = NotARange<CtrChoice::BeginEndPair>; 490 [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 491 } 492 493 { 494 using C = NotARange<CtrChoice::DefaultCtrAndInsert>; 495 [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 496 } 497 } 498 499 constexpr void test_recursive() { 500 using C1 = Container<int, CtrChoice::DirectCtr>; 501 using C2 = Container<C1, CtrChoice::FromRangeT>; 502 using C3 = Container<C2, CtrChoice::BeginEndPair>; 503 using C4 = Container<C3, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack>; 504 using A1 = std::array<int, 4>; 505 using A2 = std::array<A1, 3>; 506 using A3 = std::array<A2, 2>; 507 using A4 = std::array<A3, 2>; 508 509 A4 in = {}; 510 { // Fill the nested array with incremental values. 511 int x = 0; 512 for (auto& a3 : in) { 513 for (auto& a2 : a3) { 514 for (auto& a1 : a2) { 515 for (int& el : a1) { 516 el = x++; 517 } 518 } 519 } 520 } 521 } 522 523 std::same_as<C4> decltype(auto) result = std::ranges::to<C4>(in); 524 525 assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 526 527 int expected_value = 0; 528 for (auto& c3 : result) { 529 assert(c3.ctr_choice == CtrChoice::BeginEndPair); 530 531 for (auto& c2 : c3) { 532 assert(c2.ctr_choice == CtrChoice::FromRangeT); 533 534 for (auto& c1 : c2) { 535 assert(c1.ctr_choice == CtrChoice::DirectCtr); 536 537 for (int el : c1) { 538 assert(el == expected_value); 539 ++expected_value; 540 } 541 } 542 } 543 } 544 545 assert((in | std::ranges::to<C4>()) == result); 546 547 // LWG3984: ranges::to's recursion branch may be ill-formed 548 auto in_owning_view = std::views::all(std::move(in)); 549 static_assert(!std::ranges::viewable_range<decltype((in_owning_view))>); 550 assert(std::ranges::to<C4>(in_owning_view) == result); 551 } 552 553 constexpr bool test() { 554 test_constraints(); 555 test_ctr_choice_order(); 556 test_lwg_3785(); 557 test_recursive(); 558 559 return true; 560 } 561 562 int main(int, char**) { 563 test(); 564 static_assert(test()); 565 566 return 0; 567 } 568