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