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

source code of libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp