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

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