xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp (revision ec350ad418a24f70c88758259c774a1e11c06d74)
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 <memory>
23 
24 #include "test_macros.h"
25 #include "variant_test_helpers.h"
26 
27 namespace MetaHelpers {
28 
29 struct Dummy {
30   Dummy() = default;
31 };
32 
33 struct ThrowsCtorT {
34   ThrowsCtorT(int) noexcept(false) {}
35   ThrowsCtorT &operator=(int) noexcept { return *this; }
36 };
37 
38 struct ThrowsAssignT {
39   ThrowsAssignT(int) noexcept {}
40   ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
41 };
42 
43 struct NoThrowT {
44   NoThrowT(int) noexcept {}
45   NoThrowT &operator=(int) noexcept { return *this; }
46 };
47 
48 } // namespace MetaHelpers
49 
50 namespace RuntimeHelpers {
51 #ifndef TEST_HAS_NO_EXCEPTIONS
52 
53 struct ThrowsCtorT {
54   int value;
55   ThrowsCtorT() : value(0) {}
56   ThrowsCtorT(int) noexcept(false) { throw 42; }
57   ThrowsCtorT &operator=(int v) noexcept {
58     value = v;
59     return *this;
60   }
61 };
62 
63 struct MoveCrashes {
64   int value;
65   MoveCrashes(int v = 0) noexcept : value{v} {}
66   MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
67   MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
68   MoveCrashes &operator=(int v) noexcept {
69     value = v;
70     return *this;
71   }
72 };
73 
74 struct ThrowsCtorTandMove {
75   int value;
76   ThrowsCtorTandMove() : value(0) {}
77   ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
78   ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
79   ThrowsCtorTandMove &operator=(int v) noexcept {
80     value = v;
81     return *this;
82   }
83 };
84 
85 struct ThrowsAssignT {
86   int value;
87   ThrowsAssignT() : value(0) {}
88   ThrowsAssignT(int v) noexcept : value(v) {}
89   ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
90 };
91 
92 struct NoThrowT {
93   int value;
94   NoThrowT() : value(0) {}
95   NoThrowT(int v) noexcept : value(v) {}
96   NoThrowT &operator=(int v) noexcept {
97     value = v;
98     return *this;
99   }
100 };
101 
102 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
103 } // namespace RuntimeHelpers
104 
105 void test_T_assignment_noexcept() {
106   using namespace MetaHelpers;
107   {
108     using V = std::variant<Dummy, NoThrowT>;
109     static_assert(std::is_nothrow_assignable<V, int>::value, "");
110   }
111   {
112     using V = std::variant<Dummy, ThrowsCtorT>;
113     static_assert(!std::is_nothrow_assignable<V, int>::value, "");
114   }
115   {
116     using V = std::variant<Dummy, ThrowsAssignT>;
117     static_assert(!std::is_nothrow_assignable<V, int>::value, "");
118   }
119 }
120 
121 void test_T_assignment_sfinae() {
122   {
123     using V = std::variant<long, long long>;
124     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
125   }
126   {
127     using V = std::variant<std::string, std::string>;
128     static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
129   }
130   {
131     using V = std::variant<std::string, void *>;
132     static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
133   }
134   {
135     using V = std::variant<std::string, float>;
136     static_assert(std::is_assignable<V, int>::value == VariantAllowsNarrowingConversions,
137     "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,
147                   "no boolean conversion in operator=");
148     static_assert(!std::is_assignable<V, std::false_type>::value,
149                   "no 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,
158                   "regression on user-defined conversions in operator=");
159   }
160 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
161   {
162     using V = std::variant<int, int &&>;
163     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
164   }
165   {
166     using V = std::variant<int, const int &>;
167     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
168   }
169 #endif // TEST_VARIANT_HAS_NO_REFERENCES
170 }
171 
172 void test_T_assignment_basic() {
173   {
174     std::variant<int> v(43);
175     v = 42;
176     assert(v.index() == 0);
177     assert(std::get<0>(v) == 42);
178   }
179   {
180     std::variant<int, long> v(43l);
181     v = 42;
182     assert(v.index() == 0);
183     assert(std::get<0>(v) == 42);
184     v = 43l;
185     assert(v.index() == 1);
186     assert(std::get<1>(v) == 43);
187   }
188 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS
189   {
190     std::variant<unsigned, long> v;
191     v = 42;
192     assert(v.index() == 1);
193     assert(std::get<1>(v) == 42);
194     v = 43u;
195     assert(v.index() == 0);
196     assert(std::get<0>(v) == 43);
197   }
198 #endif
199   {
200     std::variant<std::string, bool> v = true;
201     v = "bar";
202     assert(v.index() == 0);
203     assert(std::get<0>(v) == "bar");
204   }
205   {
206     std::variant<bool, std::unique_ptr<int>> v;
207     v = nullptr;
208     assert(v.index() == 1);
209     assert(std::get<1>(v) == nullptr);
210   }
211 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
212   {
213     using V = std::variant<int &, int &&, long>;
214     int x = 42;
215     V v(43l);
216     v = x;
217     assert(v.index() == 0);
218     assert(&std::get<0>(v) == &x);
219     v = std::move(x);
220     assert(v.index() == 1);
221     assert(&std::get<1>(v) == &x);
222     // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
223     // to 'int&'.
224     const int &cx = x;
225     v = cx;
226     assert(v.index() == 2);
227     assert(std::get<2>(v) == 42);
228   }
229 #endif // TEST_VARIANT_HAS_NO_REFERENCES
230 }
231 
232 void test_T_assignment_performs_construction() {
233   using namespace RuntimeHelpers;
234 #ifndef TEST_HAS_NO_EXCEPTIONS
235   {
236     using V = std::variant<std::string, ThrowsCtorT>;
237     V v(std::in_place_type<std::string>, "hello");
238     try {
239       v = 42;
240       assert(false);
241     } catch (...) { /* ... */
242     }
243     assert(v.index() == 0);
244     assert(std::get<0>(v) == "hello");
245   }
246   {
247     using V = std::variant<ThrowsAssignT, std::string>;
248     V v(std::in_place_type<std::string>, "hello");
249     v = 42;
250     assert(v.index() == 0);
251     assert(std::get<0>(v).value == 42);
252   }
253 #endif // TEST_HAS_NO_EXCEPTIONS
254 }
255 
256 void test_T_assignment_performs_assignment() {
257   using namespace RuntimeHelpers;
258 #ifndef TEST_HAS_NO_EXCEPTIONS
259   {
260     using V = std::variant<ThrowsCtorT>;
261     V v;
262     v = 42;
263     assert(v.index() == 0);
264     assert(std::get<0>(v).value == 42);
265   }
266   {
267     using V = std::variant<ThrowsCtorT, std::string>;
268     V v;
269     v = 42;
270     assert(v.index() == 0);
271     assert(std::get<0>(v).value == 42);
272   }
273   {
274     using V = std::variant<ThrowsAssignT>;
275     V v(100);
276     try {
277       v = 42;
278       assert(false);
279     } catch (...) { /* ... */
280     }
281     assert(v.index() == 0);
282     assert(std::get<0>(v).value == 100);
283   }
284   {
285     using V = std::variant<std::string, ThrowsAssignT>;
286     V v(100);
287     try {
288       v = 42;
289       assert(false);
290     } catch (...) { /* ... */
291     }
292     assert(v.index() == 1);
293     assert(std::get<1>(v).value == 100);
294   }
295 #endif // TEST_HAS_NO_EXCEPTIONS
296 }
297 
298 int main(int, char**) {
299   test_T_assignment_basic();
300   test_T_assignment_performs_construction();
301   test_T_assignment_performs_assignment();
302   test_T_assignment_noexcept();
303   test_T_assignment_sfinae();
304 
305   return 0;
306 }
307