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