1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // The LLVM Compiler Infrastructure
5 //
6 // This file is dual licensed under the MIT and the University of Illinois Open
7 // Source Licenses. See LICENSE.TXT for details.
8 //
9 //===----------------------------------------------------------------------===//
10
11 // UNSUPPORTED: c++98, c++03, c++11, c++14
12
13 // The following compilers don't generate constexpr special members correctly.
14 // XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8
15 // XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0
16
17 // XFAIL: availability=macosx10.13
18 // XFAIL: availability=macosx10.12
19 // XFAIL: availability=macosx10.11
20 // XFAIL: availability=macosx10.10
21 // XFAIL: availability=macosx10.9
22 // XFAIL: availability=macosx10.8
23 // XFAIL: availability=macosx10.7
24
25 // <variant>
26
27 // template <class ...Types> class variant;
28
29 // variant& operator=(variant const&); // constexpr in C++20
30
31 #include <cassert>
32 #include <string>
33 #include <type_traits>
34 #include <variant>
35
36 #include "test_macros.h"
37
38 struct NoCopy {
39 NoCopy(const NoCopy &) = delete;
40 NoCopy &operator=(const NoCopy &) = default;
41 };
42
43 struct CopyOnly {
44 CopyOnly(const CopyOnly &) = default;
45 CopyOnly(CopyOnly &&) = delete;
46 CopyOnly &operator=(const CopyOnly &) = default;
47 CopyOnly &operator=(CopyOnly &&) = delete;
48 };
49
50 struct MoveOnly {
51 MoveOnly(const MoveOnly &) = delete;
52 MoveOnly(MoveOnly &&) = default;
53 MoveOnly &operator=(const MoveOnly &) = default;
54 };
55
56 struct MoveOnlyNT {
57 MoveOnlyNT(const MoveOnlyNT &) = delete;
MoveOnlyNTMoveOnlyNT58 MoveOnlyNT(MoveOnlyNT &&) {}
59 MoveOnlyNT &operator=(const MoveOnlyNT &) = default;
60 };
61
62 struct CopyAssign {
63 static int alive;
64 static int copy_construct;
65 static int copy_assign;
66 static int move_construct;
67 static int move_assign;
resetCopyAssign68 static void reset() {
69 copy_construct = copy_assign = move_construct = move_assign = alive = 0;
70 }
CopyAssignCopyAssign71 CopyAssign(int v) : value(v) { ++alive; }
CopyAssignCopyAssign72 CopyAssign(const CopyAssign &o) : value(o.value) {
73 ++alive;
74 ++copy_construct;
75 }
CopyAssignCopyAssign76 CopyAssign(CopyAssign &&o) noexcept : value(o.value) {
77 o.value = -1;
78 ++alive;
79 ++move_construct;
80 }
operator =CopyAssign81 CopyAssign &operator=(const CopyAssign &o) {
82 value = o.value;
83 ++copy_assign;
84 return *this;
85 }
operator =CopyAssign86 CopyAssign &operator=(CopyAssign &&o) noexcept {
87 value = o.value;
88 o.value = -1;
89 ++move_assign;
90 return *this;
91 }
~CopyAssignCopyAssign92 ~CopyAssign() { --alive; }
93 int value;
94 };
95
96 int CopyAssign::alive = 0;
97 int CopyAssign::copy_construct = 0;
98 int CopyAssign::copy_assign = 0;
99 int CopyAssign::move_construct = 0;
100 int CopyAssign::move_assign = 0;
101
102 struct CopyMaybeThrows {
103 CopyMaybeThrows(const CopyMaybeThrows &);
104 CopyMaybeThrows &operator=(const CopyMaybeThrows &);
105 };
106 struct CopyDoesThrow {
107 CopyDoesThrow(const CopyDoesThrow &) noexcept(false);
108 CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false);
109 };
110
111
112 struct NTCopyAssign {
NTCopyAssignNTCopyAssign113 constexpr NTCopyAssign(int v) : value(v) {}
114 NTCopyAssign(const NTCopyAssign &) = default;
115 NTCopyAssign(NTCopyAssign &&) = default;
operator =NTCopyAssign116 NTCopyAssign &operator=(const NTCopyAssign &that) {
117 value = that.value;
118 return *this;
119 };
120 NTCopyAssign &operator=(NTCopyAssign &&) = delete;
121 int value;
122 };
123
124 static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, "");
125 static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");
126
127 struct TCopyAssign {
TCopyAssignTCopyAssign128 constexpr TCopyAssign(int v) : value(v) {}
129 TCopyAssign(const TCopyAssign &) = default;
130 TCopyAssign(TCopyAssign &&) = default;
131 TCopyAssign &operator=(const TCopyAssign &) = default;
132 TCopyAssign &operator=(TCopyAssign &&) = delete;
133 int value;
134 };
135
136 static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");
137
138 struct TCopyAssignNTMoveAssign {
TCopyAssignNTMoveAssignTCopyAssignNTMoveAssign139 constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
140 TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default;
141 TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default;
142 TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default;
operator =TCopyAssignNTMoveAssign143 TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) {
144 value = that.value;
145 that.value = -1;
146 return *this;
147 }
148 int value;
149 };
150
151 static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");
152
153 #ifndef TEST_HAS_NO_EXCEPTIONS
154 struct CopyThrows {
155 CopyThrows() = default;
CopyThrowsCopyThrows156 CopyThrows(const CopyThrows &) { throw 42; }
operator =CopyThrows157 CopyThrows &operator=(const CopyThrows &) { throw 42; }
158 };
159
160 struct CopyCannotThrow {
161 static int alive;
CopyCannotThrowCopyCannotThrow162 CopyCannotThrow() { ++alive; }
CopyCannotThrowCopyCannotThrow163 CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; }
CopyCannotThrowCopyCannotThrow164 CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); }
165 CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default;
operator =CopyCannotThrow166 CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; }
167 };
168
169 int CopyCannotThrow::alive = 0;
170
171 struct MoveThrows {
172 static int alive;
MoveThrowsMoveThrows173 MoveThrows() { ++alive; }
MoveThrowsMoveThrows174 MoveThrows(const MoveThrows &) { ++alive; }
MoveThrowsMoveThrows175 MoveThrows(MoveThrows &&) { throw 42; }
operator =MoveThrows176 MoveThrows &operator=(const MoveThrows &) { return *this; }
operator =MoveThrows177 MoveThrows &operator=(MoveThrows &&) { throw 42; }
~MoveThrowsMoveThrows178 ~MoveThrows() { --alive; }
179 };
180
181 int MoveThrows::alive = 0;
182
183 struct MakeEmptyT {
184 static int alive;
MakeEmptyTMakeEmptyT185 MakeEmptyT() { ++alive; }
MakeEmptyTMakeEmptyT186 MakeEmptyT(const MakeEmptyT &) {
187 ++alive;
188 // Don't throw from the copy constructor since variant's assignment
189 // operator performs a copy before committing to the assignment.
190 }
MakeEmptyTMakeEmptyT191 MakeEmptyT(MakeEmptyT &&) { throw 42; }
operator =MakeEmptyT192 MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
operator =MakeEmptyT193 MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
~MakeEmptyTMakeEmptyT194 ~MakeEmptyT() { --alive; }
195 };
196
197 int MakeEmptyT::alive = 0;
198
makeEmpty(Variant & v)199 template <class Variant> void makeEmpty(Variant &v) {
200 Variant v2(std::in_place_type<MakeEmptyT>);
201 try {
202 v = std::move(v2);
203 assert(false);
204 } catch (...) {
205 assert(v.valueless_by_exception());
206 }
207 }
208 #endif // TEST_HAS_NO_EXCEPTIONS
209
test_copy_assignment_not_noexcept()210 void test_copy_assignment_not_noexcept() {
211 {
212 using V = std::variant<CopyMaybeThrows>;
213 static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
214 }
215 {
216 using V = std::variant<int, CopyDoesThrow>;
217 static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
218 }
219 }
220
test_copy_assignment_sfinae()221 void test_copy_assignment_sfinae() {
222 {
223 using V = std::variant<int, long>;
224 static_assert(std::is_copy_assignable<V>::value, "");
225 }
226 {
227 using V = std::variant<int, CopyOnly>;
228 static_assert(std::is_copy_assignable<V>::value, "");
229 }
230 {
231 using V = std::variant<int, NoCopy>;
232 static_assert(!std::is_copy_assignable<V>::value, "");
233 }
234 {
235 using V = std::variant<int, MoveOnly>;
236 static_assert(!std::is_copy_assignable<V>::value, "");
237 }
238 {
239 using V = std::variant<int, MoveOnlyNT>;
240 static_assert(!std::is_copy_assignable<V>::value, "");
241 }
242
243 // Make sure we properly propagate triviality (see P0602R4).
244 #if TEST_STD_VER > 17
245 {
246 using V = std::variant<int, long>;
247 static_assert(std::is_trivially_copy_assignable<V>::value, "");
248 }
249 {
250 using V = std::variant<int, NTCopyAssign>;
251 static_assert(!std::is_trivially_copy_assignable<V>::value, "");
252 static_assert(std::is_copy_assignable<V>::value, "");
253 }
254 {
255 using V = std::variant<int, TCopyAssign>;
256 static_assert(std::is_trivially_copy_assignable<V>::value, "");
257 }
258 {
259 using V = std::variant<int, TCopyAssignNTMoveAssign>;
260 static_assert(std::is_trivially_copy_assignable<V>::value, "");
261 }
262 {
263 using V = std::variant<int, CopyOnly>;
264 static_assert(std::is_trivially_copy_assignable<V>::value, "");
265 }
266 #endif // > C++17
267 }
268
test_copy_assignment_empty_empty()269 void test_copy_assignment_empty_empty() {
270 #ifndef TEST_HAS_NO_EXCEPTIONS
271 using MET = MakeEmptyT;
272 {
273 using V = std::variant<int, long, MET>;
274 V v1(std::in_place_index<0>);
275 makeEmpty(v1);
276 V v2(std::in_place_index<0>);
277 makeEmpty(v2);
278 V &vref = (v1 = v2);
279 assert(&vref == &v1);
280 assert(v1.valueless_by_exception());
281 assert(v1.index() == std::variant_npos);
282 }
283 #endif // TEST_HAS_NO_EXCEPTIONS
284 }
285
test_copy_assignment_non_empty_empty()286 void test_copy_assignment_non_empty_empty() {
287 #ifndef TEST_HAS_NO_EXCEPTIONS
288 using MET = MakeEmptyT;
289 {
290 using V = std::variant<int, MET>;
291 V v1(std::in_place_index<0>, 42);
292 V v2(std::in_place_index<0>);
293 makeEmpty(v2);
294 V &vref = (v1 = v2);
295 assert(&vref == &v1);
296 assert(v1.valueless_by_exception());
297 assert(v1.index() == std::variant_npos);
298 }
299 {
300 using V = std::variant<int, MET, std::string>;
301 V v1(std::in_place_index<2>, "hello");
302 V v2(std::in_place_index<0>);
303 makeEmpty(v2);
304 V &vref = (v1 = v2);
305 assert(&vref == &v1);
306 assert(v1.valueless_by_exception());
307 assert(v1.index() == std::variant_npos);
308 }
309 #endif // TEST_HAS_NO_EXCEPTIONS
310 }
311
test_copy_assignment_empty_non_empty()312 void test_copy_assignment_empty_non_empty() {
313 #ifndef TEST_HAS_NO_EXCEPTIONS
314 using MET = MakeEmptyT;
315 {
316 using V = std::variant<int, MET>;
317 V v1(std::in_place_index<0>);
318 makeEmpty(v1);
319 V v2(std::in_place_index<0>, 42);
320 V &vref = (v1 = v2);
321 assert(&vref == &v1);
322 assert(v1.index() == 0);
323 assert(std::get<0>(v1) == 42);
324 }
325 {
326 using V = std::variant<int, MET, std::string>;
327 V v1(std::in_place_index<0>);
328 makeEmpty(v1);
329 V v2(std::in_place_type<std::string>, "hello");
330 V &vref = (v1 = v2);
331 assert(&vref == &v1);
332 assert(v1.index() == 2);
333 assert(std::get<2>(v1) == "hello");
334 }
335 #endif // TEST_HAS_NO_EXCEPTIONS
336 }
337
338 template <typename T> struct Result { size_t index; T value; };
339
test_copy_assignment_same_index()340 void test_copy_assignment_same_index() {
341 {
342 using V = std::variant<int>;
343 V v1(43);
344 V v2(42);
345 V &vref = (v1 = v2);
346 assert(&vref == &v1);
347 assert(v1.index() == 0);
348 assert(std::get<0>(v1) == 42);
349 }
350 {
351 using V = std::variant<int, long, unsigned>;
352 V v1(43l);
353 V v2(42l);
354 V &vref = (v1 = v2);
355 assert(&vref == &v1);
356 assert(v1.index() == 1);
357 assert(std::get<1>(v1) == 42);
358 }
359 {
360 using V = std::variant<int, CopyAssign, unsigned>;
361 V v1(std::in_place_type<CopyAssign>, 43);
362 V v2(std::in_place_type<CopyAssign>, 42);
363 CopyAssign::reset();
364 V &vref = (v1 = v2);
365 assert(&vref == &v1);
366 assert(v1.index() == 1);
367 assert(std::get<1>(v1).value == 42);
368 assert(CopyAssign::copy_construct == 0);
369 assert(CopyAssign::move_construct == 0);
370 assert(CopyAssign::copy_assign == 1);
371 }
372 #ifndef TEST_HAS_NO_EXCEPTIONS
373 using MET = MakeEmptyT;
374 {
375 using V = std::variant<int, MET, std::string>;
376 V v1(std::in_place_type<MET>);
377 MET &mref = std::get<1>(v1);
378 V v2(std::in_place_type<MET>);
379 try {
380 v1 = v2;
381 assert(false);
382 } catch (...) {
383 }
384 assert(v1.index() == 1);
385 assert(&std::get<1>(v1) == &mref);
386 }
387 #endif // TEST_HAS_NO_EXCEPTIONS
388
389 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
390 #if TEST_STD_VER > 17
391 {
392 struct {
393 constexpr Result<int> operator()() const {
394 using V = std::variant<int>;
395 V v(43);
396 V v2(42);
397 v = v2;
398 return {v.index(), std::get<0>(v)};
399 }
400 } test;
401 constexpr auto result = test();
402 static_assert(result.index == 0, "");
403 static_assert(result.value == 42, "");
404 }
405 {
406 struct {
407 constexpr Result<long> operator()() const {
408 using V = std::variant<int, long, unsigned>;
409 V v(43l);
410 V v2(42l);
411 v = v2;
412 return {v.index(), std::get<1>(v)};
413 }
414 } test;
415 constexpr auto result = test();
416 static_assert(result.index == 1, "");
417 static_assert(result.value == 42l, "");
418 }
419 {
420 struct {
421 constexpr Result<int> operator()() const {
422 using V = std::variant<int, TCopyAssign, unsigned>;
423 V v(std::in_place_type<TCopyAssign>, 43);
424 V v2(std::in_place_type<TCopyAssign>, 42);
425 v = v2;
426 return {v.index(), std::get<1>(v).value};
427 }
428 } test;
429 constexpr auto result = test();
430 static_assert(result.index == 1, "");
431 static_assert(result.value == 42, "");
432 }
433 {
434 struct {
435 constexpr Result<int> operator()() const {
436 using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>;
437 V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43);
438 V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42);
439 v = v2;
440 return {v.index(), std::get<1>(v).value};
441 }
442 } test;
443 constexpr auto result = test();
444 static_assert(result.index == 1, "");
445 static_assert(result.value == 42, "");
446 }
447 #endif // > C++17
448 }
449
test_copy_assignment_different_index()450 void test_copy_assignment_different_index() {
451 {
452 using V = std::variant<int, long, unsigned>;
453 V v1(43);
454 V v2(42l);
455 V &vref = (v1 = v2);
456 assert(&vref == &v1);
457 assert(v1.index() == 1);
458 assert(std::get<1>(v1) == 42);
459 }
460 {
461 using V = std::variant<int, CopyAssign, unsigned>;
462 CopyAssign::reset();
463 V v1(std::in_place_type<unsigned>, 43);
464 V v2(std::in_place_type<CopyAssign>, 42);
465 assert(CopyAssign::copy_construct == 0);
466 assert(CopyAssign::move_construct == 0);
467 assert(CopyAssign::alive == 1);
468 V &vref = (v1 = v2);
469 assert(&vref == &v1);
470 assert(v1.index() == 1);
471 assert(std::get<1>(v1).value == 42);
472 assert(CopyAssign::alive == 2);
473 assert(CopyAssign::copy_construct == 1);
474 assert(CopyAssign::move_construct == 1);
475 assert(CopyAssign::copy_assign == 0);
476 }
477 #ifndef TEST_HAS_NO_EXCEPTIONS
478 {
479 using V = std::variant<int, CopyThrows, std::string>;
480 V v1(std::in_place_type<std::string>, "hello");
481 V v2(std::in_place_type<CopyThrows>);
482 try {
483 v1 = v2;
484 assert(false);
485 } catch (...) { /* ... */
486 }
487 // Test that copy construction is used directly if move construction may throw,
488 // resulting in a valueless variant if copy throws.
489 assert(v1.valueless_by_exception());
490 }
491 {
492 using V = std::variant<int, MoveThrows, std::string>;
493 V v1(std::in_place_type<std::string>, "hello");
494 V v2(std::in_place_type<MoveThrows>);
495 assert(MoveThrows::alive == 1);
496 // Test that copy construction is used directly if move construction may throw.
497 v1 = v2;
498 assert(v1.index() == 1);
499 assert(v2.index() == 1);
500 assert(MoveThrows::alive == 2);
501 }
502 {
503 // Test that direct copy construction is preferred when it cannot throw.
504 using V = std::variant<int, CopyCannotThrow, std::string>;
505 V v1(std::in_place_type<std::string>, "hello");
506 V v2(std::in_place_type<CopyCannotThrow>);
507 assert(CopyCannotThrow::alive == 1);
508 v1 = v2;
509 assert(v1.index() == 1);
510 assert(v2.index() == 1);
511 assert(CopyCannotThrow::alive == 2);
512 }
513 {
514 using V = std::variant<int, CopyThrows, std::string>;
515 V v1(std::in_place_type<CopyThrows>);
516 V v2(std::in_place_type<std::string>, "hello");
517 V &vref = (v1 = v2);
518 assert(&vref == &v1);
519 assert(v1.index() == 2);
520 assert(std::get<2>(v1) == "hello");
521 assert(v2.index() == 2);
522 assert(std::get<2>(v2) == "hello");
523 }
524 {
525 using V = std::variant<int, MoveThrows, std::string>;
526 V v1(std::in_place_type<MoveThrows>);
527 V v2(std::in_place_type<std::string>, "hello");
528 V &vref = (v1 = v2);
529 assert(&vref == &v1);
530 assert(v1.index() == 2);
531 assert(std::get<2>(v1) == "hello");
532 assert(v2.index() == 2);
533 assert(std::get<2>(v2) == "hello");
534 }
535 #endif // TEST_HAS_NO_EXCEPTIONS
536
537 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
538 #if TEST_STD_VER > 17
539 {
540 struct {
541 constexpr Result<long> operator()() const {
542 using V = std::variant<int, long, unsigned>;
543 V v(43);
544 V v2(42l);
545 v = v2;
546 return {v.index(), std::get<1>(v)};
547 }
548 } test;
549 constexpr auto result = test();
550 static_assert(result.index == 1, "");
551 static_assert(result.value == 42l, "");
552 }
553 {
554 struct {
555 constexpr Result<int> operator()() const {
556 using V = std::variant<int, TCopyAssign, unsigned>;
557 V v(std::in_place_type<unsigned>, 43);
558 V v2(std::in_place_type<TCopyAssign>, 42);
559 v = v2;
560 return {v.index(), std::get<1>(v).value};
561 }
562 } test;
563 constexpr auto result = test();
564 static_assert(result.index == 1, "");
565 static_assert(result.value == 42, "");
566 }
567 #endif // > C++17
568 }
569
570 template <size_t NewIdx, class ValueType>
test_constexpr_assign_imp(std::variant<long,void *,int> && v,ValueType && new_value)571 constexpr bool test_constexpr_assign_imp(
572 std::variant<long, void*, int>&& v, ValueType&& new_value)
573 {
574 const std::variant<long, void*, int> cp(
575 std::forward<ValueType>(new_value));
576 v = cp;
577 return v.index() == NewIdx &&
578 std::get<NewIdx>(v) == std::get<NewIdx>(cp);
579 }
580
test_constexpr_copy_assignment()581 void test_constexpr_copy_assignment() {
582 // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
583 #if TEST_STD_VER > 17
584 using V = std::variant<long, void*, int>;
585 static_assert(std::is_trivially_copyable<V>::value, "");
586 static_assert(std::is_trivially_copy_assignable<V>::value, "");
587 static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), "");
588 static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), "");
589 static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), "");
590 static_assert(test_constexpr_assign_imp<2>(V(42l), 101), "");
591 #endif // > C++17
592 }
593
main()594 int main() {
595 test_copy_assignment_empty_empty();
596 test_copy_assignment_non_empty_empty();
597 test_copy_assignment_empty_non_empty();
598 test_copy_assignment_same_index();
599 test_copy_assignment_different_index();
600 test_copy_assignment_sfinae();
601 test_copy_assignment_not_noexcept();
602 test_constexpr_copy_assignment();
603 }
604