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