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