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