xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/assign/assign.copy.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=(const expected& rhs);
12e356f681SHui Xie //
13e356f681SHui Xie // Effects:
14e356f681SHui Xie // - If this->has_value() && rhs.has_value() is true, equivalent to val = *rhs.
15e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to:
16e356f681SHui Xie //  reinit-expected(unex, val, rhs.error())
17e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to:
18e356f681SHui Xie //  reinit-expected(val, unex, *rhs)
19e356f681SHui Xie // - Otherwise, equivalent to unex = rhs.error().
20e356f681SHui Xie //
21e356f681SHui Xie // - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this;
22e356f681SHui Xie //
23e356f681SHui Xie // Returns: *this.
24e356f681SHui Xie //
25e356f681SHui Xie // Remarks: This operator is defined as deleted unless:
26e356f681SHui Xie // - is_copy_assignable_v<T> is true and
27e356f681SHui Xie // - is_copy_constructible_v<T> is true and
28e356f681SHui Xie // - is_copy_assignable_v<E> is true and
29e356f681SHui Xie // - is_copy_constructible_v<E> is true and
30e356f681SHui Xie // - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.
31e356f681SHui Xie 
32e356f681SHui Xie #include <cassert>
33e356f681SHui Xie #include <concepts>
34e356f681SHui Xie #include <expected>
35e356f681SHui Xie #include <type_traits>
36e356f681SHui Xie #include <utility>
37e356f681SHui Xie 
38e356f681SHui Xie #include "../../types.h"
39e356f681SHui Xie #include "test_macros.h"
40e356f681SHui Xie 
41e356f681SHui Xie struct NotCopyConstructible {
42e356f681SHui Xie   NotCopyConstructible(const NotCopyConstructible&)            = delete;
43e356f681SHui Xie   NotCopyConstructible& operator=(const NotCopyConstructible&) = default;
44e356f681SHui Xie };
45e356f681SHui Xie 
46e356f681SHui Xie struct NotCopyAssignable {
47e356f681SHui Xie   NotCopyAssignable(const NotCopyAssignable&)            = default;
48e356f681SHui Xie   NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;
49e356f681SHui Xie };
50e356f681SHui Xie 
51e356f681SHui Xie struct MoveMayThrow {
52e356f681SHui Xie   MoveMayThrow(MoveMayThrow const&)            = default;
53e356f681SHui Xie   MoveMayThrow& operator=(const MoveMayThrow&) = default;
54e356f681SHui Xie   MoveMayThrow(MoveMayThrow&&) noexcept(false) {}
55e356f681SHui Xie   MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; }
56e356f681SHui Xie };
57e356f681SHui Xie 
58e356f681SHui Xie // Test constraints
59e356f681SHui Xie static_assert(std::is_copy_assignable_v<std::expected<int, int>>);
60e356f681SHui Xie 
61e356f681SHui Xie // !is_copy_assignable_v<T>
62e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<NotCopyAssignable, int>>);
63e356f681SHui Xie 
64e356f681SHui Xie // !is_copy_constructible_v<T>
65e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<NotCopyConstructible, int>>);
66e356f681SHui Xie 
67e356f681SHui Xie // !is_copy_assignable_v<E>
68e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<int, NotCopyAssignable>>);
69e356f681SHui Xie 
70e356f681SHui Xie // !is_copy_constructible_v<E>
71e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<int, NotCopyConstructible>>);
72e356f681SHui Xie 
73e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>
74e356f681SHui Xie static_assert(std::is_copy_assignable_v<std::expected<MoveMayThrow, int>>);
75e356f681SHui Xie 
76e356f681SHui Xie // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
77e356f681SHui Xie static_assert(std::is_copy_assignable_v<std::expected<int, MoveMayThrow>>);
78e356f681SHui Xie 
79e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
80e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<MoveMayThrow, MoveMayThrow>>);
81e356f681SHui Xie 
82e356f681SHui Xie constexpr bool test() {
83e356f681SHui Xie   // If this->has_value() && rhs.has_value() is true, equivalent to val = *rhs.
84e356f681SHui Xie   {
85e356f681SHui Xie     Traced::state oldState{};
86e356f681SHui Xie     Traced::state newState{};
87e356f681SHui Xie     std::expected<Traced, int> e1(std::in_place, oldState, 5);
88e356f681SHui Xie     const std::expected<Traced, int> e2(std::in_place, newState, 10);
89e356f681SHui Xie     decltype(auto) x = (e1 = e2);
90e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);
91e356f681SHui Xie     assert(&x == &e1);
92e356f681SHui Xie 
93e356f681SHui Xie     assert(e1.has_value());
94e356f681SHui Xie     assert(e1.value().data_ == 10);
95e356f681SHui Xie     assert(oldState.copyAssignCalled);
96e356f681SHui Xie   }
97e356f681SHui Xie 
98e356f681SHui Xie   // - Otherwise, if this->has_value() is true, equivalent to:
99e356f681SHui Xie   // reinit-expected(unex, val, rhs.error())
100e356f681SHui Xie   //  E move is not noexcept
101e356f681SHui Xie   //  In this case, it should call the branch
102e356f681SHui Xie   //
103e356f681SHui Xie   //  U tmp(std::move(oldval));
104e356f681SHui Xie   //  destroy_at(addressof(oldval));
105e356f681SHui Xie   //  try {
106e356f681SHui Xie   //    construct_at(addressof(newval), std::forward<Args>(args)...);
107e356f681SHui Xie   //  } catch (...) {
108e356f681SHui Xie   //    construct_at(addressof(oldval), std::move(tmp));
109e356f681SHui Xie   //    throw;
110e356f681SHui Xie   //  }
111e356f681SHui Xie   //
112e356f681SHui Xie   {
113e356f681SHui Xie     TracedNoexcept::state oldState{};
114e356f681SHui Xie     Traced::state newState{};
115e356f681SHui Xie     std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5);
116e356f681SHui Xie     const std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10);
117e356f681SHui Xie 
118e356f681SHui Xie     decltype(auto) x = (e1 = e2);
119e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);
120e356f681SHui Xie     assert(&x == &e1);
121e356f681SHui Xie 
122e356f681SHui Xie     assert(!e1.has_value());
123e356f681SHui Xie     assert(e1.error().data_ == 10);
124e356f681SHui Xie 
125e356f681SHui Xie     assert(!oldState.copyAssignCalled);
126e356f681SHui Xie     assert(oldState.moveCtorCalled);
127e356f681SHui Xie     assert(oldState.dtorCalled);
128e356f681SHui Xie     assert(!oldState.copyCtorCalled);
129e356f681SHui Xie     assert(newState.copyCtorCalled);
130e356f681SHui Xie     assert(!newState.moveCtorCalled);
131e356f681SHui Xie     assert(!newState.dtorCalled);
132e356f681SHui Xie   }
133e356f681SHui Xie 
134e356f681SHui Xie   // - Otherwise, if this->has_value() is true, equivalent to:
135e356f681SHui Xie   // reinit-expected(unex, val, rhs.error())
136e356f681SHui Xie   //  E move is noexcept
137e356f681SHui Xie   //  In this case, it should call the branch
138e356f681SHui Xie   //
139e356f681SHui Xie   //  destroy_at(addressof(oldval));
140e356f681SHui Xie   //  construct_at(addressof(newval), std::forward<Args>(args)...);
141e356f681SHui Xie   //
142e356f681SHui Xie   {
143e356f681SHui Xie     Traced::state oldState{};
144e356f681SHui Xie     TracedNoexcept::state newState{};
145e356f681SHui Xie     std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5);
146e356f681SHui Xie     const std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10);
147e356f681SHui Xie 
148e356f681SHui Xie     decltype(auto) x = (e1 = e2);
149e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);
150e356f681SHui Xie     assert(&x == &e1);
151e356f681SHui Xie 
152e356f681SHui Xie     assert(!e1.has_value());
153e356f681SHui Xie     assert(e1.error().data_ == 10);
154e356f681SHui Xie 
155e356f681SHui Xie     assert(!oldState.copyAssignCalled);
156e356f681SHui Xie     assert(!oldState.moveCtorCalled);
157e356f681SHui Xie     assert(oldState.dtorCalled);
158e356f681SHui Xie     assert(!oldState.copyCtorCalled);
159e356f681SHui Xie     assert(newState.copyCtorCalled);
160e356f681SHui Xie     assert(!newState.moveCtorCalled);
161e356f681SHui Xie     assert(!newState.dtorCalled);
162e356f681SHui Xie   }
163e356f681SHui Xie 
164e356f681SHui Xie   // - Otherwise, if rhs.has_value() is true, equivalent to:
165e356f681SHui Xie   // reinit-expected(val, unex, *rhs)
166e356f681SHui Xie   //  T move is not noexcept
167e356f681SHui Xie   //  In this case, it should call the branch
168e356f681SHui Xie   //
169e356f681SHui Xie   //  U tmp(std::move(oldval));
170e356f681SHui Xie   //  destroy_at(addressof(oldval));
171e356f681SHui Xie   //  try {
172e356f681SHui Xie   //    construct_at(addressof(newval), std::forward<Args>(args)...);
173e356f681SHui Xie   //  } catch (...) {
174e356f681SHui Xie   //    construct_at(addressof(oldval), std::move(tmp));
175e356f681SHui Xie   //    throw;
176e356f681SHui Xie   //  }
177e356f681SHui Xie   //
178e356f681SHui Xie   {
179e356f681SHui Xie     TracedNoexcept::state oldState{};
180e356f681SHui Xie     Traced::state newState{};
181e356f681SHui Xie     std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5);
182e356f681SHui Xie     const std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10);
183e356f681SHui Xie 
184e356f681SHui Xie     decltype(auto) x = (e1 = e2);
185e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);
186e356f681SHui Xie     assert(&x == &e1);
187e356f681SHui Xie 
188e356f681SHui Xie     assert(e1.has_value());
189e356f681SHui Xie     assert(e1.value().data_ == 10);
190e356f681SHui Xie 
191e356f681SHui Xie     assert(!oldState.copyAssignCalled);
192e356f681SHui Xie     assert(oldState.moveCtorCalled);
193e356f681SHui Xie     assert(oldState.dtorCalled);
194e356f681SHui Xie     assert(!oldState.copyCtorCalled);
195e356f681SHui Xie     assert(newState.copyCtorCalled);
196e356f681SHui Xie     assert(!newState.moveCtorCalled);
197e356f681SHui Xie     assert(!newState.dtorCalled);
198e356f681SHui Xie   }
199e356f681SHui Xie 
200e356f681SHui Xie   // - Otherwise, if rhs.has_value() is true, equivalent to:
201e356f681SHui Xie   // reinit-expected(val, unex, *rhs)
202e356f681SHui Xie   //  T move is noexcept
203e356f681SHui Xie   //  In this case, it should call the branch
204e356f681SHui Xie   //
205e356f681SHui Xie   //  destroy_at(addressof(oldval));
206e356f681SHui Xie   //  construct_at(addressof(newval), std::forward<Args>(args)...);
207e356f681SHui Xie   //
208e356f681SHui Xie   {
209e356f681SHui Xie     Traced::state oldState{};
210e356f681SHui Xie     TracedNoexcept::state newState{};
211e356f681SHui Xie     std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5);
212e356f681SHui Xie     const std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10);
213e356f681SHui Xie 
214e356f681SHui Xie     decltype(auto) x = (e1 = e2);
215e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);
216e356f681SHui Xie     assert(&x == &e1);
217e356f681SHui Xie 
218e356f681SHui Xie     assert(e1.has_value());
219e356f681SHui Xie     assert(e1.value().data_ == 10);
220e356f681SHui Xie 
221e356f681SHui Xie     assert(!oldState.copyAssignCalled);
222e356f681SHui Xie     assert(!oldState.moveCtorCalled);
223e356f681SHui Xie     assert(oldState.dtorCalled);
224e356f681SHui Xie     assert(!oldState.copyCtorCalled);
225e356f681SHui Xie     assert(newState.copyCtorCalled);
226e356f681SHui Xie     assert(!newState.moveCtorCalled);
227e356f681SHui Xie     assert(!newState.dtorCalled);
228e356f681SHui Xie   }
229e356f681SHui Xie 
230e356f681SHui Xie   // Otherwise, equivalent to unex = rhs.error().
231e356f681SHui Xie   {
232e356f681SHui Xie     Traced::state oldState{};
233e356f681SHui Xie     Traced::state newState{};
234e356f681SHui Xie     std::expected<int, Traced> e1(std::unexpect, oldState, 5);
235e356f681SHui Xie     const std::expected<int, Traced> e2(std::unexpect, newState, 10);
236e356f681SHui Xie     decltype(auto) x = (e1 = e2);
237e356f681SHui Xie     static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>);
238e356f681SHui Xie     assert(&x == &e1);
239e356f681SHui Xie 
240e356f681SHui Xie     assert(!e1.has_value());
241e356f681SHui Xie     assert(e1.error().data_ == 10);
242e356f681SHui Xie     assert(oldState.copyAssignCalled);
243e356f681SHui Xie   }
2444f469053SJan Kokemüller 
2454f469053SJan Kokemüller   // CheckForInvalidWrites
2464f469053SJan Kokemüller   {
2474f469053SJan Kokemüller     {
2484f469053SJan Kokemüller       CheckForInvalidWrites<true> e1(std::unexpect);
2494f469053SJan Kokemüller       CheckForInvalidWrites<true> e2;
2504f469053SJan Kokemüller 
2514f469053SJan Kokemüller       e1 = e2;
2524f469053SJan Kokemüller 
2534f469053SJan Kokemüller       assert(e1.check());
2544f469053SJan Kokemüller       assert(e2.check());
2554f469053SJan Kokemüller     }
2564f469053SJan Kokemüller     {
2574f469053SJan Kokemüller       CheckForInvalidWrites<false> e1(std::unexpect);
2584f469053SJan Kokemüller       CheckForInvalidWrites<false> e2;
2594f469053SJan Kokemüller 
2604f469053SJan Kokemüller       e1 = e2;
2614f469053SJan Kokemüller 
2624f469053SJan Kokemüller       assert(e1.check());
2634f469053SJan Kokemüller       assert(e2.check());
2644f469053SJan Kokemüller     }
2654f469053SJan Kokemüller   }
2664f469053SJan Kokemüller 
267e356f681SHui Xie   return true;
268e356f681SHui Xie }
269e356f681SHui Xie 
270e356f681SHui Xie void testException() {
271e356f681SHui Xie #ifndef TEST_HAS_NO_EXCEPTIONS
272e356f681SHui Xie   struct ThrowOnCopyMoveMayThrow {
273e356f681SHui Xie     ThrowOnCopyMoveMayThrow() = default;
274e356f681SHui Xie     ThrowOnCopyMoveMayThrow(const ThrowOnCopyMoveMayThrow&) { throw Except{}; };
275e356f681SHui Xie     ThrowOnCopyMoveMayThrow& operator=(const ThrowOnCopyMoveMayThrow&) = default;
276e356f681SHui Xie     ThrowOnCopyMoveMayThrow(ThrowOnCopyMoveMayThrow&&) noexcept(false) {}
277e356f681SHui Xie   };
278e356f681SHui Xie 
279e356f681SHui Xie   // assign value throw on copy
280e356f681SHui Xie   {
281e356f681SHui Xie     std::expected<ThrowOnCopyMoveMayThrow, int> e1(std::unexpect, 5);
282e356f681SHui Xie     const std::expected<ThrowOnCopyMoveMayThrow, int> e2(std::in_place);
283e356f681SHui Xie     try {
284e356f681SHui Xie       e1 = e2;
285e356f681SHui Xie       assert(false);
286e356f681SHui Xie     } catch (Except) {
287e356f681SHui Xie       assert(!e1.has_value());
288e356f681SHui Xie       assert(e1.error() == 5);
289e356f681SHui Xie     }
290e356f681SHui Xie   }
291e356f681SHui Xie 
292e356f681SHui Xie   // assign error throw on copy
293e356f681SHui Xie   {
294e356f681SHui Xie     std::expected<int, ThrowOnCopyMoveMayThrow> e1(5);
295e356f681SHui Xie     const std::expected<int, ThrowOnCopyMoveMayThrow> e2(std::unexpect);
296e356f681SHui Xie     try {
297e356f681SHui Xie       e1 = e2;
298e356f681SHui Xie       assert(false);
299e356f681SHui Xie     } catch (Except) {
300e356f681SHui Xie       assert(e1.has_value());
301e356f681SHui Xie       assert(e1.value() == 5);
302e356f681SHui Xie     }
303e356f681SHui Xie   }
304e356f681SHui Xie #endif // TEST_HAS_NO_EXCEPTIONS
305e356f681SHui Xie }
306e356f681SHui Xie 
307e356f681SHui Xie int main(int, char**) {
308e356f681SHui Xie   test();
309e356f681SHui Xie   static_assert(test());
310e356f681SHui Xie   testException();
311e356f681SHui Xie   return 0;
312e356f681SHui Xie }
313