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