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

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