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

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