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