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