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 | // <variant> |
12 | |
13 | // template <class ...Types> class variant; |
14 | |
15 | // template <class T> |
16 | // variant& operator=(T&&) noexcept(see below); |
17 | |
18 | #include <cassert> |
19 | #include <string> |
20 | #include <type_traits> |
21 | #include <variant> |
22 | #include <vector> |
23 | #include <memory> |
24 | |
25 | #include "test_macros.h" |
26 | #include "variant_test_helpers.h" |
27 | |
28 | namespace MetaHelpers { |
29 | |
30 | struct Dummy { |
31 | Dummy() = default; |
32 | }; |
33 | |
34 | struct ThrowsCtorT { |
35 | ThrowsCtorT(int) noexcept(false) {} |
36 | ThrowsCtorT &operator=(int) noexcept { return *this; } |
37 | }; |
38 | |
39 | struct ThrowsAssignT { |
40 | ThrowsAssignT(int) noexcept {} |
41 | ThrowsAssignT &operator=(int) noexcept(false) { return *this; } |
42 | }; |
43 | |
44 | struct NoThrowT { |
45 | NoThrowT(int) noexcept {} |
46 | NoThrowT &operator=(int) noexcept { return *this; } |
47 | }; |
48 | |
49 | } // namespace MetaHelpers |
50 | |
51 | namespace RuntimeHelpers { |
52 | #ifndef TEST_HAS_NO_EXCEPTIONS |
53 | |
54 | struct ThrowsCtorT { |
55 | int value; |
56 | ThrowsCtorT() : value(0) {} |
57 | ThrowsCtorT(int) noexcept(false) { throw 42; } |
58 | ThrowsCtorT &operator=(int v) noexcept { |
59 | value = v; |
60 | return *this; |
61 | } |
62 | }; |
63 | |
64 | struct MoveCrashes { |
65 | int value; |
66 | MoveCrashes(int v = 0) noexcept : value{v} {} |
67 | MoveCrashes(MoveCrashes &&) noexcept { assert(false); } |
68 | MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; } |
69 | MoveCrashes &operator=(int v) noexcept { |
70 | value = v; |
71 | return *this; |
72 | } |
73 | }; |
74 | |
75 | struct ThrowsCtorTandMove { |
76 | int value; |
77 | ThrowsCtorTandMove() : value(0) {} |
78 | ThrowsCtorTandMove(int) noexcept(false) { throw 42; } |
79 | ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); } |
80 | ThrowsCtorTandMove &operator=(int v) noexcept { |
81 | value = v; |
82 | return *this; |
83 | } |
84 | }; |
85 | |
86 | struct ThrowsAssignT { |
87 | int value; |
88 | ThrowsAssignT() : value(0) {} |
89 | ThrowsAssignT(int v) noexcept : value(v) {} |
90 | ThrowsAssignT &operator=(int) noexcept(false) { throw 42; } |
91 | }; |
92 | |
93 | struct NoThrowT { |
94 | int value; |
95 | NoThrowT() : value(0) {} |
96 | NoThrowT(int v) noexcept : value(v) {} |
97 | NoThrowT &operator=(int v) noexcept { |
98 | value = v; |
99 | return *this; |
100 | } |
101 | }; |
102 | |
103 | #endif // !defined(TEST_HAS_NO_EXCEPTIONS) |
104 | } // namespace RuntimeHelpers |
105 | |
106 | void test_T_assignment_noexcept() { |
107 | using namespace MetaHelpers; |
108 | { |
109 | using V = std::variant<Dummy, NoThrowT>; |
110 | static_assert(std::is_nothrow_assignable<V, int>::value, "" ); |
111 | } |
112 | { |
113 | using V = std::variant<Dummy, ThrowsCtorT>; |
114 | static_assert(!std::is_nothrow_assignable<V, int>::value, "" ); |
115 | } |
116 | { |
117 | using V = std::variant<Dummy, ThrowsAssignT>; |
118 | static_assert(!std::is_nothrow_assignable<V, int>::value, "" ); |
119 | } |
120 | } |
121 | |
122 | void test_T_assignment_sfinae() { |
123 | { |
124 | using V = std::variant<long, long long>; |
125 | static_assert(!std::is_assignable<V, int>::value, "ambiguous" ); |
126 | } |
127 | { |
128 | using V = std::variant<std::string, std::string>; |
129 | static_assert(!std::is_assignable<V, const char *>::value, "ambiguous" ); |
130 | } |
131 | { |
132 | using V = std::variant<std::string, void *>; |
133 | static_assert(!std::is_assignable<V, int>::value, "no matching operator=" ); |
134 | } |
135 | { |
136 | using V = std::variant<std::string, float>; |
137 | static_assert(!std::is_assignable<V, int>::value, "no matching operator=" ); |
138 | } |
139 | { |
140 | using V = std::variant<std::unique_ptr<int>, bool>; |
141 | static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, |
142 | "no explicit bool in operator=" ); |
143 | struct X { |
144 | operator void*(); |
145 | }; |
146 | static_assert(!std::is_assignable<V, X>::value, "no boolean conversion in operator=" ); |
147 | static_assert(std::is_assignable<V, std::false_type>::value, "converted to bool in operator=" ); |
148 | } |
149 | { |
150 | struct X {}; |
151 | struct Y { |
152 | operator X(); |
153 | }; |
154 | using V = std::variant<X>; |
155 | static_assert(std::is_assignable<V, Y>::value, |
156 | "regression on user-defined conversions in operator=" ); |
157 | } |
158 | } |
159 | |
160 | void test_T_assignment_basic() { |
161 | { |
162 | std::variant<int> v(43); |
163 | v = 42; |
164 | assert(v.index() == 0); |
165 | assert(std::get<0>(v) == 42); |
166 | } |
167 | { |
168 | std::variant<int, long> v(43l); |
169 | v = 42; |
170 | assert(v.index() == 0); |
171 | assert(std::get<0>(v) == 42); |
172 | v = 43l; |
173 | assert(v.index() == 1); |
174 | assert(std::get<1>(v) == 43); |
175 | } |
176 | { |
177 | std::variant<unsigned, long> v; |
178 | v = 42; |
179 | assert(v.index() == 1); |
180 | assert(std::get<1>(v) == 42); |
181 | v = 43u; |
182 | assert(v.index() == 0); |
183 | assert(std::get<0>(v) == 43); |
184 | } |
185 | { |
186 | std::variant<std::string, bool> v = true; |
187 | v = "bar" ; |
188 | assert(v.index() == 0); |
189 | assert(std::get<0>(v) == "bar" ); |
190 | } |
191 | { |
192 | std::variant<bool, std::unique_ptr<int>> v; |
193 | v = nullptr; |
194 | assert(v.index() == 1); |
195 | assert(std::get<1>(v) == nullptr); |
196 | } |
197 | } |
198 | |
199 | void test_T_assignment_performs_construction() { |
200 | using namespace RuntimeHelpers; |
201 | #ifndef TEST_HAS_NO_EXCEPTIONS |
202 | { |
203 | using V = std::variant<std::string, ThrowsCtorT>; |
204 | V v(std::in_place_type<std::string>, "hello" ); |
205 | try { |
206 | v = 42; |
207 | assert(false); |
208 | } catch (...) { /* ... */ |
209 | } |
210 | assert(v.index() == 0); |
211 | assert(std::get<0>(v) == "hello" ); |
212 | } |
213 | { |
214 | using V = std::variant<ThrowsAssignT, std::string>; |
215 | V v(std::in_place_type<std::string>, "hello" ); |
216 | v = 42; |
217 | assert(v.index() == 0); |
218 | assert(std::get<0>(v).value == 42); |
219 | } |
220 | #endif // TEST_HAS_NO_EXCEPTIONS |
221 | } |
222 | |
223 | void test_T_assignment_performs_assignment() { |
224 | using namespace RuntimeHelpers; |
225 | #ifndef TEST_HAS_NO_EXCEPTIONS |
226 | { |
227 | using V = std::variant<ThrowsCtorT>; |
228 | V v; |
229 | v = 42; |
230 | assert(v.index() == 0); |
231 | assert(std::get<0>(v).value == 42); |
232 | } |
233 | { |
234 | using V = std::variant<ThrowsCtorT, std::string>; |
235 | V v; |
236 | v = 42; |
237 | assert(v.index() == 0); |
238 | assert(std::get<0>(v).value == 42); |
239 | } |
240 | { |
241 | using V = std::variant<ThrowsAssignT>; |
242 | V v(100); |
243 | try { |
244 | v = 42; |
245 | assert(false); |
246 | } catch (...) { /* ... */ |
247 | } |
248 | assert(v.index() == 0); |
249 | assert(std::get<0>(v).value == 100); |
250 | } |
251 | { |
252 | using V = std::variant<std::string, ThrowsAssignT>; |
253 | V v(100); |
254 | try { |
255 | v = 42; |
256 | assert(false); |
257 | } catch (...) { /* ... */ |
258 | } |
259 | assert(v.index() == 1); |
260 | assert(std::get<1>(v).value == 100); |
261 | } |
262 | #endif // TEST_HAS_NO_EXCEPTIONS |
263 | } |
264 | |
265 | void test_T_assignment_vector_bool() { |
266 | std::vector<bool> vec = {true}; |
267 | std::variant<bool, int> v; |
268 | v = vec[0]; |
269 | assert(v.index() == 0); |
270 | assert(std::get<0>(v) == true); |
271 | } |
272 | |
273 | int main(int, char**) { |
274 | test_T_assignment_basic(); |
275 | test_T_assignment_performs_construction(); |
276 | test_T_assignment_performs_assignment(); |
277 | test_T_assignment_noexcept(); |
278 | test_T_assignment_sfinae(); |
279 | test_T_assignment_vector_bool(); |
280 | |
281 | return 0; |
282 | } |
283 | |