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

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