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