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 {
ThrowsCtorTMetaHelpers::ThrowsCtorT35 ThrowsCtorT(int) noexcept(false) {}
operator =MetaHelpers::ThrowsCtorT36 ThrowsCtorT& operator=(int) noexcept { return *this; }
37 };
38
39 struct ThrowsAssignT {
ThrowsAssignTMetaHelpers::ThrowsAssignT40 ThrowsAssignT(int) noexcept {}
operator =MetaHelpers::ThrowsAssignT41 ThrowsAssignT& operator=(int) noexcept(false) { return *this; }
42 };
43
44 struct NoThrowT {
NoThrowTMetaHelpers::NoThrowT45 NoThrowT(int) noexcept {}
operator =MetaHelpers::NoThrowT46 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;
ThrowsCtorTRuntimeHelpers::ThrowsCtorT56 ThrowsCtorT() : value(0) {}
ThrowsCtorTRuntimeHelpers::ThrowsCtorT57 ThrowsCtorT(int) noexcept(false) { throw 42; }
operator =RuntimeHelpers::ThrowsCtorT58 ThrowsCtorT& operator=(int v) noexcept {
59 value = v;
60 return *this;
61 }
62 };
63
64 struct MoveCrashes {
65 int value;
MoveCrashesRuntimeHelpers::MoveCrashes66 MoveCrashes(int v = 0) noexcept : value{v} {}
MoveCrashesRuntimeHelpers::MoveCrashes67 MoveCrashes(MoveCrashes&&) noexcept { assert(false); }
operator =RuntimeHelpers::MoveCrashes68 MoveCrashes& operator=(MoveCrashes&&) noexcept {
69 assert(false);
70 return *this;
71 }
operator =RuntimeHelpers::MoveCrashes72 MoveCrashes& operator=(int v) noexcept {
73 value = v;
74 return *this;
75 }
76 };
77
78 struct ThrowsCtorTandMove {
79 int value;
ThrowsCtorTandMoveRuntimeHelpers::ThrowsCtorTandMove80 ThrowsCtorTandMove() : value(0) {}
ThrowsCtorTandMoveRuntimeHelpers::ThrowsCtorTandMove81 ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
ThrowsCtorTandMoveRuntimeHelpers::ThrowsCtorTandMove82 ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); }
operator =RuntimeHelpers::ThrowsCtorTandMove83 ThrowsCtorTandMove& operator=(int v) noexcept {
84 value = v;
85 return *this;
86 }
87 };
88
89 struct ThrowsAssignT {
90 int value;
ThrowsAssignTRuntimeHelpers::ThrowsAssignT91 ThrowsAssignT() : value(0) {}
ThrowsAssignTRuntimeHelpers::ThrowsAssignT92 ThrowsAssignT(int v) noexcept : value(v) {}
operator =RuntimeHelpers::ThrowsAssignT93 ThrowsAssignT& operator=(int) noexcept(false) { throw 42; }
94 };
95
96 struct NoThrowT {
97 int value;
NoThrowTRuntimeHelpers::NoThrowT98 NoThrowT() : value(0) {}
NoThrowTRuntimeHelpers::NoThrowT99 NoThrowT(int v) noexcept : value(v) {}
operator =RuntimeHelpers::NoThrowT100 NoThrowT& operator=(int v) noexcept {
101 value = v;
102 return *this;
103 }
104 };
105
106 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
107 } // namespace RuntimeHelpers
108
test_T_assignment_noexcept()109 constexpr void test_T_assignment_noexcept() {
110 using namespace MetaHelpers;
111 {
112 using V = std::variant<Dummy, NoThrowT>;
113 static_assert(std::is_nothrow_assignable<V, int>::value, "");
114 }
115 {
116 using V = std::variant<Dummy, ThrowsCtorT>;
117 static_assert(!std::is_nothrow_assignable<V, int>::value, "");
118 }
119 {
120 using V = std::variant<Dummy, ThrowsAssignT>;
121 static_assert(!std::is_nothrow_assignable<V, int>::value, "");
122 }
123 }
124
test_T_assignment_sfinae()125 constexpr void test_T_assignment_sfinae() {
126 {
127 using V = std::variant<long, long long>;
128 static_assert(!std::is_assignable<V, int>::value, "ambiguous");
129 }
130 {
131 using V = std::variant<std::string, std::string>;
132 static_assert(!std::is_assignable<V, const char*>::value, "ambiguous");
133 }
134 {
135 using V = std::variant<std::string, void*>;
136 static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
137 }
138 {
139 using V = std::variant<std::string, float>;
140 static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
141 }
142 {
143 using V = std::variant<std::unique_ptr<int>, bool>;
144 static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, "no explicit bool in operator=");
145 struct X {
146 operator void*();
147 };
148 static_assert(!std::is_assignable<V, X>::value, "no boolean conversion in operator=");
149 static_assert(std::is_assignable<V, std::false_type>::value, "converted to bool in operator=");
150 }
151 {
152 struct X {};
153 struct Y {
154 operator X();
155 };
156 using V = std::variant<X>;
157 static_assert(std::is_assignable<V, Y>::value, "regression on user-defined conversions in operator=");
158 }
159 }
160
test_T_assignment_basic()161 TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() {
162 {
163 std::variant<int> v(43);
164 v = 42;
165 assert(v.index() == 0);
166 assert(std::get<0>(v) == 42);
167 }
168 {
169 std::variant<int, long> v(43l);
170 v = 42;
171 assert(v.index() == 0);
172 assert(std::get<0>(v) == 42);
173 v = 43l;
174 assert(v.index() == 1);
175 assert(std::get<1>(v) == 43);
176 }
177 {
178 std::variant<unsigned, long> v;
179 v = 42;
180 assert(v.index() == 1);
181 assert(std::get<1>(v) == 42);
182 v = 43u;
183 assert(v.index() == 0);
184 assert(std::get<0>(v) == 43);
185 }
186 {
187 std::variant<std::string, bool> v = true;
188 v = "bar";
189 assert(v.index() == 0);
190 assert(std::get<0>(v) == "bar");
191 }
192 }
193
test_T_assignment_basic_no_constexpr()194 void test_T_assignment_basic_no_constexpr() {
195 std::variant<bool, std::unique_ptr<int>> v;
196 v = nullptr;
197 assert(v.index() == 1);
198 assert(std::get<1>(v) == nullptr);
199 }
200
201 struct TraceStat {
202 int construct = 0;
203 int copy_construct = 0;
204 int copy_assign = 0;
205 int move_construct = 0;
206 int move_assign = 0;
207 int T_copy_assign = 0;
208 int T_move_assign = 0;
209 int destroy = 0;
210 };
211
212 template <bool CtorNoexcept, bool MoveCtorNoexcept>
213 struct Trace {
214 struct T {};
215
TraceTrace216 constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; }
TraceTrace217 constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {}
TraceTrace218 constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; }
TraceTrace219 constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; }
operator =Trace220 constexpr Trace& operator=(const Trace&) {
221 ++stat->copy_assign;
222 return *this;
223 }
operator =Trace224 constexpr Trace& operator=(Trace&&) noexcept {
225 ++stat->move_assign;
226 return *this;
227 }
228
operator =Trace229 constexpr Trace& operator=(const T&) {
230 ++stat->T_copy_assign;
231 return *this;
232 }
operator =Trace233 constexpr Trace& operator=(T&&) noexcept {
234 ++stat->T_move_assign;
235 return *this;
236 }
~TraceTrace237 TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; }
238
239 TraceStat* stat;
240 };
241
test_T_assignment_performs_construction()242 TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() {
243 {
244 using V = std::variant<int, Trace<false, false>>;
245 TraceStat stat;
246 V v{1};
247 v = &stat;
248 assert(stat.construct == 1);
249 assert(stat.copy_construct == 0);
250 assert(stat.move_construct == 0);
251 assert(stat.copy_assign == 0);
252 assert(stat.move_assign == 0);
253 assert(stat.destroy == 0);
254 }
255 {
256 using V = std::variant<int, Trace<false, true>>;
257 TraceStat stat;
258 V v{1};
259 v = &stat;
260 assert(stat.construct == 1);
261 assert(stat.copy_construct == 0);
262 assert(stat.move_construct == 1);
263 assert(stat.copy_assign == 0);
264 assert(stat.move_assign == 0);
265 assert(stat.destroy == 1);
266 }
267
268 {
269 using V = std::variant<int, Trace<true, false>>;
270 TraceStat stat;
271 V v{1};
272 v = &stat;
273 assert(stat.construct == 1);
274 assert(stat.copy_construct == 0);
275 assert(stat.move_construct == 0);
276 assert(stat.copy_assign == 0);
277 assert(stat.move_assign == 0);
278 assert(stat.destroy == 0);
279 }
280
281 {
282 using V = std::variant<int, Trace<true, true>>;
283 TraceStat stat;
284 V v{1};
285 v = &stat;
286 assert(stat.construct == 1);
287 assert(stat.copy_construct == 0);
288 assert(stat.move_construct == 0);
289 assert(stat.copy_assign == 0);
290 assert(stat.move_assign == 0);
291 assert(stat.destroy == 0);
292 }
293 }
294
test_T_assignment_performs_assignment()295 TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() {
296 {
297 using V = std::variant<int, Trace<false, false>>;
298 TraceStat stat;
299 V v{&stat};
300 v = Trace<false, false>::T{};
301 assert(stat.construct == 1);
302 assert(stat.copy_construct == 0);
303 assert(stat.move_construct == 0);
304 assert(stat.copy_assign == 0);
305 assert(stat.move_assign == 0);
306 assert(stat.T_copy_assign == 0);
307 assert(stat.T_move_assign == 1);
308 assert(stat.destroy == 0);
309 }
310 {
311 using V = std::variant<int, Trace<false, false>>;
312 TraceStat stat;
313 V v{&stat};
314 Trace<false, false>::T t;
315 v = t;
316 assert(stat.construct == 1);
317 assert(stat.copy_construct == 0);
318 assert(stat.move_construct == 0);
319 assert(stat.copy_assign == 0);
320 assert(stat.move_assign == 0);
321 assert(stat.T_copy_assign == 1);
322 assert(stat.T_move_assign == 0);
323 assert(stat.destroy == 0);
324 }
325 }
326
test_T_assignment_performs_construction_throw()327 void test_T_assignment_performs_construction_throw() {
328 using namespace RuntimeHelpers;
329 #ifndef TEST_HAS_NO_EXCEPTIONS
330 {
331 using V = std::variant<std::string, ThrowsCtorT>;
332 V v(std::in_place_type<std::string>, "hello");
333 try {
334 v = 42;
335 assert(false);
336 } catch (...) { /* ... */
337 }
338 assert(v.index() == 0);
339 assert(std::get<0>(v) == "hello");
340 }
341 {
342 using V = std::variant<ThrowsAssignT, std::string>;
343 V v(std::in_place_type<std::string>, "hello");
344 v = 42;
345 assert(v.index() == 0);
346 assert(std::get<0>(v).value == 42);
347 }
348 #endif // TEST_HAS_NO_EXCEPTIONS
349 }
350
test_T_assignment_performs_assignment_throw()351 void test_T_assignment_performs_assignment_throw() {
352 using namespace RuntimeHelpers;
353 #ifndef TEST_HAS_NO_EXCEPTIONS
354 {
355 using V = std::variant<ThrowsCtorT>;
356 V v;
357 v = 42;
358 assert(v.index() == 0);
359 assert(std::get<0>(v).value == 42);
360 }
361 {
362 using V = std::variant<ThrowsCtorT, std::string>;
363 V v;
364 v = 42;
365 assert(v.index() == 0);
366 assert(std::get<0>(v).value == 42);
367 }
368 {
369 using V = std::variant<ThrowsAssignT>;
370 V v(100);
371 try {
372 v = 42;
373 assert(false);
374 } catch (...) { /* ... */
375 }
376 assert(v.index() == 0);
377 assert(std::get<0>(v).value == 100);
378 }
379 {
380 using V = std::variant<std::string, ThrowsAssignT>;
381 V v(100);
382 try {
383 v = 42;
384 assert(false);
385 } catch (...) { /* ... */
386 }
387 assert(v.index() == 1);
388 assert(std::get<1>(v).value == 100);
389 }
390 #endif // TEST_HAS_NO_EXCEPTIONS
391 }
392
test_T_assignment_vector_bool()393 TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() {
394 std::vector<bool> vec = {true};
395 std::variant<bool, int> v;
396 v = vec[0];
397 assert(v.index() == 0);
398 assert(std::get<0>(v) == true);
399 }
400
non_constexpr_test()401 void non_constexpr_test() {
402 test_T_assignment_basic_no_constexpr();
403 test_T_assignment_performs_construction_throw();
404 test_T_assignment_performs_assignment_throw();
405 }
406
test()407 TEST_CONSTEXPR_CXX20 bool test() {
408 test_T_assignment_basic();
409 test_T_assignment_performs_construction();
410 test_T_assignment_performs_assignment();
411 test_T_assignment_noexcept();
412 test_T_assignment_sfinae();
413 test_T_assignment_vector_bool();
414
415 return true;
416 }
417
main(int,char **)418 int main(int, char**) {
419 test();
420 non_constexpr_test();
421
422 #if TEST_STD_VER >= 20
423 static_assert(test());
424 #endif
425 return 0;
426 }
427