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