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:
33template <class T, class E>
34concept HasMemberSwap = requires(std::expected<T, E> x, std::expected<T, E> y) { x.swap(y); };
35
36static_assert(HasMemberSwap<int, int>);
37
38struct NotSwappable {};
39void swap(NotSwappable&, NotSwappable&) = delete;
40
41// !is_swappable_v<T>
42static_assert(!HasMemberSwap<NotSwappable, int>);
43
44// !is_swappable_v<E>
45static_assert(!HasMemberSwap<int, NotSwappable>);
46
47struct NotMoveConstructible {
48 NotMoveConstructible(NotMoveConstructible&&) = delete;
49 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}
50};
51
52// !is_move_constructible_v<T>
53static_assert(!HasMemberSwap<NotMoveConstructible, int>);
54
55// !is_move_constructible_v<E>
56static_assert(!HasMemberSwap<int, NotMoveConstructible>);
57
58struct 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>
64static_assert(HasMemberSwap<MoveMayThrow, int>);
65
66// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
67static_assert(HasMemberSwap<int, MoveMayThrow>);
68
69// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
70static_assert(!HasMemberSwap<MoveMayThrow, MoveMayThrow>);
71
72// Test noexcept
73template <class T, class E>
74concept MemberSwapNoexcept = //
75 requires(std::expected<T, E> x, std::expected<T, E> y) {
76 { x.swap(y) } noexcept;
77 };
78
79static_assert(MemberSwapNoexcept<int, int>);
80
81// !is_nothrow_move_constructible_v<T>
82static_assert(!MemberSwapNoexcept<MoveMayThrow, int>);
83
84// !is_nothrow_move_constructible_v<E>
85static_assert(!MemberSwapNoexcept<int, MoveMayThrow>);
86
87struct SwapMayThrow {
88 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}
89};
90
91// !is_nothrow_swappable_v<T>
92static_assert(!MemberSwapNoexcept<SwapMayThrow, int>);
93
94// !is_nothrow_swappable_v<E>
95static_assert(!MemberSwapNoexcept<int, SwapMayThrow>);
96
97constexpr 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
256void 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
319int main(int, char**) {
320 test();
321 static_assert(test());
322 testException();
323 return 0;
324}
325

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