xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
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