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