xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp (revision 52271a5c11f6abde1fa1221db304212b5eb8ec7c)
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