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