xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/assign/assign.move.pass.cpp (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
1e356f681SHui Xie //===----------------------------------------------------------------------===//
2*6a54dfbfSLouis Dionne //
3e356f681SHui Xie // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e356f681SHui Xie // See https://llvm.org/LICENSE.txt for license information.
5e356f681SHui Xie // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e356f681SHui Xie //
7e356f681SHui Xie //===----------------------------------------------------------------------===//
8e356f681SHui Xie 
9e356f681SHui Xie // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10e356f681SHui Xie 
11e356f681SHui Xie // constexpr expected& operator=(expected&& rhs) noexcept(see below);
12e356f681SHui Xie //
13e356f681SHui Xie // Constraints:
14e356f681SHui Xie // - is_move_constructible_v<T> is true and
15e356f681SHui Xie // - is_move_assignable_v<T> is true and
16e356f681SHui Xie // - is_move_constructible_v<E> is true and
17e356f681SHui Xie // - is_move_assignable_v<E> is true and
18e356f681SHui Xie // - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.
19e356f681SHui Xie //
20e356f681SHui Xie // Effects:
21e356f681SHui Xie // - If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs).
22e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to:
23e356f681SHui Xie //   reinit-expected(unex, val, std::move(rhs.error()))
24e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to:
25e356f681SHui Xie //   reinit-expected(val, unex, std::move(*rhs))
26e356f681SHui Xie // - Otherwise, equivalent to unex = std::move(rhs.error()).
27e356f681SHui Xie // - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this;
28e356f681SHui Xie //
29e356f681SHui Xie // Returns: *this.
30e356f681SHui Xie //
31e356f681SHui Xie // Remarks: The exception specification is equivalent to:
32e356f681SHui Xie // is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T> &&
33e356f681SHui Xie // is_nothrow_move_assignable_v<E> && is_nothrow_move_constructible_v<E>
34e356f681SHui Xie 
35e356f681SHui Xie #include <cassert>
36e356f681SHui Xie #include <concepts>
37e356f681SHui Xie #include <expected>
38e356f681SHui Xie #include <type_traits>
39e356f681SHui Xie #include <utility>
40e356f681SHui Xie 
41e356f681SHui Xie #include "../../types.h"
42e356f681SHui Xie #include "test_macros.h"
43e356f681SHui Xie 
44e356f681SHui Xie struct NotMoveConstructible {
45e356f681SHui Xie   NotMoveConstructible(NotMoveConstructible&&)            = delete;
46e356f681SHui Xie   NotMoveConstructible& operator=(NotMoveConstructible&&) = default;
47e356f681SHui Xie };
48e356f681SHui Xie 
49e356f681SHui Xie struct NotMoveAssignable {
50e356f681SHui Xie   NotMoveAssignable(NotMoveAssignable&&)            = default;
51e356f681SHui Xie   NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;
52e356f681SHui Xie };
53e356f681SHui Xie 
54e356f681SHui Xie struct MoveCtorMayThrow {
55e356f681SHui Xie   MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {}
56e356f681SHui Xie   MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default;
57e356f681SHui Xie };
58e356f681SHui Xie 
59e356f681SHui Xie // Test constraints
60e356f681SHui Xie static_assert(std::is_move_assignable_v<std::expected<int, int>>);
61e356f681SHui Xie 
62e356f681SHui Xie // !is_move_assignable_v<T>
63e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<NotMoveAssignable, int>>);
64e356f681SHui Xie 
65e356f681SHui Xie // !is_move_constructible_v<T>
66e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<NotMoveConstructible, int>>);
67e356f681SHui Xie 
68e356f681SHui Xie // !is_move_assignable_v<E>
69e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveAssignable>>);
70e356f681SHui Xie 
71e356f681SHui Xie // !is_move_constructible_v<E>
72e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveConstructible>>);
73e356f681SHui Xie 
74e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>
75e356f681SHui Xie static_assert(std::is_move_assignable_v<std::expected<MoveCtorMayThrow, int>>);
76e356f681SHui Xie 
77e356f681SHui Xie // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
78e356f681SHui Xie static_assert(std::is_move_assignable_v<std::expected<int, MoveCtorMayThrow>>);
79e356f681SHui Xie 
80e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
81e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<MoveCtorMayThrow, MoveCtorMayThrow>>);
82e356f681SHui Xie 
83e356f681SHui Xie struct MoveAssignMayThrow {
84e356f681SHui Xie   MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default;
85e356f681SHui Xie   MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; }
86e356f681SHui Xie };
87e356f681SHui Xie 
88e356f681SHui Xie // Test noexcept
89e356f681SHui Xie static_assert(std::is_nothrow_move_assignable_v<std::expected<int, int>>);
90e356f681SHui Xie 
91e356f681SHui Xie // !is_nothrow_move_assignable_v<T>
92e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveAssignMayThrow, int>>);
93e356f681SHui Xie 
94e356f681SHui Xie // !is_nothrow_move_constructible_v<T>
95e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveCtorMayThrow, int>>);
96e356f681SHui Xie 
97e356f681SHui Xie // !is_nothrow_move_assignable_v<E>
98e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveAssignMayThrow>>);
99e356f681SHui Xie 
100e356f681SHui Xie // !is_nothrow_move_constructible_v<E>
101e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveCtorMayThrow>>);
102e356f681SHui Xie 
103e356f681SHui Xie constexpr bool test() {
104e356f681SHui Xie   // If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs).
105e356f681SHui Xie   {
106e356f681SHui Xie     Traced::state oldState{};
107e356f681SHui Xie     Traced::state newState{};
108e356f681SHui Xie     std::expected<Traced, int> e1(std::in_place, oldState, 5);
109e356f681SHui Xie     std::expected<Traced, int> e2(std::in_place, newState, 10);
110e356f681SHui Xie     decltype(auto) x = (e1 = std::move(e2));
111e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);
112e356f681SHui Xie     assert(&x == &e1);
113e356f681SHui Xie 
114e356f681SHui Xie     assert(e1.has_value());
115e356f681SHui Xie     assert(e1.value().data_ == 10);
116e356f681SHui Xie     assert(oldState.moveAssignCalled);
117e356f681SHui Xie   }
118e356f681SHui Xie 
119e356f681SHui Xie   // - Otherwise, if this->has_value() is true, equivalent to:
120e356f681SHui Xie   // reinit-expected(unex, val, rhs.error())
121e356f681SHui Xie   //  E move is not noexcept
122e356f681SHui Xie   //  In this case, it should call the branch
123e356f681SHui Xie   //
124e356f681SHui Xie   //  U tmp(std::move(oldval));
125e356f681SHui Xie   //  destroy_at(addressof(oldval));
126e356f681SHui Xie   //  try {
127e356f681SHui Xie   //    construct_at(addressof(newval), std::forward<Args>(args)...);
128e356f681SHui Xie   //  } catch (...) {
129e356f681SHui Xie   //    construct_at(addressof(oldval), std::move(tmp));
130e356f681SHui Xie   //    throw;
131e356f681SHui Xie   //  }
132e356f681SHui Xie   //
133e356f681SHui Xie   {
134e356f681SHui Xie     TracedNoexcept::state oldState{};
135e356f681SHui Xie     Traced::state newState{};
136e356f681SHui Xie     std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5);
137e356f681SHui Xie     std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10);
138e356f681SHui Xie 
139e356f681SHui Xie     decltype(auto) x = (e1 = std::move(e2));
140e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);
141e356f681SHui Xie     assert(&x == &e1);
142e356f681SHui Xie 
143e356f681SHui Xie     assert(!e1.has_value());
144e356f681SHui Xie     assert(e1.error().data_ == 10);
145e356f681SHui Xie 
146e356f681SHui Xie     assert(!oldState.moveAssignCalled);
147e356f681SHui Xie     assert(oldState.moveCtorCalled);
148e356f681SHui Xie     assert(oldState.dtorCalled);
149e356f681SHui Xie     assert(!oldState.copyCtorCalled);
150e356f681SHui Xie     assert(!newState.copyCtorCalled);
151e356f681SHui Xie     assert(newState.moveCtorCalled);
152e356f681SHui Xie     assert(!newState.dtorCalled);
153e356f681SHui Xie   }
154e356f681SHui Xie 
155e356f681SHui Xie   // - Otherwise, if this->has_value() is true, equivalent to:
156e356f681SHui Xie   // reinit-expected(unex, val, rhs.error())
157e356f681SHui Xie   //  E move is noexcept
158e356f681SHui Xie   //  In this case, it should call the branch
159e356f681SHui Xie   //
160e356f681SHui Xie   //  destroy_at(addressof(oldval));
161e356f681SHui Xie   //  construct_at(addressof(newval), std::forward<Args>(args)...);
162e356f681SHui Xie   //
163e356f681SHui Xie   {
164e356f681SHui Xie     Traced::state oldState{};
165e356f681SHui Xie     TracedNoexcept::state newState{};
166e356f681SHui Xie     std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5);
167e356f681SHui Xie     std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10);
168e356f681SHui Xie 
169e356f681SHui Xie     decltype(auto) x = (e1 = std::move(e2));
170e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);
171e356f681SHui Xie     assert(&x == &e1);
172e356f681SHui Xie 
173e356f681SHui Xie     assert(!e1.has_value());
174e356f681SHui Xie     assert(e1.error().data_ == 10);
175e356f681SHui Xie 
176e356f681SHui Xie     assert(!oldState.moveCtorCalled);
177e356f681SHui Xie     assert(oldState.dtorCalled);
178e356f681SHui Xie     assert(!oldState.copyCtorCalled);
179e356f681SHui Xie     assert(!newState.copyCtorCalled);
180e356f681SHui Xie     assert(newState.moveCtorCalled);
181e356f681SHui Xie     assert(!newState.dtorCalled);
182e356f681SHui Xie   }
183e356f681SHui Xie 
184e356f681SHui Xie   // - Otherwise, if rhs.has_value() is true, equivalent to:
185e356f681SHui Xie   // reinit-expected(val, unex, *rhs)
186e356f681SHui Xie   //  T move is not noexcept
187e356f681SHui Xie   //  In this case, it should call the branch
188e356f681SHui Xie   //
189e356f681SHui Xie   //  U tmp(std::move(oldval));
190e356f681SHui Xie   //  destroy_at(addressof(oldval));
191e356f681SHui Xie   //  try {
192e356f681SHui Xie   //    construct_at(addressof(newval), std::forward<Args>(args)...);
193e356f681SHui Xie   //  } catch (...) {
194e356f681SHui Xie   //    construct_at(addressof(oldval), std::move(tmp));
195e356f681SHui Xie   //    throw;
196e356f681SHui Xie   //  }
197e356f681SHui Xie   //
198e356f681SHui Xie   {
199e356f681SHui Xie     TracedNoexcept::state oldState{};
200e356f681SHui Xie     Traced::state newState{};
201e356f681SHui Xie     std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5);
202e356f681SHui Xie     std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10);
203e356f681SHui Xie 
204e356f681SHui Xie     decltype(auto) x = (e1 = std::move(e2));
205e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);
206e356f681SHui Xie     assert(&x == &e1);
207e356f681SHui Xie 
208e356f681SHui Xie     assert(e1.has_value());
209e356f681SHui Xie     assert(e1.value().data_ == 10);
210e356f681SHui Xie 
211e356f681SHui Xie     assert(oldState.moveCtorCalled);
212e356f681SHui Xie     assert(oldState.dtorCalled);
213e356f681SHui Xie     assert(!oldState.copyCtorCalled);
214e356f681SHui Xie     assert(!newState.copyCtorCalled);
215e356f681SHui Xie     assert(newState.moveCtorCalled);
216e356f681SHui Xie     assert(!newState.dtorCalled);
217e356f681SHui Xie   }
218e356f681SHui Xie 
219e356f681SHui Xie   // - Otherwise, if rhs.has_value() is true, equivalent to:
220e356f681SHui Xie   // reinit-expected(val, unex, *rhs)
221e356f681SHui Xie   //  T move is noexcept
222e356f681SHui Xie   //  In this case, it should call the branch
223e356f681SHui Xie   //
224e356f681SHui Xie   //  destroy_at(addressof(oldval));
225e356f681SHui Xie   //  construct_at(addressof(newval), std::forward<Args>(args)...);
226e356f681SHui Xie   //
227e356f681SHui Xie   {
228e356f681SHui Xie     Traced::state oldState{};
229e356f681SHui Xie     TracedNoexcept::state newState{};
230e356f681SHui Xie     std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5);
231e356f681SHui Xie     std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10);
232e356f681SHui Xie 
233e356f681SHui Xie     decltype(auto) x = (e1 = std::move(e2));
234e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);
235e356f681SHui Xie     assert(&x == &e1);
236e356f681SHui Xie 
237e356f681SHui Xie     assert(e1.has_value());
238e356f681SHui Xie     assert(e1.value().data_ == 10);
239e356f681SHui Xie 
240e356f681SHui Xie     assert(!oldState.moveCtorCalled);
241e356f681SHui Xie     assert(oldState.dtorCalled);
242e356f681SHui Xie     assert(!oldState.copyCtorCalled);
243e356f681SHui Xie     assert(!newState.copyCtorCalled);
244e356f681SHui Xie     assert(newState.moveCtorCalled);
245e356f681SHui Xie     assert(!newState.dtorCalled);
246e356f681SHui Xie   }
247e356f681SHui Xie 
248e356f681SHui Xie   // Otherwise, equivalent to unex = rhs.error().
249e356f681SHui Xie   {
250e356f681SHui Xie     Traced::state oldState{};
251e356f681SHui Xie     Traced::state newState{};
252e356f681SHui Xie     std::expected<int, Traced> e1(std::unexpect, oldState, 5);
253e356f681SHui Xie     std::expected<int, Traced> e2(std::unexpect, newState, 10);
254e356f681SHui Xie     decltype(auto) x = (e1 = std::move(e2));
255e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>);
256e356f681SHui Xie     assert(&x == &e1);
257e356f681SHui Xie 
258e356f681SHui Xie     assert(!e1.has_value());
259e356f681SHui Xie     assert(e1.error().data_ == 10);
260e356f681SHui Xie     assert(oldState.moveAssignCalled);
261e356f681SHui Xie   }
2624f469053SJan Kokemüller 
2634f469053SJan Kokemüller   // CheckForInvalidWrites
2644f469053SJan Kokemüller   {
2654f469053SJan Kokemüller     {
2664f469053SJan Kokemüller       CheckForInvalidWrites<true> e1(std::unexpect);
2674f469053SJan Kokemüller       CheckForInvalidWrites<true> e2;
2684f469053SJan Kokemüller 
2694f469053SJan Kokemüller       e1 = std::move(e2);
2704f469053SJan Kokemüller 
2714f469053SJan Kokemüller       assert(e1.check());
2724f469053SJan Kokemüller       assert(e2.check());
2734f469053SJan Kokemüller     }
2744f469053SJan Kokemüller     {
2754f469053SJan Kokemüller       CheckForInvalidWrites<false> e1(std::unexpect);
2764f469053SJan Kokemüller       CheckForInvalidWrites<false> e2;
2774f469053SJan Kokemüller 
2784f469053SJan Kokemüller       e1 = std::move(e2);
2794f469053SJan Kokemüller 
2804f469053SJan Kokemüller       assert(e1.check());
2814f469053SJan Kokemüller       assert(e2.check());
2824f469053SJan Kokemüller     }
2834f469053SJan Kokemüller   }
2844f469053SJan Kokemüller 
285e356f681SHui Xie   return true;
286e356f681SHui Xie }
287e356f681SHui Xie 
288e356f681SHui Xie void testException() {
289e356f681SHui Xie #ifndef TEST_HAS_NO_EXCEPTIONS
290e356f681SHui Xie   // assign value throw on move
291e356f681SHui Xie   {
292e356f681SHui Xie     std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);
293e356f681SHui Xie     std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);
294e356f681SHui Xie     try {
295e356f681SHui Xie       e1 = std::move(e2);
296e356f681SHui Xie       assert(false);
297e356f681SHui Xie     } catch (Except) {
298e356f681SHui Xie       assert(!e1.has_value());
299e356f681SHui Xie       assert(e1.error() == 5);
300e356f681SHui Xie     }
301e356f681SHui Xie   }
302e356f681SHui Xie 
303e356f681SHui Xie   // assign error throw on move
304e356f681SHui Xie   {
305e356f681SHui Xie     std::expected<int, ThrowOnMoveConstruct> e1(5);
306e356f681SHui Xie     std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);
307e356f681SHui Xie     try {
308e356f681SHui Xie       e1 = std::move(e2);
309e356f681SHui Xie       assert(false);
310e356f681SHui Xie     } catch (Except) {
311e356f681SHui Xie       assert(e1.has_value());
312e356f681SHui Xie       assert(*e1 == 5);
313e356f681SHui Xie     }
314e356f681SHui Xie   }
315e356f681SHui Xie #endif // TEST_HAS_NO_EXCEPTIONS
316e356f681SHui Xie }
317e356f681SHui Xie 
318e356f681SHui Xie int main(int, char**) {
319e356f681SHui Xie   test();
320e356f681SHui Xie   static_assert(test());
321e356f681SHui Xie   testException();
322e356f681SHui Xie   return 0;
323e356f681SHui Xie }
324