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