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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14
10
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
26 struct NoCopy {
27 NoCopy(const NoCopy&) = delete;
28 NoCopy& operator=(const NoCopy&) = default;
29 };
30
31 struct CopyOnly {
32 CopyOnly(const CopyOnly&) = default;
33 CopyOnly(CopyOnly&&) = delete;
34 CopyOnly& operator=(const CopyOnly&) = default;
35 CopyOnly& operator=(CopyOnly&&) = delete;
36 };
37
38 struct MoveOnly {
39 MoveOnly(const MoveOnly&) = delete;
40 MoveOnly(MoveOnly&&) = default;
41 MoveOnly& operator=(const MoveOnly&) = delete;
42 MoveOnly& operator=(MoveOnly&&) = default;
43 };
44
45 struct MoveOnlyNT {
46 MoveOnlyNT(const MoveOnlyNT&) = delete;
MoveOnlyNTMoveOnlyNT47 MoveOnlyNT(MoveOnlyNT&&) {}
48 MoveOnlyNT& operator=(const MoveOnlyNT&) = delete;
49 MoveOnlyNT& operator=(MoveOnlyNT&&) = default;
50 };
51
52 struct MoveOnlyOddNothrow {
MoveOnlyOddNothrowMoveOnlyOddNothrow53 MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {}
54 MoveOnlyOddNothrow(const MoveOnlyOddNothrow&) = delete;
55 MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default;
56 MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&) = delete;
57 };
58
59 struct MoveAssignOnly {
60 MoveAssignOnly(MoveAssignOnly&&) = delete;
61 MoveAssignOnly& operator=(MoveAssignOnly&&) = default;
62 };
63
64 struct MoveAssign {
MoveAssignMoveAssign65 constexpr MoveAssign(int v, int* move_ctor, int* move_assi)
66 : value(v), move_construct(move_ctor), move_assign(move_assi) {}
MoveAssignMoveAssign67 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 }
operator =MoveAssign71 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
84 struct NTMoveAssign {
NTMoveAssignNTMoveAssign85 constexpr NTMoveAssign(int v) : value(v) {}
86 NTMoveAssign(const NTMoveAssign&) = default;
87 NTMoveAssign(NTMoveAssign&&) = default;
88 NTMoveAssign& operator=(const NTMoveAssign& that) = default;
operator =NTMoveAssign89 NTMoveAssign& operator=(NTMoveAssign&& that) {
90 value = that.value;
91 that.value = -1;
92 return *this;
93 };
94 int value;
95 };
96
97 static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
98 static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
99
100 struct TMoveAssign {
TMoveAssignTMoveAssign101 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
109 static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
110
111 struct TMoveAssignNTCopyAssign {
TMoveAssignNTCopyAssignTMoveAssignNTCopyAssign112 constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
113 TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default;
114 TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&) = default;
operator =TMoveAssignNTCopyAssign115 TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) {
116 value = that.value;
117 return *this;
118 }
119 TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default;
120 int value;
121 };
122
123 static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
124
125 struct TrivialCopyNontrivialMove {
126 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
TrivialCopyNontrivialMoveTrivialCopyNontrivialMove127 TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
128 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
operator =TrivialCopyNontrivialMove129 TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; }
130 };
131
132 static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
133 static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
134
test_move_assignment_noexcept()135 constexpr 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
test_move_assignment_sfinae()162 constexpr 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 provide
185 // 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
test_move_assignment_empty_empty()218 void test_move_assignment_empty_empty() {
219 #ifndef TEST_HAS_NO_EXCEPTIONS
220 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_EXCEPTIONS
233 }
234
test_move_assignment_non_empty_empty()235 void test_move_assignment_non_empty_empty() {
236 #ifndef TEST_HAS_NO_EXCEPTIONS
237 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_EXCEPTIONS
259 }
260
test_move_assignment_empty_non_empty()261 void test_move_assignment_empty_non_empty() {
262 #ifndef TEST_HAS_NO_EXCEPTIONS
263 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_EXCEPTIONS
285 }
286
287 template <typename T>
288 struct Result {
289 std::size_t index;
290 T value;
291 };
292
test_move_assignment_same_index()293 TEST_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
test_move_assignment_different_index()371 TEST_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
test_assignment_throw()426 void test_assignment_throw() {
427 #ifndef TEST_HAS_NO_EXCEPTIONS
428 using MET = MakeEmptyT;
429 // same index
430 {
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 indices
445 {
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_EXCEPTIONS
467 }
468
469 template <std::size_t NewIdx, class T, class ValueType>
test_constexpr_assign_imp(T && v,ValueType && new_value)470 constexpr 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
test_constexpr_move_assignment_trivial()479 constexpr 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
490 struct NonTrivialMoveAssign {
491 int i = 0;
NonTrivialMoveAssignNonTrivialMoveAssign492 constexpr NonTrivialMoveAssign(int ii) : i(ii) {}
493 constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default;
NonTrivialMoveAssignNonTrivialMoveAssign494 constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {}
495 constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default;
operator =NonTrivialMoveAssign496 constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) {
497 i = o.i;
498 return *this;
499 }
500 TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default;
operator ==(const NonTrivialMoveAssign & x,const NonTrivialMoveAssign & y)501 friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; }
502 };
503
test_constexpr_move_assignment_non_trivial()504 TEST_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
non_constexpr_test()515 void 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
cxx17_constexpr_test()522 constexpr 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
cxx20_constexpr_test()530 TEST_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
main(int,char **)538 int 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 >= 20
545 static_assert(cxx20_constexpr_test());
546 #endif
547 return 0;
548 }
549