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