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