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, c++17, c++20 |
10 | |
11 | // template<class U = T> |
12 | // constexpr expected& operator=(U&& v); |
13 | // |
14 | // Constraints: |
15 | // - is_same_v<expected, remove_cvref_t<U>> is false; and |
16 | // - remove_cvref_t<U> is not a specialization of unexpected; and |
17 | // - is_constructible_v<T, U> is true; and |
18 | // - is_assignable_v<T&, U> is true; and |
19 | // - is_nothrow_constructible_v<T, U> || is_nothrow_move_constructible_v<T> || |
20 | // is_nothrow_move_constructible_v<E> is true. |
21 | // |
22 | // Effects: |
23 | // - If has_value() is true, equivalent to: val = std::forward<U>(v); |
24 | // - Otherwise, equivalent to: |
25 | // reinit-expected(val, unex, std::forward<U>(v)); |
26 | // has_val = true; |
27 | // - Returns: *this. |
28 | |
29 | #include <cassert> |
30 | #include <concepts> |
31 | #include <expected> |
32 | #include <type_traits> |
33 | #include <utility> |
34 | |
35 | #include "../../types.h" |
36 | #include "test_macros.h" |
37 | |
38 | struct NotCopyConstructible { |
39 | NotCopyConstructible(const NotCopyConstructible&) = delete; |
40 | NotCopyConstructible& operator=(const NotCopyConstructible&) = default; |
41 | }; |
42 | |
43 | struct NotCopyAssignable { |
44 | NotCopyAssignable(const NotCopyAssignable&) = default; |
45 | NotCopyAssignable& operator=(const NotCopyAssignable&) = delete; |
46 | }; |
47 | |
48 | // Test constraints |
49 | static_assert(std::is_assignable_v<std::expected<int, int>&, int>); |
50 | |
51 | // is_same_v<expected, remove_cvref_t<U>> |
52 | // it is true because it covered by the copy assignment |
53 | static_assert(std::is_assignable_v<std::expected<int, int>&, std::expected<int, int>>); |
54 | |
55 | // remove_cvref_t<U> is a specialization of unexpected |
56 | // it is true because it covered the unexpected overload |
57 | static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>>); |
58 | |
59 | // !is_constructible_v<T, U> |
60 | struct NoCtorFromInt { |
61 | NoCtorFromInt(int) = delete; |
62 | NoCtorFromInt& operator=(int); |
63 | }; |
64 | static_assert(!std::is_assignable_v<std::expected<NoCtorFromInt, int>&, int>); |
65 | |
66 | // !is_assignable_v<T&, U> |
67 | struct NoAssignFromInt { |
68 | explicit NoAssignFromInt(int); |
69 | NoAssignFromInt& operator=(int) = delete; |
70 | }; |
71 | static_assert(!std::is_assignable_v<std::expected<NoAssignFromInt, int>&, int>); |
72 | |
73 | template <bool moveNoexcept, bool convertNoexcept> |
74 | struct MaybeNoexcept { |
75 | explicit MaybeNoexcept(int) noexcept(convertNoexcept); |
76 | MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept); |
77 | MaybeNoexcept& operator=(MaybeNoexcept&&) = default; |
78 | MaybeNoexcept& operator=(int); |
79 | }; |
80 | |
81 | // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
82 | // is_nothrow_move_constructible_v<E> |
83 | static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, int>&, int>); |
84 | |
85 | // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
86 | // !is_nothrow_move_constructible_v<E> |
87 | static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, true>, MaybeNoexcept<false, false>>&, int>); |
88 | |
89 | // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> && |
90 | // !is_nothrow_move_constructible_v<E> |
91 | static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, false>, MaybeNoexcept<false, false>>&, int>); |
92 | |
93 | // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
94 | // !is_nothrow_move_constructible_v<E> |
95 | static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, int>); |
96 | |
97 | constexpr bool test() { |
98 | // If has_value() is true, equivalent to: val = std::forward<U>(v); |
99 | // Copy |
100 | { |
101 | Traced::state oldState{}; |
102 | Traced::state newState{}; |
103 | std::expected<Traced, int> e1(std::in_place, oldState, 5); |
104 | Traced u(newState, 10); |
105 | decltype(auto) x = (e1 = u); |
106 | static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>); |
107 | assert(&x == &e1); |
108 | |
109 | assert(e1.has_value()); |
110 | assert(e1.value().data_ == 10); |
111 | assert(oldState.copyAssignCalled); |
112 | } |
113 | |
114 | // If has_value() is true, equivalent to: val = std::forward<U>(v); |
115 | // Move |
116 | { |
117 | Traced::state oldState{}; |
118 | Traced::state newState{}; |
119 | std::expected<Traced, int> e1(std::in_place, oldState, 5); |
120 | Traced u(newState, 10); |
121 | decltype(auto) x = (e1 = std::move(u)); |
122 | static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>); |
123 | assert(&x == &e1); |
124 | |
125 | assert(e1.has_value()); |
126 | assert(e1.value().data_ == 10); |
127 | assert(oldState.moveAssignCalled); |
128 | } |
129 | |
130 | // Otherwise, equivalent to: |
131 | // reinit-expected(val, unex, std::forward<U>(v)); |
132 | // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
133 | // !is_nothrow_move_constructible_v<E> |
134 | // copy |
135 | // |
136 | // In this case, it should call the branch |
137 | // destroy_at(addressof(oldval)); |
138 | // construct_at(addressof(newval), std::forward<Args>(args)...); |
139 | { |
140 | BothMayThrow::state oldState{}; |
141 | std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5); |
142 | const int i = 10; |
143 | decltype(auto) x = (e1 = i); |
144 | static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>); |
145 | assert(&x == &e1); |
146 | |
147 | assert(e1.has_value()); |
148 | assert(e1.value().data_ == 10); |
149 | |
150 | assert(!oldState.copyCtorCalled); |
151 | assert(!oldState.moveCtorCalled); |
152 | assert(oldState.dtorCalled); |
153 | assert(e1.value().copiedFromInt); |
154 | } |
155 | |
156 | // Otherwise, equivalent to: |
157 | // reinit-expected(val, unex, std::forward<U>(v)); |
158 | // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
159 | // !is_nothrow_move_constructible_v<E> |
160 | // move |
161 | // |
162 | // In this case, it should call the branch |
163 | // destroy_at(addressof(oldval)); |
164 | // construct_at(addressof(newval), std::forward<Args>(args)...); |
165 | { |
166 | BothMayThrow::state oldState{}; |
167 | std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5); |
168 | decltype(auto) x = (e1 = 10); |
169 | static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>); |
170 | assert(&x == &e1); |
171 | |
172 | assert(e1.has_value()); |
173 | assert(e1.value().data_ == 10); |
174 | |
175 | assert(!oldState.copyCtorCalled); |
176 | assert(!oldState.moveCtorCalled); |
177 | assert(oldState.dtorCalled); |
178 | assert(e1.value().movedFromInt); |
179 | } |
180 | |
181 | // Otherwise, equivalent to: |
182 | // reinit-expected(val, unex, std::forward<U>(v)); |
183 | // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> && |
184 | // !is_nothrow_move_constructible_v<E> |
185 | // copy |
186 | // |
187 | // In this case, it should call the branch |
188 | // T tmp(std::forward<Args>(args)...); |
189 | // destroy_at(addressof(oldval)); |
190 | // construct_at(addressof(newval), std::move(tmp)); |
191 | { |
192 | BothMayThrow::state oldState{}; |
193 | std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5); |
194 | const int i = 10; |
195 | decltype(auto) x = (e1 = i); |
196 | static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>); |
197 | assert(&x == &e1); |
198 | |
199 | assert(e1.has_value()); |
200 | assert(e1.value().data_ == 10); |
201 | |
202 | assert(!oldState.copyCtorCalled); |
203 | assert(!oldState.moveCtorCalled); |
204 | assert(oldState.dtorCalled); |
205 | assert(!e1.value().copiedFromInt); |
206 | assert(e1.value().movedFromTmp); |
207 | } |
208 | |
209 | // Otherwise, equivalent to: |
210 | // reinit-expected(val, unex, std::forward<U>(v)); |
211 | // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> && |
212 | // !is_nothrow_move_constructible_v<E> |
213 | // move |
214 | // |
215 | // In this case, it should call the branch |
216 | // T tmp(std::forward<Args>(args)...); |
217 | // destroy_at(addressof(oldval)); |
218 | // construct_at(addressof(newval), std::move(tmp)); |
219 | { |
220 | BothMayThrow::state oldState{}; |
221 | std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5); |
222 | decltype(auto) x = (e1 = 10); |
223 | static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>); |
224 | assert(&x == &e1); |
225 | |
226 | assert(e1.has_value()); |
227 | assert(e1.value().data_ == 10); |
228 | |
229 | assert(!oldState.copyCtorCalled); |
230 | assert(!oldState.moveCtorCalled); |
231 | assert(oldState.dtorCalled); |
232 | assert(!e1.value().copiedFromInt); |
233 | assert(e1.value().movedFromTmp); |
234 | } |
235 | |
236 | // Otherwise, equivalent to: |
237 | // reinit-expected(val, unex, std::forward<U>(v)); |
238 | // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
239 | // is_nothrow_move_constructible_v<E> |
240 | // copy |
241 | // |
242 | // In this case, it should call the branch |
243 | // U tmp(std::move(oldval)); |
244 | // destroy_at(addressof(oldval)); |
245 | // try { |
246 | // construct_at(addressof(newval), std::forward<Args>(args)...); |
247 | // } catch (...) { |
248 | // construct_at(addressof(oldval), std::move(tmp)); |
249 | // throw; |
250 | // } |
251 | { |
252 | TracedNoexcept::state oldState{}; |
253 | std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5); |
254 | const int i = 10; |
255 | decltype(auto) x = (e1 = i); |
256 | static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>); |
257 | assert(&x == &e1); |
258 | |
259 | assert(e1.has_value()); |
260 | assert(e1.value().data_ == 10); |
261 | |
262 | assert(!oldState.copyCtorCalled); |
263 | assert(oldState.moveCtorCalled); |
264 | assert(oldState.dtorCalled); |
265 | assert(e1.value().copiedFromInt); |
266 | } |
267 | |
268 | // Otherwise, equivalent to: |
269 | // reinit-expected(val, unex, std::forward<U>(v)); |
270 | // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && |
271 | // is_nothrow_move_constructible_v<E> |
272 | // move |
273 | // |
274 | // In this case, it should call the branch |
275 | // U tmp(std::move(oldval)); |
276 | // destroy_at(addressof(oldval)); |
277 | // try { |
278 | // construct_at(addressof(newval), std::forward<Args>(args)...); |
279 | // } catch (...) { |
280 | // construct_at(addressof(oldval), std::move(tmp)); |
281 | // throw; |
282 | // } |
283 | { |
284 | TracedNoexcept::state oldState{}; |
285 | std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5); |
286 | decltype(auto) x = (e1 = 10); |
287 | static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>); |
288 | assert(&x == &e1); |
289 | |
290 | assert(e1.has_value()); |
291 | assert(e1.value().data_ == 10); |
292 | |
293 | assert(!oldState.copyCtorCalled); |
294 | assert(oldState.moveCtorCalled); |
295 | assert(oldState.dtorCalled); |
296 | assert(e1.value().movedFromInt); |
297 | } |
298 | |
299 | // Test default template argument. |
300 | // Without it, the template parameter cannot be deduced from an initializer list |
301 | { |
302 | struct Bar { |
303 | int i; |
304 | int j; |
305 | constexpr Bar(int ii, int jj) : i(ii), j(jj) {} |
306 | }; |
307 | |
308 | std::expected<Bar, int> e({5, 6}); |
309 | e = {7, 8}; |
310 | assert(e.value().i == 7); |
311 | assert(e.value().j == 8); |
312 | } |
313 | |
314 | // CheckForInvalidWrites |
315 | { |
316 | { |
317 | CheckForInvalidWrites<true> e1(std::unexpect); |
318 | e1 = 42; |
319 | assert(e1.check()); |
320 | } |
321 | { |
322 | CheckForInvalidWrites<false> e1(std::unexpect); |
323 | e1 = true; |
324 | assert(e1.check()); |
325 | } |
326 | } |
327 | |
328 | return true; |
329 | } |
330 | |
331 | void testException() { |
332 | #ifndef TEST_HAS_NO_EXCEPTIONS |
333 | std::expected<ThrowOnConvert, int> e1(std::unexpect, 5); |
334 | try { |
335 | e1 = 10; |
336 | assert(false); |
337 | } catch (Except) { |
338 | assert(!e1.has_value()); |
339 | assert(e1.error() == 5); |
340 | } |
341 | |
342 | #endif // TEST_HAS_NO_EXCEPTIONS |
343 | } |
344 | |
345 | int main(int, char**) { |
346 | test(); |
347 | static_assert(test()); |
348 | testException(); |
349 | return 0; |
350 | } |
351 | |