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// constexpr void swap(expected& rhs) noexcept(see below);
11//
12// Constraints:
13// is_swappable_v<E> is true and is_move_constructible_v<E> is true.
14//
15// Throws: Any exception thrown by the expressions in the Effects.
16//
17// Remarks: The exception specification is equivalent to:
18// is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E>.
19
20#include <cassert>
21#include <expected>
22#include <type_traits>
23#include <utility>
24
25#include "../../types.h"
26#include "test_macros.h"
27
28// Test Constraints:
29template <class E>
30concept HasMemberSwap = requires(std::expected<void, E> x, std::expected<void, E> y) { x.swap(y); };
31
32static_assert(HasMemberSwap<int>);
33
34struct NotSwappable {};
35void swap(NotSwappable&, NotSwappable&) = delete;
36
37// !is_swappable_v<E>
38static_assert(!HasMemberSwap<NotSwappable>);
39
40struct NotMoveConstructible {
41 NotMoveConstructible(NotMoveConstructible&&) = delete;
42 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}
43};
44
45// !is_move_constructible_v<E>
46static_assert(!HasMemberSwap<NotMoveConstructible>);
47
48// Test noexcept
49struct MoveMayThrow {
50 MoveMayThrow(MoveMayThrow&&) noexcept(false);
51 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}
52};
53
54template <class E>
55concept MemberSwapNoexcept = //
56 requires(std::expected<void, E> x, std::expected<void, E> y) {
57 { x.swap(y) } noexcept;
58 };
59
60static_assert(MemberSwapNoexcept<int>);
61
62// !is_nothrow_move_constructible_v<E>
63static_assert(!MemberSwapNoexcept<MoveMayThrow>);
64
65struct SwapMayThrow {
66 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}
67};
68
69// !is_nothrow_swappable_v<E>
70static_assert(!MemberSwapNoexcept<SwapMayThrow>);
71
72constexpr bool test() {
73 // this->has_value() && rhs.has_value()
74 {
75 std::expected<void, int> x;
76 std::expected<void, int> y;
77 x.swap(y);
78
79 assert(x.has_value());
80 assert(y.has_value());
81 }
82
83 // !this->has_value() && !rhs.has_value()
84 {
85 std::expected<void, ADLSwap> x(std::unexpect, 5);
86 std::expected<void, ADLSwap> y(std::unexpect, 10);
87 x.swap(y);
88
89 assert(!x.has_value());
90 assert(x.error().i == 10);
91 assert(x.error().adlSwapCalled);
92 assert(!y.has_value());
93 assert(y.error().i == 5);
94 assert(y.error().adlSwapCalled);
95 }
96
97 // this->has_value() && !rhs.has_value()
98 {
99 Traced::state s{};
100 std::expected<void, Traced> e1(std::in_place);
101 std::expected<void, Traced> e2(std::unexpect, s, 10);
102
103 e1.swap(e2);
104
105 assert(!e1.has_value());
106 assert(e1.error().data_ == 10);
107 assert(e2.has_value());
108
109 assert(s.moveCtorCalled);
110 assert(s.dtorCalled);
111 }
112
113 // !this->has_value() && rhs.has_value()
114 {
115 Traced::state s{};
116 std::expected<void, Traced> e1(std::unexpect, s, 10);
117 std::expected<void, Traced> e2(std::in_place);
118
119 e1.swap(e2);
120
121 assert(e1.has_value());
122 assert(!e2.has_value());
123 assert(e2.error().data_ == 10);
124
125 assert(s.moveCtorCalled);
126 assert(s.dtorCalled);
127 }
128
129 // TailClobberer
130 {
131 std::expected<void, TailClobbererNonTrivialMove<1>> x(std::in_place);
132 std::expected<void, TailClobbererNonTrivialMove<1>> y(std::unexpect);
133
134 x.swap(y);
135
136 // The next line would fail if adjusting the "has value" flag happened
137 // _before_ constructing the member object inside the `swap`.
138 assert(!x.has_value());
139 assert(y.has_value());
140 }
141
142 // CheckForInvalidWrites
143 {
144 {
145 CheckForInvalidWrites<true, true> x(std::unexpect);
146 CheckForInvalidWrites<true, true> y;
147
148 x.swap(y);
149
150 assert(x.check());
151 assert(y.check());
152 }
153 {
154 CheckForInvalidWrites<false, true> x(std::unexpect);
155 CheckForInvalidWrites<false, true> y;
156
157 x.swap(y);
158
159 assert(x.check());
160 assert(y.check());
161 }
162 }
163
164 return true;
165}
166
167void testException() {
168#ifndef TEST_HAS_NO_EXCEPTIONS
169 // !e1.has_value() && e2.has_value()
170 {
171 bool e1Destroyed = false;
172 std::expected<void, ThrowOnMove> e1(std::unexpect, e1Destroyed);
173 std::expected<void, ThrowOnMove> e2(std::in_place);
174 try {
175 e1.swap(e2);
176 assert(false);
177 } catch (Except) {
178 assert(!e1.has_value());
179 assert(e2.has_value());
180 assert(!e1Destroyed);
181 }
182 }
183
184 // e1.has_value() && !e2.has_value()
185 {
186 bool e2Destroyed = false;
187 std::expected<void, ThrowOnMove> e1(std::in_place);
188 std::expected<void, ThrowOnMove> e2(std::unexpect, e2Destroyed);
189 try {
190 e1.swap(e2);
191 assert(false);
192 } catch (Except) {
193 assert(e1.has_value());
194 assert(!e2.has_value());
195 assert(!e2Destroyed);
196 }
197 }
198
199 // TailClobberer
200 {
201 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> x(std::in_place);
202 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> y(std::unexpect);
203 try {
204 x.swap(y);
205 assert(false);
206 } catch (Except) {
207 // This would fail if `TailClobbererNonTrivialMove<0, false, true>`
208 // clobbered the flag before throwing the exception.
209 assert(x.has_value());
210 assert(!y.has_value());
211 }
212 }
213#endif // TEST_HAS_NO_EXCEPTIONS
214}
215
216int main(int, char**) {
217 test();
218 static_assert(test());
219 testException();
220 return 0;
221}
222

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