549 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&&) noexcept(see below);16 17#include <cassert>18#include <string>19#include <type_traits>20#include <utility>21#include <variant>22 23#include "test_macros.h"24#include "variant_test_helpers.h"25 26struct NoCopy {27 NoCopy(const NoCopy&) = delete;28 NoCopy& operator=(const NoCopy&) = default;29};30 31struct CopyOnly {32 CopyOnly(const CopyOnly&) = default;33 CopyOnly(CopyOnly&&) = delete;34 CopyOnly& operator=(const CopyOnly&) = default;35 CopyOnly& operator=(CopyOnly&&) = delete;36};37 38struct MoveOnly {39 MoveOnly(const MoveOnly&) = delete;40 MoveOnly(MoveOnly&&) = default;41 MoveOnly& operator=(const MoveOnly&) = delete;42 MoveOnly& operator=(MoveOnly&&) = default;43};44 45struct MoveOnlyNT {46 MoveOnlyNT(const MoveOnlyNT&) = delete;47 MoveOnlyNT(MoveOnlyNT&&) {}48 MoveOnlyNT& operator=(const MoveOnlyNT&) = delete;49 MoveOnlyNT& operator=(MoveOnlyNT&&) = default;50};51 52struct MoveOnlyOddNothrow {53 MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {}54 MoveOnlyOddNothrow(const MoveOnlyOddNothrow&) = delete;55 MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default;56 MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&) = delete;57};58 59struct MoveAssignOnly {60 MoveAssignOnly(MoveAssignOnly&&) = delete;61 MoveAssignOnly& operator=(MoveAssignOnly&&) = default;62};63 64struct MoveAssign {65 constexpr MoveAssign(int v, int* move_ctor, int* move_assi)66 : value(v), move_construct(move_ctor), move_assign(move_assi) {}67 constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) {68 ++*move_construct;69 o.value = -1;70 }71 constexpr MoveAssign& operator=(MoveAssign&& o) {72 value = o.value;73 move_construct = o.move_construct;74 move_assign = o.move_assign;75 ++*move_assign;76 o.value = -1;77 return *this;78 }79 int value;80 int* move_construct;81 int* move_assign;82};83 84struct NTMoveAssign {85 constexpr NTMoveAssign(int v) : value(v) {}86 NTMoveAssign(const NTMoveAssign&) = default;87 NTMoveAssign(NTMoveAssign&&) = default;88 NTMoveAssign& operator=(const NTMoveAssign& that) = default;89 NTMoveAssign& operator=(NTMoveAssign&& that) {90 value = that.value;91 that.value = -1;92 return *this;93 };94 int value;95};96 97static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");98static_assert(std::is_move_assignable<NTMoveAssign>::value, "");99 100struct TMoveAssign {101 constexpr TMoveAssign(int v) : value(v) {}102 TMoveAssign(const TMoveAssign&) = delete;103 TMoveAssign(TMoveAssign&&) = default;104 TMoveAssign& operator=(const TMoveAssign&) = delete;105 TMoveAssign& operator=(TMoveAssign&&) = default;106 int value;107};108 109static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");110 111struct TMoveAssignNTCopyAssign {112 constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}113 TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default;114 TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&) = default;115 TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) {116 value = that.value;117 return *this;118 }119 TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default;120 int value;121};122 123static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");124 125struct TrivialCopyNontrivialMove {126 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;127 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}128 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;129 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; }130};131 132static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");133static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");134 135constexpr void test_move_assignment_noexcept() {136 {137 using V = std::variant<int>;138 static_assert(std::is_nothrow_move_assignable<V>::value, "");139 }140 {141 using V = std::variant<MoveOnly>;142 static_assert(std::is_nothrow_move_assignable<V>::value, "");143 }144 {145 using V = std::variant<int, long>;146 static_assert(std::is_nothrow_move_assignable<V>::value, "");147 }148 {149 using V = std::variant<int, MoveOnly>;150 static_assert(std::is_nothrow_move_assignable<V>::value, "");151 }152 {153 using V = std::variant<MoveOnlyNT>;154 static_assert(!std::is_nothrow_move_assignable<V>::value, "");155 }156 {157 using V = std::variant<MoveOnlyOddNothrow>;158 static_assert(!std::is_nothrow_move_assignable<V>::value, "");159 }160}161 162constexpr void test_move_assignment_sfinae() {163 {164 using V = std::variant<int, long>;165 static_assert(std::is_move_assignable<V>::value, "");166 }167 {168 using V = std::variant<int, CopyOnly>;169 static_assert(std::is_move_assignable<V>::value, "");170 }171 {172 using V = std::variant<int, NoCopy>;173 static_assert(!std::is_move_assignable<V>::value, "");174 }175 {176 using V = std::variant<int, MoveOnly>;177 static_assert(std::is_move_assignable<V>::value, "");178 }179 {180 using V = std::variant<int, MoveOnlyNT>;181 static_assert(std::is_move_assignable<V>::value, "");182 }183 {184 // variant only provides move assignment when the types also provide185 // a move constructor.186 using V = std::variant<int, MoveAssignOnly>;187 static_assert(!std::is_move_assignable<V>::value, "");188 }189 190 // Make sure we properly propagate triviality (see P0602R4).191 {192 using V = std::variant<int, long>;193 static_assert(std::is_trivially_move_assignable<V>::value, "");194 }195 {196 using V = std::variant<int, NTMoveAssign>;197 static_assert(!std::is_trivially_move_assignable<V>::value, "");198 static_assert(std::is_move_assignable<V>::value, "");199 }200 {201 using V = std::variant<int, TMoveAssign>;202 static_assert(std::is_trivially_move_assignable<V>::value, "");203 }204 {205 using V = std::variant<int, TMoveAssignNTCopyAssign>;206 static_assert(std::is_trivially_move_assignable<V>::value, "");207 }208 {209 using V = std::variant<int, TrivialCopyNontrivialMove>;210 static_assert(!std::is_trivially_move_assignable<V>::value, "");211 }212 {213 using V = std::variant<int, CopyOnly>;214 static_assert(std::is_trivially_move_assignable<V>::value, "");215 }216}217 218void test_move_assignment_empty_empty() {219#ifndef TEST_HAS_NO_EXCEPTIONS220 using MET = MakeEmptyT;221 {222 using V = std::variant<int, long, MET>;223 V v1(std::in_place_index<0>);224 makeEmpty(v1);225 V v2(std::in_place_index<0>);226 makeEmpty(v2);227 V& vref = (v1 = std::move(v2));228 assert(&vref == &v1);229 assert(v1.valueless_by_exception());230 assert(v1.index() == std::variant_npos);231 }232#endif // TEST_HAS_NO_EXCEPTIONS233}234 235void test_move_assignment_non_empty_empty() {236#ifndef TEST_HAS_NO_EXCEPTIONS237 using MET = MakeEmptyT;238 {239 using V = std::variant<int, MET>;240 V v1(std::in_place_index<0>, 42);241 V v2(std::in_place_index<0>);242 makeEmpty(v2);243 V& vref = (v1 = std::move(v2));244 assert(&vref == &v1);245 assert(v1.valueless_by_exception());246 assert(v1.index() == std::variant_npos);247 }248 {249 using V = std::variant<int, MET, std::string>;250 V v1(std::in_place_index<2>, "hello");251 V v2(std::in_place_index<0>);252 makeEmpty(v2);253 V& vref = (v1 = std::move(v2));254 assert(&vref == &v1);255 assert(v1.valueless_by_exception());256 assert(v1.index() == std::variant_npos);257 }258#endif // TEST_HAS_NO_EXCEPTIONS259}260 261void test_move_assignment_empty_non_empty() {262#ifndef TEST_HAS_NO_EXCEPTIONS263 using MET = MakeEmptyT;264 {265 using V = std::variant<int, MET>;266 V v1(std::in_place_index<0>);267 makeEmpty(v1);268 V v2(std::in_place_index<0>, 42);269 V& vref = (v1 = std::move(v2));270 assert(&vref == &v1);271 assert(v1.index() == 0);272 assert(std::get<0>(v1) == 42);273 }274 {275 using V = std::variant<int, MET, std::string>;276 V v1(std::in_place_index<0>);277 makeEmpty(v1);278 V v2(std::in_place_type<std::string>, "hello");279 V& vref = (v1 = std::move(v2));280 assert(&vref == &v1);281 assert(v1.index() == 2);282 assert(std::get<2>(v1) == "hello");283 }284#endif // TEST_HAS_NO_EXCEPTIONS285}286 287template <typename T>288struct Result {289 std::size_t index;290 T value;291};292 293TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() {294 {295 using V = std::variant<int>;296 V v1(43);297 V v2(42);298 V& vref = (v1 = std::move(v2));299 assert(&vref == &v1);300 assert(v1.index() == 0);301 assert(std::get<0>(v1) == 42);302 }303 {304 using V = std::variant<int, long, unsigned>;305 V v1(43l);306 V v2(42l);307 V& vref = (v1 = std::move(v2));308 assert(&vref == &v1);309 assert(v1.index() == 1);310 assert(std::get<1>(v1) == 42);311 }312 {313 using V = std::variant<int, MoveAssign, unsigned>;314 int move_construct = 0;315 int move_assign = 0;316 V v1(std::in_place_type<MoveAssign>, 43, &move_construct, &move_assign);317 V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);318 V& vref = (v1 = std::move(v2));319 assert(&vref == &v1);320 assert(v1.index() == 1);321 assert(std::get<1>(v1).value == 42);322 assert(move_construct == 0);323 assert(move_assign == 1);324 }325 326 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).327 {328 struct {329 constexpr Result<int> operator()() const {330 using V = std::variant<int>;331 V v(43);332 V v2(42);333 v = std::move(v2);334 return {v.index(), std::get<0>(v)};335 }336 } test;337 constexpr auto result = test();338 static_assert(result.index == 0, "");339 static_assert(result.value == 42, "");340 }341 {342 struct {343 constexpr Result<long> operator()() const {344 using V = std::variant<int, long, unsigned>;345 V v(43l);346 V v2(42l);347 v = std::move(v2);348 return {v.index(), std::get<1>(v)};349 }350 } test;351 constexpr auto result = test();352 static_assert(result.index == 1, "");353 static_assert(result.value == 42l, "");354 }355 {356 struct {357 constexpr Result<int> operator()() const {358 using V = std::variant<int, TMoveAssign, unsigned>;359 V v(std::in_place_type<TMoveAssign>, 43);360 V v2(std::in_place_type<TMoveAssign>, 42);361 v = std::move(v2);362 return {v.index(), std::get<1>(v).value};363 }364 } test;365 constexpr auto result = test();366 static_assert(result.index == 1, "");367 static_assert(result.value == 42, "");368 }369}370 371TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() {372 {373 using V = std::variant<int, long, unsigned>;374 V v1(43);375 V v2(42l);376 V& vref = (v1 = std::move(v2));377 assert(&vref == &v1);378 assert(v1.index() == 1);379 assert(std::get<1>(v1) == 42);380 }381 {382 using V = std::variant<int, MoveAssign, unsigned>;383 int move_construct = 0;384 int move_assign = 0;385 V v1(std::in_place_type<unsigned>, 43u);386 V v2(std::in_place_type<MoveAssign>, 42, &move_construct, &move_assign);387 V& vref = (v1 = std::move(v2));388 assert(&vref == &v1);389 assert(v1.index() == 1);390 assert(std::get<1>(v1).value == 42);391 assert(move_construct == 1);392 assert(move_assign == 0);393 }394 395 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).396 {397 struct {398 constexpr Result<long> operator()() const {399 using V = std::variant<int, long, unsigned>;400 V v(43);401 V v2(42l);402 v = std::move(v2);403 return {v.index(), std::get<1>(v)};404 }405 } test;406 constexpr auto result = test();407 static_assert(result.index == 1, "");408 static_assert(result.value == 42l, "");409 }410 {411 struct {412 constexpr Result<long> operator()() const {413 using V = std::variant<int, TMoveAssign, unsigned>;414 V v(std::in_place_type<unsigned>, 43u);415 V v2(std::in_place_type<TMoveAssign>, 42);416 v = std::move(v2);417 return {v.index(), std::get<1>(v).value};418 }419 } test;420 constexpr auto result = test();421 static_assert(result.index == 1, "");422 static_assert(result.value == 42, "");423 }424}425 426void test_assignment_throw() {427#ifndef TEST_HAS_NO_EXCEPTIONS428 using MET = MakeEmptyT;429 // same index430 {431 using V = std::variant<int, MET, std::string>;432 V v1(std::in_place_type<MET>);433 MET& mref = std::get<1>(v1);434 V v2(std::in_place_type<MET>);435 try {436 v1 = std::move(v2);437 assert(false);438 } catch (...) {439 }440 assert(v1.index() == 1);441 assert(&std::get<1>(v1) == &mref);442 }443 444 // different indices445 {446 using V = std::variant<int, MET, std::string>;447 V v1(std::in_place_type<int>);448 V v2(std::in_place_type<MET>);449 try {450 v1 = std::move(v2);451 assert(false);452 } catch (...) {453 }454 assert(v1.valueless_by_exception());455 assert(v1.index() == std::variant_npos);456 }457 {458 using V = std::variant<int, MET, std::string>;459 V v1(std::in_place_type<MET>);460 V v2(std::in_place_type<std::string>, "hello");461 V& vref = (v1 = std::move(v2));462 assert(&vref == &v1);463 assert(v1.index() == 2);464 assert(std::get<2>(v1) == "hello");465 }466#endif // TEST_HAS_NO_EXCEPTIONS467}468 469template <std::size_t NewIdx, class T, class ValueType>470constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) {471 using Variant = std::decay_t<T>;472 Variant v2(std::forward<ValueType>(new_value));473 const auto cp = v2;474 v = std::move(v2);475 assert(v.index() == NewIdx);476 assert(std::get<NewIdx>(v) == std::get<NewIdx>(cp));477}478 479constexpr void test_constexpr_move_assignment_trivial() {480 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).481 using V = std::variant<long, void*, int>;482 static_assert(std::is_trivially_copyable<V>::value, "");483 static_assert(std::is_trivially_move_assignable<V>::value, "");484 test_constexpr_assign_imp<0>(V(42l), 101l);485 test_constexpr_assign_imp<0>(V(nullptr), 101l);486 test_constexpr_assign_imp<1>(V(42l), nullptr);487 test_constexpr_assign_imp<2>(V(42l), 101);488}489 490struct NonTrivialMoveAssign {491 int i = 0;492 constexpr NonTrivialMoveAssign(int ii) : i(ii) {}493 constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default;494 constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {}495 constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default;496 constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) {497 i = o.i;498 return *this;499 }500 TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default;501 friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; }502};503 504TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() {505 using V = std::variant<long, void*, NonTrivialMoveAssign>;506 static_assert(!std::is_trivially_copyable<V>::value);507 static_assert(!std::is_trivially_move_assignable<V>::value);508 test_constexpr_assign_imp<0>(V(42l), 101l);509 test_constexpr_assign_imp<0>(V(nullptr), 101l);510 test_constexpr_assign_imp<1>(V(42l), nullptr);511 test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5));512 test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5));513}514 515void non_constexpr_test() {516 test_move_assignment_empty_empty();517 test_move_assignment_non_empty_empty();518 test_move_assignment_empty_non_empty();519 test_assignment_throw();520}521 522constexpr bool cxx17_constexpr_test() {523 test_move_assignment_sfinae();524 test_move_assignment_noexcept();525 test_constexpr_move_assignment_trivial();526 527 return true;528}529 530TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() {531 test_move_assignment_same_index();532 test_move_assignment_different_index();533 test_constexpr_move_assignment_non_trivial();534 535 return true;536}537 538int main(int, char**) {539 non_constexpr_test();540 cxx17_constexpr_test();541 cxx20_constexpr_test();542 543 static_assert(cxx17_constexpr_test());544#if TEST_STD_VER >= 20545 static_assert(cxx20_constexpr_test());546#endif547 return 0;548}549