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