xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp (revision c360553c15a8e5aa94d2236eb73e7dfeab9543e5)
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++03, c++11, c++14
11 
12 // Throwing bad_variant_access is supported starting in macosx10.13
13 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions
14 
15 // <variant>
16 
17 // template <class ...Types> class variant;
18 
19 // template <class T>
20 // variant& operator=(T&&) noexcept(see below);
21 
22 #include <cassert>
23 #include <string>
24 #include <type_traits>
25 #include <variant>
26 #include <memory>
27 
28 #include "test_macros.h"
29 #include "variant_test_helpers.h"
30 
31 namespace MetaHelpers {
32 
33 struct Dummy {
34   Dummy() = default;
35 };
36 
37 struct ThrowsCtorT {
38   ThrowsCtorT(int) noexcept(false) {}
39   ThrowsCtorT &operator=(int) noexcept { return *this; }
40 };
41 
42 struct ThrowsAssignT {
43   ThrowsAssignT(int) noexcept {}
44   ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
45 };
46 
47 struct NoThrowT {
48   NoThrowT(int) noexcept {}
49   NoThrowT &operator=(int) noexcept { return *this; }
50 };
51 
52 } // namespace MetaHelpers
53 
54 namespace RuntimeHelpers {
55 #ifndef TEST_HAS_NO_EXCEPTIONS
56 
57 struct ThrowsCtorT {
58   int value;
59   ThrowsCtorT() : value(0) {}
60   ThrowsCtorT(int) noexcept(false) { throw 42; }
61   ThrowsCtorT &operator=(int v) noexcept {
62     value = v;
63     return *this;
64   }
65 };
66 
67 struct MoveCrashes {
68   int value;
69   MoveCrashes(int v = 0) noexcept : value{v} {}
70   MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
71   MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
72   MoveCrashes &operator=(int v) noexcept {
73     value = v;
74     return *this;
75   }
76 };
77 
78 struct ThrowsCtorTandMove {
79   int value;
80   ThrowsCtorTandMove() : value(0) {}
81   ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
82   ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
83   ThrowsCtorTandMove &operator=(int v) noexcept {
84     value = v;
85     return *this;
86   }
87 };
88 
89 struct ThrowsAssignT {
90   int value;
91   ThrowsAssignT() : value(0) {}
92   ThrowsAssignT(int v) noexcept : value(v) {}
93   ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
94 };
95 
96 struct NoThrowT {
97   int value;
98   NoThrowT() : value(0) {}
99   NoThrowT(int v) noexcept : value(v) {}
100   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 
109 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 
125 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 == VariantAllowsNarrowingConversions,
141     "no matching operator=");
142   }
143   {
144     using V = std::variant<std::unique_ptr<int>, bool>;
145     static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
146                   "no explicit bool in operator=");
147     struct X {
148       operator void*();
149     };
150     static_assert(!std::is_assignable<V, X>::value,
151                   "no boolean conversion in operator=");
152     static_assert(!std::is_assignable<V, std::false_type>::value,
153                   "no converted to bool in operator=");
154   }
155   {
156     struct X {};
157     struct Y {
158       operator X();
159     };
160     using V = std::variant<X>;
161     static_assert(std::is_assignable<V, Y>::value,
162                   "regression on user-defined conversions in operator=");
163   }
164 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
165   {
166     using V = std::variant<int, int &&>;
167     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
168   }
169   {
170     using V = std::variant<int, const int &>;
171     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
172   }
173 #endif // TEST_VARIANT_HAS_NO_REFERENCES
174 }
175 
176 void test_T_assignment_basic() {
177   {
178     std::variant<int> v(43);
179     v = 42;
180     assert(v.index() == 0);
181     assert(std::get<0>(v) == 42);
182   }
183   {
184     std::variant<int, long> v(43l);
185     v = 42;
186     assert(v.index() == 0);
187     assert(std::get<0>(v) == 42);
188     v = 43l;
189     assert(v.index() == 1);
190     assert(std::get<1>(v) == 43);
191   }
192 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS
193   {
194     std::variant<unsigned, long> v;
195     v = 42;
196     assert(v.index() == 1);
197     assert(std::get<1>(v) == 42);
198     v = 43u;
199     assert(v.index() == 0);
200     assert(std::get<0>(v) == 43);
201   }
202 #endif
203   {
204     std::variant<std::string, bool> v = true;
205     v = "bar";
206     assert(v.index() == 0);
207     assert(std::get<0>(v) == "bar");
208   }
209   {
210     std::variant<bool, std::unique_ptr<int>> v;
211     v = nullptr;
212     assert(v.index() == 1);
213     assert(std::get<1>(v) == nullptr);
214   }
215   {
216     std::variant<bool volatile, int> v = 42;
217     v = false;
218     assert(v.index() == 0);
219     assert(!std::get<0>(v));
220     bool lvt = true;
221     v = lvt;
222     assert(v.index() == 0);
223     assert(std::get<0>(v));
224   }
225 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
226   {
227     using V = std::variant<int &, int &&, long>;
228     int x = 42;
229     V v(43l);
230     v = x;
231     assert(v.index() == 0);
232     assert(&std::get<0>(v) == &x);
233     v = std::move(x);
234     assert(v.index() == 1);
235     assert(&std::get<1>(v) == &x);
236     // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
237     // to 'int&'.
238     const int &cx = x;
239     v = cx;
240     assert(v.index() == 2);
241     assert(std::get<2>(v) == 42);
242   }
243 #endif // TEST_VARIANT_HAS_NO_REFERENCES
244 }
245 
246 void test_T_assignment_performs_construction() {
247   using namespace RuntimeHelpers;
248 #ifndef TEST_HAS_NO_EXCEPTIONS
249   {
250     using V = std::variant<std::string, ThrowsCtorT>;
251     V v(std::in_place_type<std::string>, "hello");
252     try {
253       v = 42;
254       assert(false);
255     } catch (...) { /* ... */
256     }
257     assert(v.index() == 0);
258     assert(std::get<0>(v) == "hello");
259   }
260   {
261     using V = std::variant<ThrowsAssignT, std::string>;
262     V v(std::in_place_type<std::string>, "hello");
263     v = 42;
264     assert(v.index() == 0);
265     assert(std::get<0>(v).value == 42);
266   }
267 #endif // TEST_HAS_NO_EXCEPTIONS
268 }
269 
270 void test_T_assignment_performs_assignment() {
271   using namespace RuntimeHelpers;
272 #ifndef TEST_HAS_NO_EXCEPTIONS
273   {
274     using V = std::variant<ThrowsCtorT>;
275     V v;
276     v = 42;
277     assert(v.index() == 0);
278     assert(std::get<0>(v).value == 42);
279   }
280   {
281     using V = std::variant<ThrowsCtorT, std::string>;
282     V v;
283     v = 42;
284     assert(v.index() == 0);
285     assert(std::get<0>(v).value == 42);
286   }
287   {
288     using V = std::variant<ThrowsAssignT>;
289     V v(100);
290     try {
291       v = 42;
292       assert(false);
293     } catch (...) { /* ... */
294     }
295     assert(v.index() == 0);
296     assert(std::get<0>(v).value == 100);
297   }
298   {
299     using V = std::variant<std::string, ThrowsAssignT>;
300     V v(100);
301     try {
302       v = 42;
303       assert(false);
304     } catch (...) { /* ... */
305     }
306     assert(v.index() == 1);
307     assert(std::get<1>(v).value == 100);
308   }
309 #endif // TEST_HAS_NO_EXCEPTIONS
310 }
311 
312 int main(int, char**) {
313   test_T_assignment_basic();
314   test_T_assignment_performs_construction();
315   test_T_assignment_performs_assignment();
316   test_T_assignment_noexcept();
317   test_T_assignment_sfinae();
318 
319   return 0;
320 }
321