swap.pass.cpp (18157B)
1 // -*- C++ -*- 2 //===----------------------------------------------------------------------===// 3 // 4 // The LLVM Compiler Infrastructure 5 // 6 // This file is dual licensed under the MIT and the University of Illinois Open 7 // Source Licenses. See LICENSE.TXT for details. 8 // 9 //===----------------------------------------------------------------------===// 10 11 // UNSUPPORTED: c++98, c++03, c++11, c++14 12 13 // XFAIL: availability=macosx10.13 14 // XFAIL: availability=macosx10.12 15 // XFAIL: availability=macosx10.11 16 // XFAIL: availability=macosx10.10 17 // XFAIL: availability=macosx10.9 18 // XFAIL: availability=macosx10.8 19 // XFAIL: availability=macosx10.7 20 21 // <variant> 22 23 // template <class ...Types> class variant; 24 25 // void swap(variant& rhs) noexcept(see below) 26 27 #include <cassert> 28 #include <string> 29 #include <type_traits> 30 #include <variant> 31 32 #include "test_convertible.hpp" 33 #include "test_macros.h" 34 #include "variant_test_helpers.hpp" 35 36 struct NotSwappable {}; 37 void swap(NotSwappable &, NotSwappable &) = delete; 38 39 struct NotCopyable { 40 NotCopyable() = default; 41 NotCopyable(const NotCopyable &) = delete; 42 NotCopyable &operator=(const NotCopyable &) = delete; 43 }; 44 45 struct NotCopyableWithSwap { 46 NotCopyableWithSwap() = default; 47 NotCopyableWithSwap(const NotCopyableWithSwap &) = delete; 48 NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete; 49 }; 50 void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {} 51 52 struct NotMoveAssignable { 53 NotMoveAssignable() = default; 54 NotMoveAssignable(NotMoveAssignable &&) = default; 55 NotMoveAssignable &operator=(NotMoveAssignable &&) = delete; 56 }; 57 58 struct NotMoveAssignableWithSwap { 59 NotMoveAssignableWithSwap() = default; 60 NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default; 61 NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete; 62 }; 63 void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {} 64 65 template <bool Throws> void do_throw() {} 66 67 template <> void do_throw<true>() { 68 #ifndef TEST_HAS_NO_EXCEPTIONS 69 throw 42; 70 #else 71 std::abort(); 72 #endif 73 } 74 75 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, 76 bool NT_Swap, bool EnableSwap = true> 77 struct NothrowTypeImp { 78 static int move_called; 79 static int move_assign_called; 80 static int swap_called; 81 static void reset() { move_called = move_assign_called = swap_called = 0; } 82 NothrowTypeImp() = default; 83 explicit NothrowTypeImp(int v) : value(v) {} 84 NothrowTypeImp(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) { 85 assert(false); 86 } // never called by test 87 NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) { 88 ++move_called; 89 do_throw<!NT_Move>(); 90 o.value = -1; 91 } 92 NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) { 93 assert(false); 94 return *this; 95 } // never called by the tests 96 NothrowTypeImp &operator=(NothrowTypeImp &&o) noexcept(NT_MoveAssign) { 97 ++move_assign_called; 98 do_throw<!NT_MoveAssign>(); 99 value = o.value; 100 o.value = -1; 101 return *this; 102 } 103 int value; 104 }; 105 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, 106 bool NT_Swap, bool EnableSwap> 107 int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, 108 EnableSwap>::move_called = 0; 109 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, 110 bool NT_Swap, bool EnableSwap> 111 int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, 112 EnableSwap>::move_assign_called = 0; 113 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, 114 bool NT_Swap, bool EnableSwap> 115 int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, 116 EnableSwap>::swap_called = 0; 117 118 template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, 119 bool NT_Swap> 120 void swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, 121 NT_Swap, true> &lhs, 122 NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, 123 NT_Swap, true> &rhs) noexcept(NT_Swap) { 124 lhs.swap_called++; 125 do_throw<!NT_Swap>(); 126 int tmp = lhs.value; 127 lhs.value = rhs.value; 128 rhs.value = tmp; 129 } 130 131 // throwing copy, nothrow move ctor/assign, no swap provided 132 using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>; 133 // throwing copy and move assign, nothrow move ctor, no swap provided 134 using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>; 135 // nothrow move ctor, throwing move assignment, swap provided 136 using NothrowMoveCtorWithThrowingSwap = 137 NothrowTypeImp<false, true, false, false, false, true>; 138 // throwing move ctor, nothrow move assignment, no swap provided 139 using ThrowingMoveCtor = 140 NothrowTypeImp<false, false, false, true, false, false>; 141 // throwing special members, nothrowing swap 142 using ThrowingTypeWithNothrowSwap = 143 NothrowTypeImp<false, false, false, false, true, true>; 144 using NothrowTypeWithThrowingSwap = 145 NothrowTypeImp<true, true, true, true, false, true>; 146 // throwing move assign with nothrow move and nothrow swap 147 using ThrowingMoveAssignNothrowMoveCtorWithSwap = 148 NothrowTypeImp<false, true, false, false, true, true>; 149 // throwing move assign with nothrow move but no swap. 150 using ThrowingMoveAssignNothrowMoveCtor = 151 NothrowTypeImp<false, true, false, false, false, false>; 152 153 struct NonThrowingNonNoexceptType { 154 static int move_called; 155 static void reset() { move_called = 0; } 156 NonThrowingNonNoexceptType() = default; 157 NonThrowingNonNoexceptType(int v) : value(v) {} 158 NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false) 159 : value(o.value) { 160 ++move_called; 161 o.value = -1; 162 } 163 NonThrowingNonNoexceptType & 164 operator=(NonThrowingNonNoexceptType &&) noexcept(false) { 165 assert(false); // never called by the tests. 166 return *this; 167 } 168 int value; 169 }; 170 int NonThrowingNonNoexceptType::move_called = 0; 171 172 struct ThrowsOnSecondMove { 173 int value; 174 int move_count; 175 ThrowsOnSecondMove(int v) : value(v), move_count(0) {} 176 ThrowsOnSecondMove(ThrowsOnSecondMove &&o) noexcept(false) 177 : value(o.value), move_count(o.move_count + 1) { 178 if (move_count == 2) 179 do_throw<true>(); 180 o.value = -1; 181 } 182 ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) { 183 assert(false); // not called by test 184 return *this; 185 } 186 }; 187 188 void test_swap_valueless_by_exception() { 189 #ifndef TEST_HAS_NO_EXCEPTIONS 190 using V = std::variant<int, MakeEmptyT>; 191 { // both empty 192 V v1; 193 makeEmpty(v1); 194 V v2; 195 makeEmpty(v2); 196 assert(MakeEmptyT::alive == 0); 197 { // member swap 198 v1.swap(v2); 199 assert(v1.valueless_by_exception()); 200 assert(v2.valueless_by_exception()); 201 assert(MakeEmptyT::alive == 0); 202 } 203 { // non-member swap 204 swap(v1, v2); 205 assert(v1.valueless_by_exception()); 206 assert(v2.valueless_by_exception()); 207 assert(MakeEmptyT::alive == 0); 208 } 209 } 210 { // only one empty 211 V v1(42); 212 V v2; 213 makeEmpty(v2); 214 { // member swap 215 v1.swap(v2); 216 assert(v1.valueless_by_exception()); 217 assert(std::get<0>(v2) == 42); 218 // swap again 219 v2.swap(v1); 220 assert(v2.valueless_by_exception()); 221 assert(std::get<0>(v1) == 42); 222 } 223 { // non-member swap 224 swap(v1, v2); 225 assert(v1.valueless_by_exception()); 226 assert(std::get<0>(v2) == 42); 227 // swap again 228 swap(v1, v2); 229 assert(v2.valueless_by_exception()); 230 assert(std::get<0>(v1) == 42); 231 } 232 } 233 #endif 234 } 235 236 void test_swap_same_alternative() { 237 { 238 using T = ThrowingTypeWithNothrowSwap; 239 using V = std::variant<T, int>; 240 T::reset(); 241 V v1(std::in_place_index<0>, 42); 242 V v2(std::in_place_index<0>, 100); 243 v1.swap(v2); 244 assert(T::swap_called == 1); 245 assert(std::get<0>(v1).value == 100); 246 assert(std::get<0>(v2).value == 42); 247 swap(v1, v2); 248 assert(T::swap_called == 2); 249 assert(std::get<0>(v1).value == 42); 250 assert(std::get<0>(v2).value == 100); 251 } 252 { 253 using T = NothrowMoveable; 254 using V = std::variant<T, int>; 255 T::reset(); 256 V v1(std::in_place_index<0>, 42); 257 V v2(std::in_place_index<0>, 100); 258 v1.swap(v2); 259 assert(T::swap_called == 0); 260 assert(T::move_called == 1); 261 assert(T::move_assign_called == 2); 262 assert(std::get<0>(v1).value == 100); 263 assert(std::get<0>(v2).value == 42); 264 T::reset(); 265 swap(v1, v2); 266 assert(T::swap_called == 0); 267 assert(T::move_called == 1); 268 assert(T::move_assign_called == 2); 269 assert(std::get<0>(v1).value == 42); 270 assert(std::get<0>(v2).value == 100); 271 } 272 #ifndef TEST_HAS_NO_EXCEPTIONS 273 { 274 using T = NothrowTypeWithThrowingSwap; 275 using V = std::variant<T, int>; 276 T::reset(); 277 V v1(std::in_place_index<0>, 42); 278 V v2(std::in_place_index<0>, 100); 279 try { 280 v1.swap(v2); 281 assert(false); 282 } catch (int) { 283 } 284 assert(T::swap_called == 1); 285 assert(T::move_called == 0); 286 assert(T::move_assign_called == 0); 287 assert(std::get<0>(v1).value == 42); 288 assert(std::get<0>(v2).value == 100); 289 } 290 { 291 using T = ThrowingMoveCtor; 292 using V = std::variant<T, int>; 293 T::reset(); 294 V v1(std::in_place_index<0>, 42); 295 V v2(std::in_place_index<0>, 100); 296 try { 297 v1.swap(v2); 298 assert(false); 299 } catch (int) { 300 } 301 assert(T::move_called == 1); // call threw 302 assert(T::move_assign_called == 0); 303 assert(std::get<0>(v1).value == 304 42); // throw happened before v1 was moved from 305 assert(std::get<0>(v2).value == 100); 306 } 307 { 308 using T = ThrowingMoveAssignNothrowMoveCtor; 309 using V = std::variant<T, int>; 310 T::reset(); 311 V v1(std::in_place_index<0>, 42); 312 V v2(std::in_place_index<0>, 100); 313 try { 314 v1.swap(v2); 315 assert(false); 316 } catch (int) { 317 } 318 assert(T::move_called == 1); 319 assert(T::move_assign_called == 1); // call threw and didn't complete 320 assert(std::get<0>(v1).value == -1); // v1 was moved from 321 assert(std::get<0>(v2).value == 100); 322 } 323 #endif 324 } 325 326 void test_swap_different_alternatives() { 327 { 328 using T = NothrowMoveCtorWithThrowingSwap; 329 using V = std::variant<T, int>; 330 T::reset(); 331 V v1(std::in_place_index<0>, 42); 332 V v2(std::in_place_index<1>, 100); 333 v1.swap(v2); 334 assert(T::swap_called == 0); 335 // The libc++ implementation double copies the argument, and not 336 // the variant swap is called on. 337 LIBCPP_ASSERT(T::move_called == 1); 338 assert(T::move_called <= 2); 339 assert(T::move_assign_called == 0); 340 assert(std::get<1>(v1) == 100); 341 assert(std::get<0>(v2).value == 42); 342 T::reset(); 343 swap(v1, v2); 344 assert(T::swap_called == 0); 345 LIBCPP_ASSERT(T::move_called == 2); 346 assert(T::move_called <= 2); 347 assert(T::move_assign_called == 0); 348 assert(std::get<0>(v1).value == 42); 349 assert(std::get<1>(v2) == 100); 350 } 351 #ifndef TEST_HAS_NO_EXCEPTIONS 352 { 353 using T1 = ThrowingTypeWithNothrowSwap; 354 using T2 = NonThrowingNonNoexceptType; 355 using V = std::variant<T1, T2>; 356 T1::reset(); 357 T2::reset(); 358 V v1(std::in_place_index<0>, 42); 359 V v2(std::in_place_index<1>, 100); 360 try { 361 v1.swap(v2); 362 assert(false); 363 } catch (int) { 364 } 365 assert(T1::swap_called == 0); 366 assert(T1::move_called == 1); // throws 367 assert(T1::move_assign_called == 0); 368 // FIXME: libc++ shouldn't move from T2 here. 369 LIBCPP_ASSERT(T2::move_called == 1); 370 assert(T2::move_called <= 1); 371 assert(std::get<0>(v1).value == 42); 372 if (T2::move_called != 0) 373 assert(v2.valueless_by_exception()); 374 else 375 assert(std::get<1>(v2).value == 100); 376 } 377 { 378 using T1 = NonThrowingNonNoexceptType; 379 using T2 = ThrowingTypeWithNothrowSwap; 380 using V = std::variant<T1, T2>; 381 T1::reset(); 382 T2::reset(); 383 V v1(std::in_place_index<0>, 42); 384 V v2(std::in_place_index<1>, 100); 385 try { 386 v1.swap(v2); 387 assert(false); 388 } catch (int) { 389 } 390 LIBCPP_ASSERT(T1::move_called == 0); 391 assert(T1::move_called <= 1); 392 assert(T2::swap_called == 0); 393 assert(T2::move_called == 1); // throws 394 assert(T2::move_assign_called == 0); 395 if (T1::move_called != 0) 396 assert(v1.valueless_by_exception()); 397 else 398 assert(std::get<0>(v1).value == 42); 399 assert(std::get<1>(v2).value == 100); 400 } 401 // FIXME: The tests below are just very libc++ specific 402 #ifdef _LIBCPP_VERSION 403 { 404 using T1 = ThrowsOnSecondMove; 405 using T2 = NonThrowingNonNoexceptType; 406 using V = std::variant<T1, T2>; 407 T2::reset(); 408 V v1(std::in_place_index<0>, 42); 409 V v2(std::in_place_index<1>, 100); 410 v1.swap(v2); 411 assert(T2::move_called == 2); 412 assert(std::get<1>(v1).value == 100); 413 assert(std::get<0>(v2).value == 42); 414 assert(std::get<0>(v2).move_count == 1); 415 } 416 { 417 using T1 = NonThrowingNonNoexceptType; 418 using T2 = ThrowsOnSecondMove; 419 using V = std::variant<T1, T2>; 420 T1::reset(); 421 V v1(std::in_place_index<0>, 42); 422 V v2(std::in_place_index<1>, 100); 423 try { 424 v1.swap(v2); 425 assert(false); 426 } catch (int) { 427 } 428 assert(T1::move_called == 1); 429 assert(v1.valueless_by_exception()); 430 assert(std::get<0>(v2).value == 42); 431 } 432 #endif 433 // testing libc++ extension. If either variant stores a nothrow move 434 // constructible type v1.swap(v2) provides the strong exception safety 435 // guarantee. 436 #ifdef _LIBCPP_VERSION 437 { 438 439 using T1 = ThrowingTypeWithNothrowSwap; 440 using T2 = NothrowMoveable; 441 using V = std::variant<T1, T2>; 442 T1::reset(); 443 T2::reset(); 444 V v1(std::in_place_index<0>, 42); 445 V v2(std::in_place_index<1>, 100); 446 try { 447 v1.swap(v2); 448 assert(false); 449 } catch (int) { 450 } 451 assert(T1::swap_called == 0); 452 assert(T1::move_called == 1); 453 assert(T1::move_assign_called == 0); 454 assert(T2::swap_called == 0); 455 assert(T2::move_called == 2); 456 assert(T2::move_assign_called == 0); 457 assert(std::get<0>(v1).value == 42); 458 assert(std::get<1>(v2).value == 100); 459 // swap again, but call v2's swap. 460 T1::reset(); 461 T2::reset(); 462 try { 463 v2.swap(v1); 464 assert(false); 465 } catch (int) { 466 } 467 assert(T1::swap_called == 0); 468 assert(T1::move_called == 1); 469 assert(T1::move_assign_called == 0); 470 assert(T2::swap_called == 0); 471 assert(T2::move_called == 2); 472 assert(T2::move_assign_called == 0); 473 assert(std::get<0>(v1).value == 42); 474 assert(std::get<1>(v2).value == 100); 475 } 476 #endif // _LIBCPP_VERSION 477 #endif 478 } 479 480 template <class Var> 481 constexpr auto has_swap_member_imp(int) 482 -> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) { 483 return true; 484 } 485 486 template <class Var> constexpr auto has_swap_member_imp(long) -> bool { 487 return false; 488 } 489 490 template <class Var> constexpr bool has_swap_member() { 491 return has_swap_member_imp<Var>(0); 492 } 493 494 void test_swap_sfinae() { 495 { 496 // This variant type does not provide either a member or non-member swap 497 // but is still swappable via the generic swap algorithm, since the 498 // variant is move constructible and move assignable. 499 using V = std::variant<int, NotSwappable>; 500 LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); 501 static_assert(std::is_swappable_v<V>, ""); 502 } 503 { 504 using V = std::variant<int, NotCopyable>; 505 LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); 506 static_assert(!std::is_swappable_v<V>, ""); 507 } 508 { 509 using V = std::variant<int, NotCopyableWithSwap>; 510 LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); 511 static_assert(!std::is_swappable_v<V>, ""); 512 } 513 { 514 using V = std::variant<int, NotMoveAssignable>; 515 LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); 516 static_assert(!std::is_swappable_v<V>, ""); 517 } 518 } 519 520 void test_swap_noexcept() { 521 { 522 using V = std::variant<int, NothrowMoveable>; 523 static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); 524 static_assert(std::is_nothrow_swappable_v<V>, ""); 525 // instantiate swap 526 V v1, v2; 527 v1.swap(v2); 528 swap(v1, v2); 529 } 530 { 531 using V = std::variant<int, NothrowMoveCtor>; 532 static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); 533 static_assert(!std::is_nothrow_swappable_v<V>, ""); 534 // instantiate swap 535 V v1, v2; 536 v1.swap(v2); 537 swap(v1, v2); 538 } 539 { 540 using V = std::variant<int, ThrowingTypeWithNothrowSwap>; 541 static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); 542 static_assert(!std::is_nothrow_swappable_v<V>, ""); 543 // instantiate swap 544 V v1, v2; 545 v1.swap(v2); 546 swap(v1, v2); 547 } 548 { 549 using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtor>; 550 static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); 551 static_assert(!std::is_nothrow_swappable_v<V>, ""); 552 // instantiate swap 553 V v1, v2; 554 v1.swap(v2); 555 swap(v1, v2); 556 } 557 { 558 using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtorWithSwap>; 559 static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); 560 static_assert(std::is_nothrow_swappable_v<V>, ""); 561 // instantiate swap 562 V v1, v2; 563 v1.swap(v2); 564 swap(v1, v2); 565 } 566 { 567 using V = std::variant<int, NotMoveAssignableWithSwap>; 568 static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); 569 static_assert(std::is_nothrow_swappable_v<V>, ""); 570 // instantiate swap 571 V v1, v2; 572 v1.swap(v2); 573 swap(v1, v2); 574 } 575 { 576 // This variant type does not provide either a member or non-member swap 577 // but is still swappable via the generic swap algorithm, since the 578 // variant is move constructible and move assignable. 579 using V = std::variant<int, NotSwappable>; 580 LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); 581 static_assert(std::is_swappable_v<V>, ""); 582 static_assert(std::is_nothrow_swappable_v<V>, ""); 583 V v1, v2; 584 swap(v1, v2); 585 } 586 } 587 588 #ifdef _LIBCPP_VERSION 589 // This is why variant should SFINAE member swap. :-) 590 template class std::variant<int, NotSwappable>; 591 #endif 592 593 int main() { 594 test_swap_valueless_by_exception(); 595 test_swap_same_alternative(); 596 test_swap_different_alternatives(); 597 test_swap_sfinae(); 598 test_swap_noexcept(); 599 }