xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp (revision 4513f0f0224fd34bbebfdc7aa6bb78a995a6ac7b)
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++98, c++03, c++11, c++14
11 
12 // XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions
13 
14 // <variant>
15 
16 // template <class ...Types> class variant;
17 
18 // template <class T>
19 // variant& operator=(T&&) noexcept(see below);
20 
21 #include <cassert>
22 #include <string>
23 #include <type_traits>
24 #include <variant>
25 #include <memory>
26 
27 #include "test_macros.h"
28 #include "variant_test_helpers.hpp"
29 
30 namespace MetaHelpers {
31 
32 struct Dummy {
33   Dummy() = default;
34 };
35 
36 struct ThrowsCtorT {
37   ThrowsCtorT(int) noexcept(false) {}
38   ThrowsCtorT &operator=(int) noexcept { return *this; }
39 };
40 
41 struct ThrowsAssignT {
42   ThrowsAssignT(int) noexcept {}
43   ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
44 };
45 
46 struct NoThrowT {
47   NoThrowT(int) noexcept {}
48   NoThrowT &operator=(int) noexcept { return *this; }
49 };
50 
51 } // namespace MetaHelpers
52 
53 namespace RuntimeHelpers {
54 #ifndef TEST_HAS_NO_EXCEPTIONS
55 
56 struct ThrowsCtorT {
57   int value;
58   ThrowsCtorT() : value(0) {}
59   ThrowsCtorT(int) noexcept(false) { throw 42; }
60   ThrowsCtorT &operator=(int v) noexcept {
61     value = v;
62     return *this;
63   }
64 };
65 
66 struct MoveCrashes {
67   int value;
68   MoveCrashes(int v = 0) noexcept : value{v} {}
69   MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
70   MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
71   MoveCrashes &operator=(int v) noexcept {
72     value = v;
73     return *this;
74   }
75 };
76 
77 struct ThrowsCtorTandMove {
78   int value;
79   ThrowsCtorTandMove() : value(0) {}
80   ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
81   ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
82   ThrowsCtorTandMove &operator=(int v) noexcept {
83     value = v;
84     return *this;
85   }
86 };
87 
88 struct ThrowsAssignT {
89   int value;
90   ThrowsAssignT() : value(0) {}
91   ThrowsAssignT(int v) noexcept : value(v) {}
92   ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
93 };
94 
95 struct NoThrowT {
96   int value;
97   NoThrowT() : value(0) {}
98   NoThrowT(int v) noexcept : value(v) {}
99   NoThrowT &operator=(int v) noexcept {
100     value = v;
101     return *this;
102   }
103 };
104 
105 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
106 } // namespace RuntimeHelpers
107 
108 void test_T_assignment_noexcept() {
109   using namespace MetaHelpers;
110   {
111     using V = std::variant<Dummy, NoThrowT>;
112     static_assert(std::is_nothrow_assignable<V, int>::value, "");
113   }
114   {
115     using V = std::variant<Dummy, ThrowsCtorT>;
116     static_assert(!std::is_nothrow_assignable<V, int>::value, "");
117   }
118   {
119     using V = std::variant<Dummy, ThrowsAssignT>;
120     static_assert(!std::is_nothrow_assignable<V, int>::value, "");
121   }
122 }
123 
124 void test_T_assignment_sfinae() {
125   {
126     using V = std::variant<long, long long>;
127     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
128   }
129   {
130     using V = std::variant<std::string, std::string>;
131     static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
132   }
133   {
134     using V = std::variant<std::string, void *>;
135     static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
136   }
137   {
138     using V = std::variant<std::string, float>;
139     static_assert(!std::is_assignable<V, int>::value, "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   {
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   {
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   {
212     std::variant<bool volatile, int> v = 42;
213     v = false;
214     assert(v.index() == 0);
215     assert(!std::get<0>(v));
216     bool lvt = true;
217     v = lvt;
218     assert(v.index() == 0);
219     assert(std::get<0>(v));
220   }
221 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
222   {
223     using V = std::variant<int &, int &&, long>;
224     int x = 42;
225     V v(43l);
226     v = x;
227     assert(v.index() == 0);
228     assert(&std::get<0>(v) == &x);
229     v = std::move(x);
230     assert(v.index() == 1);
231     assert(&std::get<1>(v) == &x);
232     // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
233     // to 'int&'.
234     const int &cx = x;
235     v = cx;
236     assert(v.index() == 2);
237     assert(std::get<2>(v) == 42);
238   }
239 #endif // TEST_VARIANT_HAS_NO_REFERENCES
240 }
241 
242 void test_T_assignment_performs_construction() {
243   using namespace RuntimeHelpers;
244 #ifndef TEST_HAS_NO_EXCEPTIONS
245   {
246     using V = std::variant<std::string, ThrowsCtorT>;
247     V v(std::in_place_type<std::string>, "hello");
248     try {
249       v = 42;
250       assert(false);
251     } catch (...) { /* ... */
252     }
253     assert(v.index() == 0);
254     assert(std::get<0>(v) == "hello");
255   }
256   {
257     using V = std::variant<ThrowsAssignT, std::string>;
258     V v(std::in_place_type<std::string>, "hello");
259     v = 42;
260     assert(v.index() == 0);
261     assert(std::get<0>(v).value == 42);
262   }
263 #endif // TEST_HAS_NO_EXCEPTIONS
264 }
265 
266 void test_T_assignment_performs_assignment() {
267   using namespace RuntimeHelpers;
268 #ifndef TEST_HAS_NO_EXCEPTIONS
269   {
270     using V = std::variant<ThrowsCtorT>;
271     V v;
272     v = 42;
273     assert(v.index() == 0);
274     assert(std::get<0>(v).value == 42);
275   }
276   {
277     using V = std::variant<ThrowsCtorT, std::string>;
278     V v;
279     v = 42;
280     assert(v.index() == 0);
281     assert(std::get<0>(v).value == 42);
282   }
283   {
284     using V = std::variant<ThrowsAssignT>;
285     V v(100);
286     try {
287       v = 42;
288       assert(false);
289     } catch (...) { /* ... */
290     }
291     assert(v.index() == 0);
292     assert(std::get<0>(v).value == 100);
293   }
294   {
295     using V = std::variant<std::string, ThrowsAssignT>;
296     V v(100);
297     try {
298       v = 42;
299       assert(false);
300     } catch (...) { /* ... */
301     }
302     assert(v.index() == 1);
303     assert(std::get<1>(v).value == 100);
304   }
305 #endif // TEST_HAS_NO_EXCEPTIONS
306 }
307 
308 int main(int, char**) {
309   test_T_assignment_basic();
310   test_T_assignment_performs_construction();
311   test_T_assignment_performs_assignment();
312   test_T_assignment_noexcept();
313   test_T_assignment_sfinae();
314 
315   return 0;
316 }
317