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&); 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 // The following tests are for not-yet-standardized behavior (P0602): 244 { 245 using V = std::variant<int, long>; 246 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 247 } 248 { 249 using V = std::variant<int, NTCopyAssign>; 250 static_assert(!std::is_trivially_copy_assignable<V>::value, ""); 251 static_assert(std::is_copy_assignable<V>::value, ""); 252 } 253 { 254 using V = std::variant<int, TCopyAssign>; 255 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 256 } 257 { 258 using V = std::variant<int, TCopyAssignNTMoveAssign>; 259 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 260 } 261 { 262 using V = std::variant<int, CopyOnly>; 263 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 264 } 265 } 266 267 void test_copy_assignment_empty_empty() { 268 #ifndef TEST_HAS_NO_EXCEPTIONS 269 using MET = MakeEmptyT; 270 { 271 using V = std::variant<int, long, MET>; 272 V v1(std::in_place_index<0>); 273 makeEmpty(v1); 274 V v2(std::in_place_index<0>); 275 makeEmpty(v2); 276 V &vref = (v1 = v2); 277 assert(&vref == &v1); 278 assert(v1.valueless_by_exception()); 279 assert(v1.index() == std::variant_npos); 280 } 281 #endif // TEST_HAS_NO_EXCEPTIONS 282 } 283 284 void test_copy_assignment_non_empty_empty() { 285 #ifndef TEST_HAS_NO_EXCEPTIONS 286 using MET = MakeEmptyT; 287 { 288 using V = std::variant<int, MET>; 289 V v1(std::in_place_index<0>, 42); 290 V v2(std::in_place_index<0>); 291 makeEmpty(v2); 292 V &vref = (v1 = v2); 293 assert(&vref == &v1); 294 assert(v1.valueless_by_exception()); 295 assert(v1.index() == std::variant_npos); 296 } 297 { 298 using V = std::variant<int, MET, std::string>; 299 V v1(std::in_place_index<2>, "hello"); 300 V v2(std::in_place_index<0>); 301 makeEmpty(v2); 302 V &vref = (v1 = v2); 303 assert(&vref == &v1); 304 assert(v1.valueless_by_exception()); 305 assert(v1.index() == std::variant_npos); 306 } 307 #endif // TEST_HAS_NO_EXCEPTIONS 308 } 309 310 void test_copy_assignment_empty_non_empty() { 311 #ifndef TEST_HAS_NO_EXCEPTIONS 312 using MET = MakeEmptyT; 313 { 314 using V = std::variant<int, MET>; 315 V v1(std::in_place_index<0>); 316 makeEmpty(v1); 317 V v2(std::in_place_index<0>, 42); 318 V &vref = (v1 = v2); 319 assert(&vref == &v1); 320 assert(v1.index() == 0); 321 assert(std::get<0>(v1) == 42); 322 } 323 { 324 using V = std::variant<int, MET, std::string>; 325 V v1(std::in_place_index<0>); 326 makeEmpty(v1); 327 V v2(std::in_place_type<std::string>, "hello"); 328 V &vref = (v1 = v2); 329 assert(&vref == &v1); 330 assert(v1.index() == 2); 331 assert(std::get<2>(v1) == "hello"); 332 } 333 #endif // TEST_HAS_NO_EXCEPTIONS 334 } 335 336 template <typename T> struct Result { size_t index; T value; }; 337 338 void test_copy_assignment_same_index() { 339 { 340 using V = std::variant<int>; 341 V v1(43); 342 V v2(42); 343 V &vref = (v1 = v2); 344 assert(&vref == &v1); 345 assert(v1.index() == 0); 346 assert(std::get<0>(v1) == 42); 347 } 348 { 349 using V = std::variant<int, long, unsigned>; 350 V v1(43l); 351 V v2(42l); 352 V &vref = (v1 = v2); 353 assert(&vref == &v1); 354 assert(v1.index() == 1); 355 assert(std::get<1>(v1) == 42); 356 } 357 { 358 using V = std::variant<int, CopyAssign, unsigned>; 359 V v1(std::in_place_type<CopyAssign>, 43); 360 V v2(std::in_place_type<CopyAssign>, 42); 361 CopyAssign::reset(); 362 V &vref = (v1 = v2); 363 assert(&vref == &v1); 364 assert(v1.index() == 1); 365 assert(std::get<1>(v1).value == 42); 366 assert(CopyAssign::copy_construct == 0); 367 assert(CopyAssign::move_construct == 0); 368 assert(CopyAssign::copy_assign == 1); 369 } 370 #ifndef TEST_HAS_NO_EXCEPTIONS 371 using MET = MakeEmptyT; 372 { 373 using V = std::variant<int, MET, std::string>; 374 V v1(std::in_place_type<MET>); 375 MET &mref = std::get<1>(v1); 376 V v2(std::in_place_type<MET>); 377 try { 378 v1 = v2; 379 assert(false); 380 } catch (...) { 381 } 382 assert(v1.index() == 1); 383 assert(&std::get<1>(v1) == &mref); 384 } 385 #endif // TEST_HAS_NO_EXCEPTIONS 386 387 // The following tests are for not-yet-standardized behavior (P0602): 388 { 389 struct { 390 constexpr Result<int> operator()() const { 391 using V = std::variant<int>; 392 V v(43); 393 V v2(42); 394 v = v2; 395 return {v.index(), std::get<0>(v)}; 396 } 397 } test; 398 constexpr auto result = test(); 399 static_assert(result.index == 0, ""); 400 static_assert(result.value == 42, ""); 401 } 402 { 403 struct { 404 constexpr Result<long> operator()() const { 405 using V = std::variant<int, long, unsigned>; 406 V v(43l); 407 V v2(42l); 408 v = v2; 409 return {v.index(), std::get<1>(v)}; 410 } 411 } test; 412 constexpr auto result = test(); 413 static_assert(result.index == 1, ""); 414 static_assert(result.value == 42l, ""); 415 } 416 { 417 struct { 418 constexpr Result<int> operator()() const { 419 using V = std::variant<int, TCopyAssign, unsigned>; 420 V v(std::in_place_type<TCopyAssign>, 43); 421 V v2(std::in_place_type<TCopyAssign>, 42); 422 v = v2; 423 return {v.index(), std::get<1>(v).value}; 424 } 425 } test; 426 constexpr auto result = test(); 427 static_assert(result.index == 1, ""); 428 static_assert(result.value == 42, ""); 429 } 430 { 431 struct { 432 constexpr Result<int> operator()() const { 433 using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>; 434 V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43); 435 V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42); 436 v = v2; 437 return {v.index(), std::get<1>(v).value}; 438 } 439 } test; 440 constexpr auto result = test(); 441 static_assert(result.index == 1, ""); 442 static_assert(result.value == 42, ""); 443 } 444 } 445 446 void test_copy_assignment_different_index() { 447 { 448 using V = std::variant<int, long, unsigned>; 449 V v1(43); 450 V v2(42l); 451 V &vref = (v1 = v2); 452 assert(&vref == &v1); 453 assert(v1.index() == 1); 454 assert(std::get<1>(v1) == 42); 455 } 456 { 457 using V = std::variant<int, CopyAssign, unsigned>; 458 CopyAssign::reset(); 459 V v1(std::in_place_type<unsigned>, 43); 460 V v2(std::in_place_type<CopyAssign>, 42); 461 assert(CopyAssign::copy_construct == 0); 462 assert(CopyAssign::move_construct == 0); 463 assert(CopyAssign::alive == 1); 464 V &vref = (v1 = v2); 465 assert(&vref == &v1); 466 assert(v1.index() == 1); 467 assert(std::get<1>(v1).value == 42); 468 assert(CopyAssign::alive == 2); 469 assert(CopyAssign::copy_construct == 1); 470 assert(CopyAssign::move_construct == 1); 471 assert(CopyAssign::copy_assign == 0); 472 } 473 #ifndef TEST_HAS_NO_EXCEPTIONS 474 { 475 using V = std::variant<int, CopyThrows, std::string>; 476 V v1(std::in_place_type<std::string>, "hello"); 477 V v2(std::in_place_type<CopyThrows>); 478 try { 479 v1 = v2; 480 assert(false); 481 } catch (...) { /* ... */ 482 } 483 // Test that copy construction is used directly if move construction may throw, 484 // resulting in a valueless variant if copy throws. 485 assert(v1.valueless_by_exception()); 486 } 487 { 488 using V = std::variant<int, MoveThrows, std::string>; 489 V v1(std::in_place_type<std::string>, "hello"); 490 V v2(std::in_place_type<MoveThrows>); 491 assert(MoveThrows::alive == 1); 492 // Test that copy construction is used directly if move construction may throw. 493 v1 = v2; 494 assert(v1.index() == 1); 495 assert(v2.index() == 1); 496 assert(MoveThrows::alive == 2); 497 } 498 { 499 // Test that direct copy construction is preferred when it cannot throw. 500 using V = std::variant<int, CopyCannotThrow, std::string>; 501 V v1(std::in_place_type<std::string>, "hello"); 502 V v2(std::in_place_type<CopyCannotThrow>); 503 assert(CopyCannotThrow::alive == 1); 504 v1 = v2; 505 assert(v1.index() == 1); 506 assert(v2.index() == 1); 507 assert(CopyCannotThrow::alive == 2); 508 } 509 { 510 using V = std::variant<int, CopyThrows, std::string>; 511 V v1(std::in_place_type<CopyThrows>); 512 V v2(std::in_place_type<std::string>, "hello"); 513 V &vref = (v1 = v2); 514 assert(&vref == &v1); 515 assert(v1.index() == 2); 516 assert(std::get<2>(v1) == "hello"); 517 assert(v2.index() == 2); 518 assert(std::get<2>(v2) == "hello"); 519 } 520 { 521 using V = std::variant<int, MoveThrows, std::string>; 522 V v1(std::in_place_type<MoveThrows>); 523 V v2(std::in_place_type<std::string>, "hello"); 524 V &vref = (v1 = v2); 525 assert(&vref == &v1); 526 assert(v1.index() == 2); 527 assert(std::get<2>(v1) == "hello"); 528 assert(v2.index() == 2); 529 assert(std::get<2>(v2) == "hello"); 530 } 531 #endif // TEST_HAS_NO_EXCEPTIONS 532 533 // The following tests are for not-yet-standardized behavior (P0602): 534 { 535 struct { 536 constexpr Result<long> operator()() const { 537 using V = std::variant<int, long, unsigned>; 538 V v(43); 539 V v2(42l); 540 v = v2; 541 return {v.index(), std::get<1>(v)}; 542 } 543 } test; 544 constexpr auto result = test(); 545 static_assert(result.index == 1, ""); 546 static_assert(result.value == 42l, ""); 547 } 548 { 549 struct { 550 constexpr Result<int> operator()() const { 551 using V = std::variant<int, TCopyAssign, unsigned>; 552 V v(std::in_place_type<unsigned>, 43); 553 V v2(std::in_place_type<TCopyAssign>, 42); 554 v = v2; 555 return {v.index(), std::get<1>(v).value}; 556 } 557 } test; 558 constexpr auto result = test(); 559 static_assert(result.index == 1, ""); 560 static_assert(result.value == 42, ""); 561 } 562 } 563 564 template <size_t NewIdx, class ValueType> 565 constexpr bool test_constexpr_assign_extension_imp( 566 std::variant<long, void*, int>&& v, ValueType&& new_value) 567 { 568 const std::variant<long, void*, int> cp( 569 std::forward<ValueType>(new_value)); 570 v = cp; 571 return v.index() == NewIdx && 572 std::get<NewIdx>(v) == std::get<NewIdx>(cp); 573 } 574 575 void test_constexpr_copy_assignment_extension() { 576 // The following tests are for not-yet-standardized behavior (P0602): 577 using V = std::variant<long, void*, int>; 578 static_assert(std::is_trivially_copyable<V>::value, ""); 579 static_assert(std::is_trivially_copy_assignable<V>::value, ""); 580 static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), ""); 581 static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), ""); 582 static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), ""); 583 static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), ""); 584 } 585 586 int main() { 587 test_copy_assignment_empty_empty(); 588 test_copy_assignment_non_empty_empty(); 589 test_copy_assignment_empty_non_empty(); 590 test_copy_assignment_same_index(); 591 test_copy_assignment_different_index(); 592 test_copy_assignment_sfinae(); 593 test_copy_assignment_not_noexcept(); 594 test_constexpr_copy_assignment_extension(); 595 } 596