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