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 G> |
12 | // constexpr expected& operator=(unexpected<G>&& e); |
13 | // |
14 | // Let GF be G |
15 | // Constraints: |
16 | // - is_constructible_v<E, GF> is true; and |
17 | // - is_assignable_v<E&, GF> is true; and |
18 | // - is_nothrow_constructible_v<E, GF> || 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: |
23 | // reinit-expected(unex, val, std::forward<GF>(e.error())); |
24 | // has_val = false; |
25 | // - Otherwise, equivalent to: unex = std::forward<GF>(e.error()); |
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 | |
37 | struct NotMoveConstructible { |
38 | NotMoveConstructible(NotMoveConstructible&&) = delete; |
39 | NotMoveConstructible& operator=(NotMoveConstructible&&) = default; |
40 | }; |
41 | |
42 | struct NotMoveAssignable { |
43 | NotMoveAssignable(NotMoveAssignable&&) = default; |
44 | NotMoveAssignable& operator=(NotMoveAssignable&&) = delete; |
45 | }; |
46 | |
47 | struct MoveMayThrow { |
48 | MoveMayThrow(MoveMayThrow const&) = default; |
49 | MoveMayThrow& operator=(const MoveMayThrow&) = default; |
50 | MoveMayThrow(MoveMayThrow&&) noexcept(false) {} |
51 | MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; } |
52 | }; |
53 | |
54 | // Test constraints |
55 | static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>&&>); |
56 | |
57 | // !is_constructible_v<E, GF> |
58 | static_assert( |
59 | !std::is_assignable_v<std::expected<int, NotMoveConstructible>&, std::unexpected<NotMoveConstructible>&&>); |
60 | |
61 | // !is_assignable_v<E&, GF> |
62 | static_assert(!std::is_assignable_v<std::expected<int, NotMoveAssignable>&, std::unexpected<NotMoveAssignable>&&>); |
63 | |
64 | template <bool moveNoexcept, bool convertNoexcept> |
65 | struct MaybeNoexcept { |
66 | explicit MaybeNoexcept(int) noexcept(convertNoexcept); |
67 | MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept); |
68 | MaybeNoexcept& operator=(MaybeNoexcept&&) = default; |
69 | MaybeNoexcept& operator=(int); |
70 | }; |
71 | |
72 | // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> && |
73 | // is_nothrow_move_constructible_v<E> |
74 | static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<true, false>>&, |
75 | std::unexpected<int>&&>); |
76 | |
77 | // is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> && |
78 | // !is_nothrow_move_constructible_v<E> |
79 | static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, true>>&, |
80 | std::unexpected<int>&&>); |
81 | |
82 | // !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<T> && |
83 | // !is_nothrow_move_constructible_v<E> |
84 | static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, true>, MaybeNoexcept<false, false>>&, |
85 | std::unexpected<int>&&>); |
86 | |
87 | // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> && |
88 | // !is_nothrow_move_constructible_v<E> |
89 | static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, |
90 | std::unexpected<int>&&>); |
91 | |
92 | constexpr bool test() { |
93 | // - If has_value() is true, equivalent to: |
94 | // reinit-expected(unex, val, std::forward<GF>(e.error())); |
95 | // is_nothrow_constructible_v<E, GF> |
96 | // |
97 | // In this case, it should call the branch |
98 | // destroy_at(addressof(oldval)); |
99 | // construct_at(addressof(newval), std::forward<Args>(args)...); |
100 | { |
101 | BothNoexcept::state oldState{}; |
102 | std::expected<BothNoexcept, BothNoexcept> e(std::in_place, oldState, 5); |
103 | std::unexpected<int> un(10); |
104 | decltype(auto) x = (e = std::move(un)); |
105 | static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothNoexcept>&>); |
106 | assert(&x == &e); |
107 | |
108 | assert(!oldState.moveCtorCalled); |
109 | assert(oldState.dtorCalled); |
110 | assert(e.error().movedFromInt); |
111 | } |
112 | |
113 | // - If has_value() is true, equivalent to: |
114 | // reinit-expected(unex, val, std::forward<GF>(e.error())); |
115 | // !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<E> |
116 | // |
117 | // In this case, it should call the branch |
118 | // T tmp(std::forward<Args>(args)...); |
119 | // destroy_at(addressof(oldval)); |
120 | // construct_at(addressof(newval), std::move(tmp)); |
121 | { |
122 | BothNoexcept::state oldState{}; |
123 | std::expected<BothNoexcept, MoveNoexceptConvThrow> e(std::in_place, oldState, 5); |
124 | std::unexpected<int> un(10); |
125 | decltype(auto) x = (e = std::move(un)); |
126 | static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, MoveNoexceptConvThrow>&>); |
127 | assert(&x == &e); |
128 | |
129 | assert(!oldState.moveCtorCalled); |
130 | assert(oldState.dtorCalled); |
131 | assert(!e.error().movedFromInt); |
132 | assert(e.error().movedFromTmp); |
133 | } |
134 | |
135 | // - If has_value() is true, equivalent to: |
136 | // reinit-expected(unex, val, std::forward<GF>(e.error())); |
137 | // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<E> |
138 | // is_nothrow_move_constructible_v<T> |
139 | // |
140 | // In this case, it should call the branch |
141 | // U tmp(std::move(oldval)); |
142 | // destroy_at(addressof(oldval)); |
143 | // try { |
144 | // construct_at(addressof(newval), std::forward<Args>(args)...); |
145 | // } catch (...) { |
146 | // construct_at(addressof(oldval), std::move(tmp)); |
147 | // throw; |
148 | // } |
149 | { |
150 | BothNoexcept::state oldState{}; |
151 | std::expected<BothNoexcept, BothMayThrow> e(std::in_place, oldState, 5); |
152 | std::unexpected<int> un(10); |
153 | decltype(auto) x = (e = std::move(un)); |
154 | static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothMayThrow>&>); |
155 | assert(&x == &e); |
156 | |
157 | assert(oldState.moveCtorCalled); |
158 | assert(oldState.dtorCalled); |
159 | assert(e.error().movedFromInt); |
160 | } |
161 | |
162 | // Otherwise, equivalent to: unex = std::forward<GF>(e.error()); |
163 | { |
164 | Traced::state oldState{}; |
165 | Traced::state newState{}; |
166 | std::expected<int, Traced> e1(std::unexpect, oldState, 5); |
167 | std::unexpected<Traced> e(std::in_place, newState, 10); |
168 | decltype(auto) x = (e1 = std::move(e)); |
169 | static_assert(std::same_as<decltype(x), std::expected<int, Traced >&>); |
170 | assert(&x == &e1); |
171 | |
172 | assert(!e1.has_value()); |
173 | assert(e1.error().data_ == 10); |
174 | assert(oldState.moveAssignCalled); |
175 | } |
176 | return true; |
177 | } |
178 | |
179 | void testException() { |
180 | #ifndef TEST_HAS_NO_EXCEPTIONS |
181 | std::expected<int, ThrowOnConvert> e1(std::in_place, 5); |
182 | std::unexpected<int> un(10); |
183 | try { |
184 | e1 = std::move(un); |
185 | assert(false); |
186 | } catch (Except) { |
187 | assert(e1.has_value()); |
188 | assert(*e1 == 5); |
189 | } |
190 | #endif // TEST_HAS_NO_EXCEPTIONS |
191 | } |
192 | |
193 | int main(int, char**) { |
194 | test(); |
195 | static_assert(test()); |
196 | testException(); |
197 | return 0; |
198 | } |
199 | |