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: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions
14 
15 // <variant>
16 
17 // template <class ...Types> class variant;
18 
19 // variant(variant&&) noexcept(see below); // constexpr in C++20
20 
21 #include <cassert>
22 #include <string>
23 #include <type_traits>
24 #include <variant>
25 
26 #include "test_macros.h"
27 #include "test_workarounds.h"
28 
29 struct ThrowsMove {
30   ThrowsMove(ThrowsMove &&) noexcept(false) {}
31 };
32 
33 struct NoCopy {
34   NoCopy(const NoCopy &) = delete;
35 };
36 
37 struct MoveOnly {
38   int value;
39   MoveOnly(int v) : value(v) {}
40   MoveOnly(const MoveOnly &) = delete;
41   MoveOnly(MoveOnly &&) = default;
42 };
43 
44 struct MoveOnlyNT {
45   int value;
46   MoveOnlyNT(int v) : value(v) {}
47   MoveOnlyNT(const MoveOnlyNT &) = delete;
48   MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; }
49 };
50 
51 struct NTMove {
52   constexpr NTMove(int v) : value(v) {}
53   NTMove(const NTMove &) = delete;
54   NTMove(NTMove &&that) : value(that.value) { that.value = -1; }
55   int value;
56 };
57 
58 static_assert(!std::is_trivially_move_constructible<NTMove>::value, "");
59 static_assert(std::is_move_constructible<NTMove>::value, "");
60 
61 struct TMove {
62   constexpr TMove(int v) : value(v) {}
63   TMove(const TMove &) = delete;
64   TMove(TMove &&) = default;
65   int value;
66 };
67 
68 static_assert(std::is_trivially_move_constructible<TMove>::value, "");
69 
70 struct TMoveNTCopy {
71   constexpr TMoveNTCopy(int v) : value(v) {}
72   TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {}
73   TMoveNTCopy(TMoveNTCopy&&) = default;
74   int value;
75 };
76 
77 static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
78 
79 #ifndef TEST_HAS_NO_EXCEPTIONS
80 struct MakeEmptyT {
81   static int alive;
82   MakeEmptyT() { ++alive; }
83   MakeEmptyT(const MakeEmptyT &) {
84     ++alive;
85     // Don't throw from the copy constructor since variant's assignment
86     // operator performs a copy before committing to the assignment.
87   }
88   MakeEmptyT(MakeEmptyT &&) { throw 42; }
89   MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
90   MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
91   ~MakeEmptyT() { --alive; }
92 };
93 
94 int MakeEmptyT::alive = 0;
95 
96 template <class Variant> void makeEmpty(Variant &v) {
97   Variant v2(std::in_place_type<MakeEmptyT>);
98   try {
99     v = std::move(v2);
100     assert(false);
101   } catch (...) {
102     assert(v.valueless_by_exception());
103   }
104 }
105 #endif // TEST_HAS_NO_EXCEPTIONS
106 
107 void test_move_noexcept() {
108   {
109     using V = std::variant<int, long>;
110     static_assert(std::is_nothrow_move_constructible<V>::value, "");
111   }
112   {
113     using V = std::variant<int, MoveOnly>;
114     static_assert(std::is_nothrow_move_constructible<V>::value, "");
115   }
116   {
117     using V = std::variant<int, MoveOnlyNT>;
118     static_assert(!std::is_nothrow_move_constructible<V>::value, "");
119   }
120   {
121     using V = std::variant<int, ThrowsMove>;
122     static_assert(!std::is_nothrow_move_constructible<V>::value, "");
123   }
124 }
125 
126 void test_move_ctor_sfinae() {
127   {
128     using V = std::variant<int, long>;
129     static_assert(std::is_move_constructible<V>::value, "");
130   }
131   {
132     using V = std::variant<int, MoveOnly>;
133     static_assert(std::is_move_constructible<V>::value, "");
134   }
135   {
136     using V = std::variant<int, MoveOnlyNT>;
137     static_assert(std::is_move_constructible<V>::value, "");
138   }
139   {
140     using V = std::variant<int, NoCopy>;
141     static_assert(!std::is_move_constructible<V>::value, "");
142   }
143 
144   // Make sure we properly propagate triviality (see P0602R4).
145 #if TEST_STD_VER > 17
146   {
147     using V = std::variant<int, long>;
148     static_assert(std::is_trivially_move_constructible<V>::value, "");
149   }
150   {
151     using V = std::variant<int, NTMove>;
152     static_assert(!std::is_trivially_move_constructible<V>::value, "");
153     static_assert(std::is_move_constructible<V>::value, "");
154   }
155   {
156     using V = std::variant<int, TMove>;
157     static_assert(std::is_trivially_move_constructible<V>::value, "");
158   }
159   {
160     using V = std::variant<int, TMoveNTCopy>;
161     static_assert(std::is_trivially_move_constructible<V>::value, "");
162   }
163 #endif // > C++17
164 }
165 
166 template <typename T>
167 struct Result { size_t index; T value; };
168 
169 void test_move_ctor_basic() {
170   {
171     std::variant<int> v(std::in_place_index<0>, 42);
172     std::variant<int> v2 = std::move(v);
173     assert(v2.index() == 0);
174     assert(std::get<0>(v2) == 42);
175   }
176   {
177     std::variant<int, long> v(std::in_place_index<1>, 42);
178     std::variant<int, long> v2 = std::move(v);
179     assert(v2.index() == 1);
180     assert(std::get<1>(v2) == 42);
181   }
182   {
183     std::variant<MoveOnly> v(std::in_place_index<0>, 42);
184     assert(v.index() == 0);
185     std::variant<MoveOnly> v2(std::move(v));
186     assert(v2.index() == 0);
187     assert(std::get<0>(v2).value == 42);
188   }
189   {
190     std::variant<int, MoveOnly> v(std::in_place_index<1>, 42);
191     assert(v.index() == 1);
192     std::variant<int, MoveOnly> v2(std::move(v));
193     assert(v2.index() == 1);
194     assert(std::get<1>(v2).value == 42);
195   }
196   {
197     std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42);
198     assert(v.index() == 0);
199     std::variant<MoveOnlyNT> v2(std::move(v));
200     assert(v2.index() == 0);
201     assert(std::get<0>(v).value == -1);
202     assert(std::get<0>(v2).value == 42);
203   }
204   {
205     std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42);
206     assert(v.index() == 1);
207     std::variant<int, MoveOnlyNT> v2(std::move(v));
208     assert(v2.index() == 1);
209     assert(std::get<1>(v).value == -1);
210     assert(std::get<1>(v2).value == 42);
211   }
212 
213   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
214 #if TEST_STD_VER > 17
215   {
216     struct {
217       constexpr Result<int> operator()() const {
218         std::variant<int> v(std::in_place_index<0>, 42);
219         std::variant<int> v2 = std::move(v);
220         return {v2.index(), std::get<0>(std::move(v2))};
221       }
222     } test;
223     constexpr auto result = test();
224     static_assert(result.index == 0, "");
225     static_assert(result.value == 42, "");
226   }
227   {
228     struct {
229       constexpr Result<long> operator()() const {
230         std::variant<int, long> v(std::in_place_index<1>, 42);
231         std::variant<int, long> v2 = std::move(v);
232         return {v2.index(), std::get<1>(std::move(v2))};
233       }
234     } test;
235     constexpr auto result = test();
236     static_assert(result.index == 1, "");
237     static_assert(result.value == 42, "");
238   }
239   {
240     struct {
241       constexpr Result<TMove> operator()() const {
242         std::variant<TMove> v(std::in_place_index<0>, 42);
243         std::variant<TMove> v2(std::move(v));
244         return {v2.index(), std::get<0>(std::move(v2))};
245       }
246     } test;
247     constexpr auto result = test();
248     static_assert(result.index == 0, "");
249     static_assert(result.value.value == 42, "");
250   }
251   {
252     struct {
253       constexpr Result<TMove> operator()() const {
254         std::variant<int, TMove> v(std::in_place_index<1>, 42);
255         std::variant<int, TMove> v2(std::move(v));
256         return {v2.index(), std::get<1>(std::move(v2))};
257       }
258     } test;
259     constexpr auto result = test();
260     static_assert(result.index == 1, "");
261     static_assert(result.value.value == 42, "");
262   }
263   {
264     struct {
265       constexpr Result<TMoveNTCopy> operator()() const {
266         std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42);
267         std::variant<TMoveNTCopy> v2(std::move(v));
268         return {v2.index(), std::get<0>(std::move(v2))};
269       }
270     } test;
271     constexpr auto result = test();
272     static_assert(result.index == 0, "");
273     static_assert(result.value.value == 42, "");
274   }
275   {
276     struct {
277       constexpr Result<TMoveNTCopy> operator()() const {
278         std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42);
279         std::variant<int, TMoveNTCopy> v2(std::move(v));
280         return {v2.index(), std::get<1>(std::move(v2))};
281       }
282     } test;
283     constexpr auto result = test();
284     static_assert(result.index == 1, "");
285     static_assert(result.value.value == 42, "");
286   }
287 #endif // > C++17
288 }
289 
290 void test_move_ctor_valueless_by_exception() {
291 #ifndef TEST_HAS_NO_EXCEPTIONS
292   using V = std::variant<int, MakeEmptyT>;
293   V v1;
294   makeEmpty(v1);
295   V v(std::move(v1));
296   assert(v.valueless_by_exception());
297 #endif // TEST_HAS_NO_EXCEPTIONS
298 }
299 
300 template <size_t Idx>
301 constexpr bool test_constexpr_ctor_imp(std::variant<long, void*, const int> const& v) {
302   auto copy = v;
303   auto v2 = std::move(copy);
304   return v2.index() == v.index() &&
305          v2.index() == Idx &&
306         std::get<Idx>(v2) == std::get<Idx>(v);
307 }
308 
309 void test_constexpr_move_ctor() {
310   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
311 #if TEST_STD_VER > 17
312   using V = std::variant<long, void*, const int>;
313 #ifdef TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
314   static_assert(std::is_trivially_destructible<V>::value, "");
315   static_assert(std::is_trivially_copy_constructible<V>::value, "");
316   static_assert(std::is_trivially_move_constructible<V>::value, "");
317   static_assert(!std::is_copy_assignable<V>::value, "");
318   static_assert(!std::is_move_assignable<V>::value, "");
319 #else // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
320   static_assert(std::is_trivially_copyable<V>::value, "");
321 #endif // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
322   static_assert(std::is_trivially_move_constructible<V>::value, "");
323   static_assert(test_constexpr_ctor_imp<0>(V(42l)), "");
324   static_assert(test_constexpr_ctor_imp<1>(V(nullptr)), "");
325   static_assert(test_constexpr_ctor_imp<2>(V(101)), "");
326 #endif // > C++17
327 }
328 
329 int main(int, char**) {
330   test_move_ctor_basic();
331   test_move_ctor_valueless_by_exception();
332   test_move_noexcept();
333   test_move_ctor_sfinae();
334   test_constexpr_move_ctor();
335 
336   return 0;
337 }
338