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