xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
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