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