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 // Throwing bad_variant_access is supported starting in macosx10.13
12 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !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.h"
29 
30 namespace MetaHelpers {
31 
32 struct Dummy {
33   Dummy() = default;
34 };
35 
36 struct ThrowsCtorT {
ThrowsCtorTMetaHelpers::ThrowsCtorT37   ThrowsCtorT(int) noexcept(false) {}
operator =MetaHelpers::ThrowsCtorT38   ThrowsCtorT &operator=(int) noexcept { return *this; }
39 };
40 
41 struct ThrowsAssignT {
ThrowsAssignTMetaHelpers::ThrowsAssignT42   ThrowsAssignT(int) noexcept {}
operator =MetaHelpers::ThrowsAssignT43   ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
44 };
45 
46 struct NoThrowT {
NoThrowTMetaHelpers::NoThrowT47   NoThrowT(int) noexcept {}
operator =MetaHelpers::NoThrowT48   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;
ThrowsCtorTRuntimeHelpers::ThrowsCtorT58   ThrowsCtorT() : value(0) {}
ThrowsCtorTRuntimeHelpers::ThrowsCtorT59   ThrowsCtorT(int) noexcept(false) { throw 42; }
operator =RuntimeHelpers::ThrowsCtorT60   ThrowsCtorT &operator=(int v) noexcept {
61     value = v;
62     return *this;
63   }
64 };
65 
66 struct MoveCrashes {
67   int value;
MoveCrashesRuntimeHelpers::MoveCrashes68   MoveCrashes(int v = 0) noexcept : value{v} {}
MoveCrashesRuntimeHelpers::MoveCrashes69   MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
operator =RuntimeHelpers::MoveCrashes70   MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
operator =RuntimeHelpers::MoveCrashes71   MoveCrashes &operator=(int v) noexcept {
72     value = v;
73     return *this;
74   }
75 };
76 
77 struct ThrowsCtorTandMove {
78   int value;
ThrowsCtorTandMoveRuntimeHelpers::ThrowsCtorTandMove79   ThrowsCtorTandMove() : value(0) {}
ThrowsCtorTandMoveRuntimeHelpers::ThrowsCtorTandMove80   ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
ThrowsCtorTandMoveRuntimeHelpers::ThrowsCtorTandMove81   ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
operator =RuntimeHelpers::ThrowsCtorTandMove82   ThrowsCtorTandMove &operator=(int v) noexcept {
83     value = v;
84     return *this;
85   }
86 };
87 
88 struct ThrowsAssignT {
89   int value;
ThrowsAssignTRuntimeHelpers::ThrowsAssignT90   ThrowsAssignT() : value(0) {}
ThrowsAssignTRuntimeHelpers::ThrowsAssignT91   ThrowsAssignT(int v) noexcept : value(v) {}
operator =RuntimeHelpers::ThrowsAssignT92   ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
93 };
94 
95 struct NoThrowT {
96   int value;
NoThrowTRuntimeHelpers::NoThrowT97   NoThrowT() : value(0) {}
NoThrowTRuntimeHelpers::NoThrowT98   NoThrowT(int v) noexcept : value(v) {}
operator =RuntimeHelpers::NoThrowT99   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 
test_T_assignment_noexcept()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 
test_T_assignment_sfinae()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 == VariantAllowsNarrowingConversions,
140     "no matching operator=");
141   }
142   {
143     using V = std::variant<std::unique_ptr<int>, bool>;
144     static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
145                   "no explicit bool in operator=");
146     struct X {
147       operator void*();
148     };
149     static_assert(!std::is_assignable<V, X>::value,
150                   "no boolean conversion in operator=");
151     static_assert(!std::is_assignable<V, std::false_type>::value,
152                   "no converted to bool in operator=");
153   }
154   {
155     struct X {};
156     struct Y {
157       operator X();
158     };
159     using V = std::variant<X>;
160     static_assert(std::is_assignable<V, Y>::value,
161                   "regression on user-defined conversions in operator=");
162   }
163 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
164   {
165     using V = std::variant<int, int &&>;
166     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
167   }
168   {
169     using V = std::variant<int, const int &>;
170     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
171   }
172 #endif // TEST_VARIANT_HAS_NO_REFERENCES
173 }
174 
test_T_assignment_basic()175 void test_T_assignment_basic() {
176   {
177     std::variant<int> v(43);
178     v = 42;
179     assert(v.index() == 0);
180     assert(std::get<0>(v) == 42);
181   }
182   {
183     std::variant<int, long> v(43l);
184     v = 42;
185     assert(v.index() == 0);
186     assert(std::get<0>(v) == 42);
187     v = 43l;
188     assert(v.index() == 1);
189     assert(std::get<1>(v) == 43);
190   }
191 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS
192   {
193     std::variant<unsigned, long> v;
194     v = 42;
195     assert(v.index() == 1);
196     assert(std::get<1>(v) == 42);
197     v = 43u;
198     assert(v.index() == 0);
199     assert(std::get<0>(v) == 43);
200   }
201 #endif
202   {
203     std::variant<std::string, bool> v = true;
204     v = "bar";
205     assert(v.index() == 0);
206     assert(std::get<0>(v) == "bar");
207   }
208   {
209     std::variant<bool, std::unique_ptr<int>> v;
210     v = nullptr;
211     assert(v.index() == 1);
212     assert(std::get<1>(v) == nullptr);
213   }
214 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
215   {
216     using V = std::variant<int &, int &&, long>;
217     int x = 42;
218     V v(43l);
219     v = x;
220     assert(v.index() == 0);
221     assert(&std::get<0>(v) == &x);
222     v = std::move(x);
223     assert(v.index() == 1);
224     assert(&std::get<1>(v) == &x);
225     // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
226     // to 'int&'.
227     const int &cx = x;
228     v = cx;
229     assert(v.index() == 2);
230     assert(std::get<2>(v) == 42);
231   }
232 #endif // TEST_VARIANT_HAS_NO_REFERENCES
233 }
234 
test_T_assignment_performs_construction()235 void test_T_assignment_performs_construction() {
236   using namespace RuntimeHelpers;
237 #ifndef TEST_HAS_NO_EXCEPTIONS
238   {
239     using V = std::variant<std::string, ThrowsCtorT>;
240     V v(std::in_place_type<std::string>, "hello");
241     try {
242       v = 42;
243       assert(false);
244     } catch (...) { /* ... */
245     }
246     assert(v.index() == 0);
247     assert(std::get<0>(v) == "hello");
248   }
249   {
250     using V = std::variant<ThrowsAssignT, std::string>;
251     V v(std::in_place_type<std::string>, "hello");
252     v = 42;
253     assert(v.index() == 0);
254     assert(std::get<0>(v).value == 42);
255   }
256 #endif // TEST_HAS_NO_EXCEPTIONS
257 }
258 
test_T_assignment_performs_assignment()259 void test_T_assignment_performs_assignment() {
260   using namespace RuntimeHelpers;
261 #ifndef TEST_HAS_NO_EXCEPTIONS
262   {
263     using V = std::variant<ThrowsCtorT>;
264     V v;
265     v = 42;
266     assert(v.index() == 0);
267     assert(std::get<0>(v).value == 42);
268   }
269   {
270     using V = std::variant<ThrowsCtorT, std::string>;
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<ThrowsAssignT>;
278     V v(100);
279     try {
280       v = 42;
281       assert(false);
282     } catch (...) { /* ... */
283     }
284     assert(v.index() == 0);
285     assert(std::get<0>(v).value == 100);
286   }
287   {
288     using V = std::variant<std::string, ThrowsAssignT>;
289     V v(100);
290     try {
291       v = 42;
292       assert(false);
293     } catch (...) { /* ... */
294     }
295     assert(v.index() == 1);
296     assert(std::get<1>(v).value == 100);
297   }
298 #endif // TEST_HAS_NO_EXCEPTIONS
299 }
300 
main(int,char **)301 int main(int, char**) {
302   test_T_assignment_basic();
303   test_T_assignment_performs_construction();
304   test_T_assignment_performs_assignment();
305   test_T_assignment_noexcept();
306   test_T_assignment_sfinae();
307 
308   return 0;
309 }
310