xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/swap/free.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 // 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:
22 struct NotSwappable {
23   NotSwappable operator=(const NotSwappable&) = delete;
24 };
25 void swap(NotSwappable&, NotSwappable&) = delete;
26 
27 static_assert(std::is_swappable_v<std::expected<int, int>>);
28 
29 // !is_swappable_v<T>
30 static_assert(!std::is_swappable_v<std::expected<NotSwappable, int>>);
31 
32 // !is_swappable_v<E>
33 static_assert(!std::is_swappable_v<std::expected<int, NotSwappable>>);
34 
35 struct NotMoveConstructible {
36   NotMoveConstructible(NotMoveConstructible&&) = delete;
37   friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}
38 };
39 
40 // !is_move_constructible_v<T>
41 static_assert(!std::is_swappable_v<std::expected<NotMoveConstructible, int>>);
42 
43 // !is_move_constructible_v<E>
44 static_assert(!std::is_swappable_v<std::expected<int, NotMoveConstructible>>);
45 
46 struct 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>
52 static_assert(std::is_swappable_v<std::expected<MoveMayThrow, int>>);
53 
54 // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
55 static_assert(std::is_swappable_v<std::expected<int, MoveMayThrow>>);
56 
57 // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
58 static_assert(!std::is_swappable_v<std::expected<MoveMayThrow, MoveMayThrow>>);
59 
60 // Test noexcept
61 static_assert(std::is_nothrow_swappable_v<std::expected<int, int>>);
62 
63 // !is_nothrow_move_constructible_v<T>
64 static_assert(!std::is_nothrow_swappable_v<std::expected<MoveMayThrow, int>>);
65 
66 // !is_nothrow_move_constructible_v<E>
67 static_assert(!std::is_nothrow_swappable_v<std::expected<int, MoveMayThrow>>);
68 
69 struct SwapMayThrow {
70   friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}
71 };
72 
73 // !is_nothrow_swappable_v<T>
74 static_assert(!std::is_nothrow_swappable_v<std::expected<SwapMayThrow, int>>);
75 
76 // !is_nothrow_swappable_v<E>
77 static_assert(!std::is_nothrow_swappable_v<std::expected<int, SwapMayThrow>>);
78 
79 constexpr 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 
216 void 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 
279 int main(int, char**) {
280   test();
281   static_assert(test());
282   testException();
283   return 0;
284 }
285