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 | // constexpr void swap(expected& rhs) noexcept(see below); |
12 | // |
13 | // Constraints: |
14 | // is_swappable_v<T> is true and |
15 | // is_swappable_v<E> is true and |
16 | // is_move_constructible_v<T> && is_move_constructible_v<E> is true, and |
17 | // is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true. |
18 | // |
19 | // Throws: Any exception thrown by the expressions in the Effects. |
20 | // Remarks: The exception specification is equivalent to: |
21 | // is_nothrow_move_constructible_v<T> && is_nothrow_swappable_v<T> && |
22 | // is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E> |
23 | |
24 | #include <cassert> |
25 | #include <expected> |
26 | #include <type_traits> |
27 | #include <utility> |
28 | |
29 | #include "../../types.h" |
30 | #include "test_macros.h" |
31 | |
32 | // Test Constraints: |
33 | template <class T, class E> |
34 | concept HasMemberSwap = requires(std::expected<T, E> x, std::expected<T, E> y) { x.swap(y); }; |
35 | |
36 | static_assert(HasMemberSwap<int, int>); |
37 | |
38 | struct NotSwappable {}; |
39 | void swap(NotSwappable&, NotSwappable&) = delete; |
40 | |
41 | // !is_swappable_v<T> |
42 | static_assert(!HasMemberSwap<NotSwappable, int>); |
43 | |
44 | // !is_swappable_v<E> |
45 | static_assert(!HasMemberSwap<int, NotSwappable>); |
46 | |
47 | struct NotMoveConstructible { |
48 | NotMoveConstructible(NotMoveConstructible&&) = delete; |
49 | friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} |
50 | }; |
51 | |
52 | // !is_move_constructible_v<T> |
53 | static_assert(!HasMemberSwap<NotMoveConstructible, int>); |
54 | |
55 | // !is_move_constructible_v<E> |
56 | static_assert(!HasMemberSwap<int, NotMoveConstructible>); |
57 | |
58 | struct MoveMayThrow { |
59 | MoveMayThrow(MoveMayThrow&&) noexcept(false); |
60 | friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} |
61 | }; |
62 | |
63 | // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E> |
64 | static_assert(HasMemberSwap<MoveMayThrow, int>); |
65 | |
66 | // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> |
67 | static_assert(HasMemberSwap<int, MoveMayThrow>); |
68 | |
69 | // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> |
70 | static_assert(!HasMemberSwap<MoveMayThrow, MoveMayThrow>); |
71 | |
72 | // Test noexcept |
73 | template <class T, class E> |
74 | concept MemberSwapNoexcept = // |
75 | requires(std::expected<T, E> x, std::expected<T, E> y) { |
76 | { x.swap(y) } noexcept; |
77 | }; |
78 | |
79 | static_assert(MemberSwapNoexcept<int, int>); |
80 | |
81 | // !is_nothrow_move_constructible_v<T> |
82 | static_assert(!MemberSwapNoexcept<MoveMayThrow, int>); |
83 | |
84 | // !is_nothrow_move_constructible_v<E> |
85 | static_assert(!MemberSwapNoexcept<int, MoveMayThrow>); |
86 | |
87 | struct SwapMayThrow { |
88 | friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} |
89 | }; |
90 | |
91 | // !is_nothrow_swappable_v<T> |
92 | static_assert(!MemberSwapNoexcept<SwapMayThrow, int>); |
93 | |
94 | // !is_nothrow_swappable_v<E> |
95 | static_assert(!MemberSwapNoexcept<int, SwapMayThrow>); |
96 | |
97 | constexpr bool test() { |
98 | // this->has_value() && rhs.has_value() |
99 | { |
100 | std::expected<ADLSwap, int> x(std::in_place, 5); |
101 | std::expected<ADLSwap, int> y(std::in_place, 10); |
102 | x.swap(y); |
103 | |
104 | assert(x.has_value()); |
105 | assert(x->i == 10); |
106 | assert(x->adlSwapCalled); |
107 | assert(y.has_value()); |
108 | assert(y->i == 5); |
109 | assert(y->adlSwapCalled); |
110 | } |
111 | |
112 | // !this->has_value() && !rhs.has_value() |
113 | { |
114 | std::expected<int, ADLSwap> x(std::unexpect, 5); |
115 | std::expected<int, ADLSwap> y(std::unexpect, 10); |
116 | x.swap(y); |
117 | |
118 | assert(!x.has_value()); |
119 | assert(x.error().i == 10); |
120 | assert(x.error().adlSwapCalled); |
121 | assert(!y.has_value()); |
122 | assert(y.error().i == 5); |
123 | assert(y.error().adlSwapCalled); |
124 | } |
125 | |
126 | // this->has_value() && !rhs.has_value() |
127 | // && is_nothrow_move_constructible_v<E> |
128 | { |
129 | std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5); |
130 | std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10); |
131 | |
132 | e1.swap(e2); |
133 | |
134 | assert(!e1.has_value()); |
135 | assert(e1.error().i == 10); |
136 | assert(e2.has_value()); |
137 | assert(e2->i == 5); |
138 | |
139 | assert(e1.error().numberOfMoves == 2); |
140 | assert(!e1.error().swapCalled); |
141 | assert(e2->numberOfMoves == 1); |
142 | assert(!e2->swapCalled); |
143 | } |
144 | |
145 | // this->has_value() && !rhs.has_value() |
146 | // && !is_nothrow_move_constructible_v<E> |
147 | { |
148 | std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5); |
149 | std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10); |
150 | |
151 | e1.swap(e2); |
152 | |
153 | assert(!e1.has_value()); |
154 | assert(e1.error().i == 10); |
155 | assert(e2.has_value()); |
156 | assert(e2->i == 5); |
157 | |
158 | assert(e1.error().numberOfMoves == 1); |
159 | assert(!e1.error().swapCalled); |
160 | assert(e2->numberOfMoves == 2); |
161 | assert(!e2->swapCalled); |
162 | } |
163 | |
164 | // !this->has_value() && rhs.has_value() |
165 | // && is_nothrow_move_constructible_v<E> |
166 | { |
167 | std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10); |
168 | std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5); |
169 | |
170 | e1.swap(e2); |
171 | |
172 | assert(e1.has_value()); |
173 | assert(e1->i == 5); |
174 | assert(!e2.has_value()); |
175 | assert(e2.error().i == 10); |
176 | |
177 | assert(e1->numberOfMoves == 1); |
178 | assert(!e1->swapCalled); |
179 | assert(e2.error().numberOfMoves == 2); |
180 | assert(!e2.error().swapCalled); |
181 | } |
182 | |
183 | // !this->has_value() && rhs.has_value() |
184 | // && !is_nothrow_move_constructible_v<E> |
185 | { |
186 | std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10); |
187 | std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5); |
188 | |
189 | e1.swap(e2); |
190 | |
191 | assert(e1.has_value()); |
192 | assert(e1->i == 5); |
193 | assert(!e2.has_value()); |
194 | assert(e2.error().i == 10); |
195 | |
196 | assert(e1->numberOfMoves == 2); |
197 | assert(!e1->swapCalled); |
198 | assert(e2.error().numberOfMoves == 1); |
199 | assert(!e2.error().swapCalled); |
200 | } |
201 | |
202 | // TailClobberer |
203 | { |
204 | // is_nothrow_move_constructible_v<E> |
205 | { |
206 | std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> x(std::in_place); |
207 | std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> y(std::unexpect); |
208 | |
209 | x.swap(y); |
210 | |
211 | // Both of these would fail if adjusting the "has value" flags happened |
212 | // _before_ constructing the member objects inside the `swap`. |
213 | assert(!x.has_value()); |
214 | assert(y.has_value()); |
215 | } |
216 | |
217 | // !is_nothrow_move_constructible_v<E> |
218 | { |
219 | std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> x(std::in_place); |
220 | std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> y(std::unexpect); |
221 | |
222 | x.swap(y); |
223 | |
224 | // Both of these would fail if adjusting the "has value" flags happened |
225 | // _before_ constructing the member objects inside the `swap`. |
226 | assert(!x.has_value()); |
227 | assert(y.has_value()); |
228 | } |
229 | } |
230 | |
231 | // CheckForInvalidWrites |
232 | { |
233 | { |
234 | CheckForInvalidWrites<true> x(std::unexpect); |
235 | CheckForInvalidWrites<true> y; |
236 | |
237 | x.swap(y); |
238 | |
239 | assert(x.check()); |
240 | assert(y.check()); |
241 | } |
242 | { |
243 | CheckForInvalidWrites<false> x(std::unexpect); |
244 | CheckForInvalidWrites<false> y; |
245 | |
246 | x.swap(y); |
247 | |
248 | assert(x.check()); |
249 | assert(y.check()); |
250 | } |
251 | } |
252 | |
253 | return true; |
254 | } |
255 | |
256 | void testException() { |
257 | #ifndef TEST_HAS_NO_EXCEPTIONS |
258 | // !e1.has_value() && e2.has_value() |
259 | { |
260 | std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5); |
261 | std::expected<ThrowOnMoveConstruct, int> e2(std::in_place); |
262 | try { |
263 | e1.swap(e2); |
264 | assert(false); |
265 | } catch (Except) { |
266 | assert(!e1.has_value()); |
267 | assert(e1.error() == 5); |
268 | } |
269 | } |
270 | |
271 | // e1.has_value() && !e2.has_value() |
272 | { |
273 | std::expected<int, ThrowOnMoveConstruct> e1(5); |
274 | std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect); |
275 | try { |
276 | e1.swap(e2); |
277 | assert(false); |
278 | } catch (Except) { |
279 | assert(e1.has_value()); |
280 | assert(*e1 == 5); |
281 | } |
282 | } |
283 | |
284 | // TailClobberer |
285 | { |
286 | // is_nothrow_move_constructible_v<E> |
287 | { |
288 | std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> x(std::in_place); |
289 | std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> y(std::unexpect); |
290 | try { |
291 | x.swap(y); |
292 | assert(false); |
293 | } catch (Except) { |
294 | assert(x.has_value()); |
295 | // This would fail if `TailClobbererNonTrivialMove<1>` clobbered the |
296 | // flag when rolling back the swap. |
297 | assert(!y.has_value()); |
298 | } |
299 | } |
300 | |
301 | // !is_nothrow_move_constructible_v<E> |
302 | { |
303 | std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> x(std::in_place); |
304 | std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> y(std::unexpect); |
305 | try { |
306 | x.swap(y); |
307 | assert(false); |
308 | } catch (Except) { |
309 | // This would fail if `TailClobbererNonTrivialMove<0>` clobbered the |
310 | // flag when rolling back the swap. |
311 | assert(x.has_value()); |
312 | assert(!y.has_value()); |
313 | } |
314 | } |
315 | } |
316 | #endif // TEST_HAS_NO_EXCEPTIONS |
317 | } |
318 | |
319 | int main(int, char**) { |
320 | test(); |
321 | static_assert(test()); |
322 | testException(); |
323 | return 0; |
324 | } |
325 | |