xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp (revision 170810fca6ee0f0e9a5b60c83122127d68bcc7ec)
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 == VariantAllowsNarrowingConversions,
138     "no matching operator=");
139   }
140   {
141     using V = std::variant<std::unique_ptr<int>, bool>;
142     static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
143                   "no explicit bool in operator=");
144     struct X {
145       operator void*();
146     };
147     static_assert(!std::is_assignable<V, X>::value,
148                   "no boolean conversion in operator=");
149     static_assert(std::is_assignable<V, std::false_type>::value,
150                   "converted to bool in operator=");
151   }
152   {
153     struct X {};
154     struct Y {
155       operator X();
156     };
157     using V = std::variant<X>;
158     static_assert(std::is_assignable<V, Y>::value,
159                   "regression on user-defined conversions in operator=");
160   }
161 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
162   {
163     using V = std::variant<int, int &&>;
164     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
165   }
166   {
167     using V = std::variant<int, const int &>;
168     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
169   }
170 #endif // TEST_VARIANT_HAS_NO_REFERENCES
171 }
172 
173 void test_T_assignment_basic() {
174   {
175     std::variant<int> v(43);
176     v = 42;
177     assert(v.index() == 0);
178     assert(std::get<0>(v) == 42);
179   }
180   {
181     std::variant<int, long> v(43l);
182     v = 42;
183     assert(v.index() == 0);
184     assert(std::get<0>(v) == 42);
185     v = 43l;
186     assert(v.index() == 1);
187     assert(std::get<1>(v) == 43);
188   }
189 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS
190   {
191     std::variant<unsigned, long> v;
192     v = 42;
193     assert(v.index() == 1);
194     assert(std::get<1>(v) == 42);
195     v = 43u;
196     assert(v.index() == 0);
197     assert(std::get<0>(v) == 43);
198   }
199 #endif
200   {
201     std::variant<std::string, bool> v = true;
202     v = "bar";
203     assert(v.index() == 0);
204     assert(std::get<0>(v) == "bar");
205   }
206   {
207     std::variant<bool, std::unique_ptr<int>> v;
208     v = nullptr;
209     assert(v.index() == 1);
210     assert(std::get<1>(v) == nullptr);
211   }
212 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
213   {
214     using V = std::variant<int &, int &&, long>;
215     int x = 42;
216     V v(43l);
217     v = x;
218     assert(v.index() == 0);
219     assert(&std::get<0>(v) == &x);
220     v = std::move(x);
221     assert(v.index() == 1);
222     assert(&std::get<1>(v) == &x);
223     // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
224     // to 'int&'.
225     const int &cx = x;
226     v = cx;
227     assert(v.index() == 2);
228     assert(std::get<2>(v) == 42);
229   }
230 #endif // TEST_VARIANT_HAS_NO_REFERENCES
231 }
232 
233 void test_T_assignment_performs_construction() {
234   using namespace RuntimeHelpers;
235 #ifndef TEST_HAS_NO_EXCEPTIONS
236   {
237     using V = std::variant<std::string, ThrowsCtorT>;
238     V v(std::in_place_type<std::string>, "hello");
239     try {
240       v = 42;
241       assert(false);
242     } catch (...) { /* ... */
243     }
244     assert(v.index() == 0);
245     assert(std::get<0>(v) == "hello");
246   }
247   {
248     using V = std::variant<ThrowsAssignT, std::string>;
249     V v(std::in_place_type<std::string>, "hello");
250     v = 42;
251     assert(v.index() == 0);
252     assert(std::get<0>(v).value == 42);
253   }
254 #endif // TEST_HAS_NO_EXCEPTIONS
255 }
256 
257 void test_T_assignment_performs_assignment() {
258   using namespace RuntimeHelpers;
259 #ifndef TEST_HAS_NO_EXCEPTIONS
260   {
261     using V = std::variant<ThrowsCtorT>;
262     V v;
263     v = 42;
264     assert(v.index() == 0);
265     assert(std::get<0>(v).value == 42);
266   }
267   {
268     using V = std::variant<ThrowsCtorT, std::string>;
269     V v;
270     v = 42;
271     assert(v.index() == 0);
272     assert(std::get<0>(v).value == 42);
273   }
274   {
275     using V = std::variant<ThrowsAssignT>;
276     V v(100);
277     try {
278       v = 42;
279       assert(false);
280     } catch (...) { /* ... */
281     }
282     assert(v.index() == 0);
283     assert(std::get<0>(v).value == 100);
284   }
285   {
286     using V = std::variant<std::string, ThrowsAssignT>;
287     V v(100);
288     try {
289       v = 42;
290       assert(false);
291     } catch (...) { /* ... */
292     }
293     assert(v.index() == 1);
294     assert(std::get<1>(v).value == 100);
295   }
296 #endif // TEST_HAS_NO_EXCEPTIONS
297 }
298 
299 void test_T_assignment_vector_bool() {
300   std::vector<bool> vec = {true};
301   std::variant<bool, int> v;
302   v = vec[0];
303   assert(v.index() == 0);
304   assert(std::get<0>(v) == true);
305 }
306 
307 int main(int, char**) {
308   test_T_assignment_basic();
309   test_T_assignment_performs_construction();
310   test_T_assignment_performs_assignment();
311   test_T_assignment_noexcept();
312   test_T_assignment_sfinae();
313   test_T_assignment_vector_bool();
314 
315   return 0;
316 }
317