copy.pass.cpp (17469B)
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 // The following compilers don't generate constexpr special members correctly. 14 // XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 15 // XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 16 17 // XFAIL: availability=macosx10.13 18 // XFAIL: availability=macosx10.12 19 // XFAIL: availability=macosx10.11 20 // XFAIL: availability=macosx10.10 21 // XFAIL: availability=macosx10.9 22 // XFAIL: availability=macosx10.8 23 // XFAIL: availability=macosx10.7 24 25 // <variant> 26 27 // template <class ...Types> class variant; 28 29 // variant& operator=(variant const&); // constexpr in C++20 30 31 #include <cassert> 32 #include <string> 33 #include <type_traits> 34 #include <variant> 35 36 #include "test_macros.h" 37 38 struct NoCopy { 39 NoCopy(const NoCopy &) = delete; 40 NoCopy &operator=(const NoCopy &) = default; 41 }; 42 43 struct CopyOnly { 44 CopyOnly(const CopyOnly &) = default; 45 CopyOnly(CopyOnly &&) = delete; 46 CopyOnly &operator=(const CopyOnly &) = default; 47 CopyOnly &operator=(CopyOnly &&) = delete; 48 }; 49 50 struct MoveOnly { 51 MoveOnly(const MoveOnly &) = delete; 52 MoveOnly(MoveOnly &&) = default; 53 MoveOnly &operator=(const MoveOnly &) = default; 54 }; 55 56 struct MoveOnlyNT { 57 MoveOnlyNT(const MoveOnlyNT &) = delete; 58 MoveOnlyNT(MoveOnlyNT &&) {} 59 MoveOnlyNT &operator=(const MoveOnlyNT &) = default; 60 }; 61 62 struct CopyAssign { 63 static int alive; 64 static int copy_construct; 65 static int copy_assign; 66 static int move_construct; 67 static int move_assign; 68 static void reset() { 69 copy_construct = copy_assign = move_construct = move_assign = alive = 0; 70 } 71 CopyAssign(int v) : value(v) { ++alive; } 72 CopyAssign(const CopyAssign &o) : value(o.value) { 73 ++alive; 74 ++copy_construct; 75 } 76 CopyAssign(CopyAssign &&o) noexcept : value(o.value) { 77 o.value = -1; 78 ++alive; 79 ++move_construct; 80 } 81 CopyAssign &operator=(const CopyAssign &o) { 82 value = o.value; 83 ++copy_assign; 84 return *this; 85 } 86 CopyAssign &operator=(CopyAssign &&o) noexcept { 87 value = o.value; 88 o.value = -1; 89 ++move_assign; 90 return *this; 91 } 92 ~CopyAssign() { --alive; } 93 int value; 94 }; 95 96 int CopyAssign::alive = 0; 97 int CopyAssign::copy_construct = 0; 98 int CopyAssign::copy_assign = 0; 99 int CopyAssign::move_construct = 0; 100 int CopyAssign::move_assign = 0; 101 102 struct CopyMaybeThrows { 103 CopyMaybeThrows(const CopyMaybeThrows &); 104 CopyMaybeThrows &operator=(const CopyMaybeThrows &); 105 }; 106 struct CopyDoesThrow { 107 CopyDoesThrow(const CopyDoesThrow &) noexcept(false); 108 CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false); 109 }; 110 111 112 struct NTCopyAssign { 113 constexpr NTCopyAssign(int v) : value(v) {} 114 NTCopyAssign(const NTCopyAssign &) = default; 115 NTCopyAssign(NTCopyAssign &&) = default; 116 NTCopyAssign &operator=(const NTCopyAssign &that) { 117 value = that.value; 118 return *this; 119 }; 120 NTCopyAssign &operator=(NTCopyAssign &&) = delete; 121 int value; 122 }; 123 124 static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, ""); 125 static_assert(std::is_copy_assignable<NTCopyAssign>::value, ""); 126 127 struct TCopyAssign { 128 constexpr TCopyAssign(int v) : value(v) {} 129 TCopyAssign(const TCopyAssign &) = default; 130 TCopyAssign(TCopyAssign &&) = default; 131 TCopyAssign &operator=(const TCopyAssign &) = default; 132 TCopyAssign &operator=(TCopyAssign &&) = delete; 133 int value; 134 }; 135 136 static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, ""); 137 138 struct TCopyAssignNTMoveAssign { 139 constexpr TCopyAssignNTMoveAssign(int v) : value(v) {} 140 TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default; 141 TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default; 142 TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default; 143 TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) { 144 value = that.value; 145 that.value = -1; 146 return *this; 147 } 148 int value; 149 }; 150 151 static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, ""); 152 153 #ifndef TEST_HAS_NO_EXCEPTIONS 154 struct CopyThrows { 155 CopyThrows() = default; 156 CopyThrows(const CopyThrows &) { throw 42; } 157 CopyThrows &operator=(const CopyThrows &) { throw 42; } 158 }; 159 160 struct CopyCannotThrow { 161 static int alive; 162 CopyCannotThrow() { ++alive; } 163 CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; } 164 CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); } 165 CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default; 166 CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; } 167 }; 168 169 int CopyCannotThrow::alive = 0; 170 171 struct MoveThrows { 172 static int alive; 173 MoveThrows() { ++alive; } 174 MoveThrows(const MoveThrows &) { ++alive; } 175 MoveThrows(MoveThrows &&) { throw 42; } 176 MoveThrows &operator=(const MoveThrows &) { return *this; } 177 MoveThrows &operator=(MoveThrows &&) { throw 42; } 178 ~MoveThrows() { --alive; } 179 }; 180 181 int MoveThrows::alive = 0; 182 183 struct MakeEmptyT { 184 static int alive; 185 MakeEmptyT() { ++alive; } 186 MakeEmptyT(const MakeEmptyT &) { 187 ++alive; 188 // Don't throw from the copy constructor since variant's assignment 189 // operator performs a copy before committing to the assignment. 190 } 191 MakeEmptyT(MakeEmptyT &&) { throw 42; } 192 MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; } 193 MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } 194 ~MakeEmptyT() { --alive; } 195 }; 196 197 int MakeEmptyT::alive = 0; 198 199 template <class Variant> void makeEmpty(Variant &v) { 200 Variant v2(std::in_place_type<MakeEmptyT>); 201 try { 202 v = std::move(v2); 203 assert(false); 204 } catch (...) { 205 assert(v.valueless_by_exception()); 206 } 207 } 208 #endif // TEST_HAS_NO_EXCEPTIONS 209 210 void test_copy_assignment_not_noexcept() { 211 { 212 using V = std::variant<CopyMaybeThrows>; 213 static_assert(!std::is_nothrow_copy_assignable<V>::value, ""); 214 } 215 { 216 using V = std::variant<int, CopyDoesThrow>; 217 static_assert(!std::is_nothrow_copy_assignable<V>::value, ""); 218 } 219 } 220 221 void test_copy_assignment_sfinae() { 222 { 223 using V = std::variant<int, long>; 224 static_assert(std::is_copy_assignable<V>::value, ""); 225 } 226 { 227 using V = std::variant<int, CopyOnly>; 228 static_assert(std::is_copy_assignable<V>::value, ""); 229 } 230 { 231 using V = std::variant<int, NoCopy>; 232 static_assert(!std::is_copy_assignable<V>::value, ""); 233 } 234 { 235 using V = std::variant<int, MoveOnly>; 236 static_assert(!std::is_copy_assignable<V>::value, ""); 237 } 238 { 239 using V = std::variant<int, MoveOnlyNT>; 240 static_assert(!std::is_copy_assignable<V>::value, ""); 241 } 242 243 // Make sure we properly propagate triviality (see P0602R4). 244 #if TEST_STD_VER > 17 245 { 246 using V = std::variant<int, long>; 247 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 248 } 249 { 250 using V = std::variant<int, NTCopyAssign>; 251 static_assert(!std::is_trivially_copy_assignable<V>::value, ""); 252 static_assert(std::is_copy_assignable<V>::value, ""); 253 } 254 { 255 using V = std::variant<int, TCopyAssign>; 256 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 257 } 258 { 259 using V = std::variant<int, TCopyAssignNTMoveAssign>; 260 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 261 } 262 { 263 using V = std::variant<int, CopyOnly>; 264 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 265 } 266 #endif // > C++17 267 } 268 269 void test_copy_assignment_empty_empty() { 270 #ifndef TEST_HAS_NO_EXCEPTIONS 271 using MET = MakeEmptyT; 272 { 273 using V = std::variant<int, long, MET>; 274 V v1(std::in_place_index<0>); 275 makeEmpty(v1); 276 V v2(std::in_place_index<0>); 277 makeEmpty(v2); 278 V &vref = (v1 = v2); 279 assert(&vref == &v1); 280 assert(v1.valueless_by_exception()); 281 assert(v1.index() == std::variant_npos); 282 } 283 #endif // TEST_HAS_NO_EXCEPTIONS 284 } 285 286 void test_copy_assignment_non_empty_empty() { 287 #ifndef TEST_HAS_NO_EXCEPTIONS 288 using MET = MakeEmptyT; 289 { 290 using V = std::variant<int, MET>; 291 V v1(std::in_place_index<0>, 42); 292 V v2(std::in_place_index<0>); 293 makeEmpty(v2); 294 V &vref = (v1 = v2); 295 assert(&vref == &v1); 296 assert(v1.valueless_by_exception()); 297 assert(v1.index() == std::variant_npos); 298 } 299 { 300 using V = std::variant<int, MET, std::string>; 301 V v1(std::in_place_index<2>, "hello"); 302 V v2(std::in_place_index<0>); 303 makeEmpty(v2); 304 V &vref = (v1 = v2); 305 assert(&vref == &v1); 306 assert(v1.valueless_by_exception()); 307 assert(v1.index() == std::variant_npos); 308 } 309 #endif // TEST_HAS_NO_EXCEPTIONS 310 } 311 312 void test_copy_assignment_empty_non_empty() { 313 #ifndef TEST_HAS_NO_EXCEPTIONS 314 using MET = MakeEmptyT; 315 { 316 using V = std::variant<int, MET>; 317 V v1(std::in_place_index<0>); 318 makeEmpty(v1); 319 V v2(std::in_place_index<0>, 42); 320 V &vref = (v1 = v2); 321 assert(&vref == &v1); 322 assert(v1.index() == 0); 323 assert(std::get<0>(v1) == 42); 324 } 325 { 326 using V = std::variant<int, MET, std::string>; 327 V v1(std::in_place_index<0>); 328 makeEmpty(v1); 329 V v2(std::in_place_type<std::string>, "hello"); 330 V &vref = (v1 = v2); 331 assert(&vref == &v1); 332 assert(v1.index() == 2); 333 assert(std::get<2>(v1) == "hello"); 334 } 335 #endif // TEST_HAS_NO_EXCEPTIONS 336 } 337 338 template <typename T> struct Result { size_t index; T value; }; 339 340 void test_copy_assignment_same_index() { 341 { 342 using V = std::variant<int>; 343 V v1(43); 344 V v2(42); 345 V &vref = (v1 = v2); 346 assert(&vref == &v1); 347 assert(v1.index() == 0); 348 assert(std::get<0>(v1) == 42); 349 } 350 { 351 using V = std::variant<int, long, unsigned>; 352 V v1(43l); 353 V v2(42l); 354 V &vref = (v1 = v2); 355 assert(&vref == &v1); 356 assert(v1.index() == 1); 357 assert(std::get<1>(v1) == 42); 358 } 359 { 360 using V = std::variant<int, CopyAssign, unsigned>; 361 V v1(std::in_place_type<CopyAssign>, 43); 362 V v2(std::in_place_type<CopyAssign>, 42); 363 CopyAssign::reset(); 364 V &vref = (v1 = v2); 365 assert(&vref == &v1); 366 assert(v1.index() == 1); 367 assert(std::get<1>(v1).value == 42); 368 assert(CopyAssign::copy_construct == 0); 369 assert(CopyAssign::move_construct == 0); 370 assert(CopyAssign::copy_assign == 1); 371 } 372 #ifndef TEST_HAS_NO_EXCEPTIONS 373 using MET = MakeEmptyT; 374 { 375 using V = std::variant<int, MET, std::string>; 376 V v1(std::in_place_type<MET>); 377 MET &mref = std::get<1>(v1); 378 V v2(std::in_place_type<MET>); 379 try { 380 v1 = v2; 381 assert(false); 382 } catch (...) { 383 } 384 assert(v1.index() == 1); 385 assert(&std::get<1>(v1) == &mref); 386 } 387 #endif // TEST_HAS_NO_EXCEPTIONS 388 389 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 390 #if TEST_STD_VER > 17 391 { 392 struct { 393 constexpr Result<int> operator()() const { 394 using V = std::variant<int>; 395 V v(43); 396 V v2(42); 397 v = v2; 398 return {v.index(), std::get<0>(v)}; 399 } 400 } test; 401 constexpr auto result = test(); 402 static_assert(result.index == 0, ""); 403 static_assert(result.value == 42, ""); 404 } 405 { 406 struct { 407 constexpr Result<long> operator()() const { 408 using V = std::variant<int, long, unsigned>; 409 V v(43l); 410 V v2(42l); 411 v = v2; 412 return {v.index(), std::get<1>(v)}; 413 } 414 } test; 415 constexpr auto result = test(); 416 static_assert(result.index == 1, ""); 417 static_assert(result.value == 42l, ""); 418 } 419 { 420 struct { 421 constexpr Result<int> operator()() const { 422 using V = std::variant<int, TCopyAssign, unsigned>; 423 V v(std::in_place_type<TCopyAssign>, 43); 424 V v2(std::in_place_type<TCopyAssign>, 42); 425 v = v2; 426 return {v.index(), std::get<1>(v).value}; 427 } 428 } test; 429 constexpr auto result = test(); 430 static_assert(result.index == 1, ""); 431 static_assert(result.value == 42, ""); 432 } 433 { 434 struct { 435 constexpr Result<int> operator()() const { 436 using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>; 437 V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43); 438 V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42); 439 v = v2; 440 return {v.index(), std::get<1>(v).value}; 441 } 442 } test; 443 constexpr auto result = test(); 444 static_assert(result.index == 1, ""); 445 static_assert(result.value == 42, ""); 446 } 447 #endif // > C++17 448 } 449 450 void test_copy_assignment_different_index() { 451 { 452 using V = std::variant<int, long, unsigned>; 453 V v1(43); 454 V v2(42l); 455 V &vref = (v1 = v2); 456 assert(&vref == &v1); 457 assert(v1.index() == 1); 458 assert(std::get<1>(v1) == 42); 459 } 460 { 461 using V = std::variant<int, CopyAssign, unsigned>; 462 CopyAssign::reset(); 463 V v1(std::in_place_type<unsigned>, 43); 464 V v2(std::in_place_type<CopyAssign>, 42); 465 assert(CopyAssign::copy_construct == 0); 466 assert(CopyAssign::move_construct == 0); 467 assert(CopyAssign::alive == 1); 468 V &vref = (v1 = v2); 469 assert(&vref == &v1); 470 assert(v1.index() == 1); 471 assert(std::get<1>(v1).value == 42); 472 assert(CopyAssign::alive == 2); 473 assert(CopyAssign::copy_construct == 1); 474 assert(CopyAssign::move_construct == 1); 475 assert(CopyAssign::copy_assign == 0); 476 } 477 #ifndef TEST_HAS_NO_EXCEPTIONS 478 { 479 using V = std::variant<int, CopyThrows, std::string>; 480 V v1(std::in_place_type<std::string>, "hello"); 481 V v2(std::in_place_type<CopyThrows>); 482 try { 483 v1 = v2; 484 assert(false); 485 } catch (...) { /* ... */ 486 } 487 // Test that copy construction is used directly if move construction may throw, 488 // resulting in a valueless variant if copy throws. 489 assert(v1.valueless_by_exception()); 490 } 491 { 492 using V = std::variant<int, MoveThrows, std::string>; 493 V v1(std::in_place_type<std::string>, "hello"); 494 V v2(std::in_place_type<MoveThrows>); 495 assert(MoveThrows::alive == 1); 496 // Test that copy construction is used directly if move construction may throw. 497 v1 = v2; 498 assert(v1.index() == 1); 499 assert(v2.index() == 1); 500 assert(MoveThrows::alive == 2); 501 } 502 { 503 // Test that direct copy construction is preferred when it cannot throw. 504 using V = std::variant<int, CopyCannotThrow, std::string>; 505 V v1(std::in_place_type<std::string>, "hello"); 506 V v2(std::in_place_type<CopyCannotThrow>); 507 assert(CopyCannotThrow::alive == 1); 508 v1 = v2; 509 assert(v1.index() == 1); 510 assert(v2.index() == 1); 511 assert(CopyCannotThrow::alive == 2); 512 } 513 { 514 using V = std::variant<int, CopyThrows, std::string>; 515 V v1(std::in_place_type<CopyThrows>); 516 V v2(std::in_place_type<std::string>, "hello"); 517 V &vref = (v1 = v2); 518 assert(&vref == &v1); 519 assert(v1.index() == 2); 520 assert(std::get<2>(v1) == "hello"); 521 assert(v2.index() == 2); 522 assert(std::get<2>(v2) == "hello"); 523 } 524 { 525 using V = std::variant<int, MoveThrows, std::string>; 526 V v1(std::in_place_type<MoveThrows>); 527 V v2(std::in_place_type<std::string>, "hello"); 528 V &vref = (v1 = v2); 529 assert(&vref == &v1); 530 assert(v1.index() == 2); 531 assert(std::get<2>(v1) == "hello"); 532 assert(v2.index() == 2); 533 assert(std::get<2>(v2) == "hello"); 534 } 535 #endif // TEST_HAS_NO_EXCEPTIONS 536 537 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 538 #if TEST_STD_VER > 17 539 { 540 struct { 541 constexpr Result<long> operator()() const { 542 using V = std::variant<int, long, unsigned>; 543 V v(43); 544 V v2(42l); 545 v = v2; 546 return {v.index(), std::get<1>(v)}; 547 } 548 } test; 549 constexpr auto result = test(); 550 static_assert(result.index == 1, ""); 551 static_assert(result.value == 42l, ""); 552 } 553 { 554 struct { 555 constexpr Result<int> operator()() const { 556 using V = std::variant<int, TCopyAssign, unsigned>; 557 V v(std::in_place_type<unsigned>, 43); 558 V v2(std::in_place_type<TCopyAssign>, 42); 559 v = v2; 560 return {v.index(), std::get<1>(v).value}; 561 } 562 } test; 563 constexpr auto result = test(); 564 static_assert(result.index == 1, ""); 565 static_assert(result.value == 42, ""); 566 } 567 #endif // > C++17 568 } 569 570 template <size_t NewIdx, class ValueType> 571 constexpr bool test_constexpr_assign_imp( 572 std::variant<long, void*, int>&& v, ValueType&& new_value) 573 { 574 const std::variant<long, void*, int> cp( 575 std::forward<ValueType>(new_value)); 576 v = cp; 577 return v.index() == NewIdx && 578 std::get<NewIdx>(v) == std::get<NewIdx>(cp); 579 } 580 581 void test_constexpr_copy_assignment() { 582 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). 583 #if TEST_STD_VER > 17 584 using V = std::variant<long, void*, int>; 585 static_assert(std::is_trivially_copyable<V>::value, ""); 586 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 587 static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), ""); 588 static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), ""); 589 static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), ""); 590 static_assert(test_constexpr_assign_imp<2>(V(42l), 101), ""); 591 #endif // > C++17 592 } 593 594 int main() { 595 test_copy_assignment_empty_empty(); 596 test_copy_assignment_non_empty_empty(); 597 test_copy_assignment_empty_non_empty(); 598 test_copy_assignment_same_index(); 599 test_copy_assignment_different_index(); 600 test_copy_assignment_sfinae(); 601 test_copy_assignment_not_noexcept(); 602 test_constexpr_copy_assignment(); 603 }