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
38struct NotCopyConstructible {
39 NotCopyConstructible(const NotCopyConstructible&) = delete;
40 NotCopyConstructible& operator=(const NotCopyConstructible&) = default;
41};
42
43struct NotCopyAssignable {
44 NotCopyAssignable(const NotCopyAssignable&) = default;
45 NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;
46};
47
48// Test constraints
49static_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
53static_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
57static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>>);
58
59// !is_constructible_v<T, U>
60struct NoCtorFromInt {
61 NoCtorFromInt(int) = delete;
62 NoCtorFromInt& operator=(int);
63};
64static_assert(!std::is_assignable_v<std::expected<NoCtorFromInt, int>&, int>);
65
66// !is_assignable_v<T&, U>
67struct NoAssignFromInt {
68 explicit NoAssignFromInt(int);
69 NoAssignFromInt& operator=(int) = delete;
70};
71static_assert(!std::is_assignable_v<std::expected<NoAssignFromInt, int>&, int>);
72
73template <bool moveNoexcept, bool convertNoexcept>
74struct 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>
83static_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>
87static_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>
91static_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>
95static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, int>);
96
97constexpr 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
331void 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
345int main(int, char**) {
346 test();
347 static_assert(test());
348 testException();
349 return 0;
350}
351

source code of libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp