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