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

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