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
10 // UNSUPPORTED: libcpp-no-concepts
11
12 // template<class T>
13 // concept swappable = // see below
14
15 #include <concepts>
16
17 #include <algorithm>
18 #include <cassert>
19 #include <deque>
20 #include <map>
21 #include <memory>
22 #include <string>
23 #include <optional>
24 #include <unordered_map>
25 #include <vector>
26
27 #include "test_macros.h"
28 #include "type_classification/moveconstructible.h"
29 #include "type_classification/swappable.h"
30
31 template <class T>
32 struct expected {
33 T x;
34 T y;
35 };
36
37 // clang-format off
38 // Checks [concept.swappable]/2.1
39 template <class T, class U>
40 requires std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U> > &&
41 std::swappable<std::remove_cvref_t<T> >
check_swap_21(T && x,U && y)42 constexpr bool check_swap_21(T&& x, U&& y) {
43 expected<std::remove_cvref_t<T> > const e{y, x};
44 std::ranges::swap(std::forward<T>(x), std::forward<U>(y));
45 return x == e.x && y == e.y;
46 }
47
48 // Checks [concept.swappable]/2.2
49 template <std::swappable T, std::size_t N>
check_swap_22(T (& x)[N],T (& y)[N])50 constexpr bool check_swap_22(T (&x)[N], T (&y)[N]) {
51 expected<T[N]> e;
52 std::copy(y, y + N, e.x);
53 std::copy(x, x + N, e.y);
54
55 std::ranges::swap(x, y);
56 return std::equal(x, x + N, e.x, e.x + N) &&
57 std::equal(y, y + N, e.y, e.y + N);
58 }
59
60 // Checks [concept.swappable]/2.3
61 template <std::swappable T>
62 requires std::copy_constructible<std::remove_cvref_t<T> >
check_swap_23(T x,T y)63 constexpr bool check_swap_23(T x, T y) {
64 expected<std::remove_cvref_t<T> > const e{y, x};
65 std::ranges::swap(x, y);
66 return x == e.x && y == e.y;
67 }
68 // clang-format on
69
check_lvalue_adl_swappable()70 constexpr bool check_lvalue_adl_swappable() {
71 auto x = lvalue_adl_swappable(0);
72 auto y = lvalue_adl_swappable(1);
73 constexpr auto is_noexcept = noexcept(std::ranges::swap(x, y));
74 return check_swap_21(x, y) && is_noexcept;
75 }
76 static_assert(check_lvalue_adl_swappable());
77
check_rvalue_adl_swappable()78 constexpr bool check_rvalue_adl_swappable() {
79 constexpr auto is_noexcept = noexcept(
80 std::ranges::swap(rvalue_adl_swappable(0), rvalue_adl_swappable(1)));
81 return check_swap_21(rvalue_adl_swappable(0), rvalue_adl_swappable(1)) &&
82 is_noexcept;
83 }
84 static_assert(check_rvalue_adl_swappable());
85
check_lvalue_rvalue_adl_swappable()86 constexpr bool check_lvalue_rvalue_adl_swappable() {
87 auto x = lvalue_rvalue_adl_swappable(0);
88 constexpr auto is_noexcept =
89 noexcept(std::ranges::swap(x, lvalue_rvalue_adl_swappable(1)));
90 return check_swap_21(x, lvalue_rvalue_adl_swappable(1)) && is_noexcept;
91 }
92 static_assert(check_lvalue_rvalue_adl_swappable());
93
check_rvalue_lvalue_adl_swappable()94 constexpr bool check_rvalue_lvalue_adl_swappable() {
95 auto x = rvalue_lvalue_adl_swappable(0);
96 constexpr auto is_noexcept =
97 noexcept(std::ranges::swap(rvalue_lvalue_adl_swappable(1), x));
98 return check_swap_21(rvalue_lvalue_adl_swappable(1), x) && is_noexcept;
99 }
100 static_assert(check_rvalue_lvalue_adl_swappable());
101
check_throwable_swappable()102 constexpr bool check_throwable_swappable() {
103 auto x = throwable_adl_swappable{0};
104 auto y = throwable_adl_swappable{1};
105 constexpr auto not_noexcept = !noexcept(std::ranges::swap(x, y));
106 return check_swap_21(x, y) && not_noexcept;
107 }
108 static_assert(check_throwable_swappable());
109
check_non_move_constructible_adl_swappable()110 constexpr bool check_non_move_constructible_adl_swappable() {
111 auto x = non_move_constructible_adl_swappable{0};
112 auto y = non_move_constructible_adl_swappable{1};
113 constexpr auto is_noexcept = noexcept(std::ranges::swap(x, y));
114 return check_swap_21(x, y) && is_noexcept;
115 }
116 static_assert(check_non_move_constructible_adl_swappable());
117
check_non_move_assignable_adl_swappable()118 constexpr bool check_non_move_assignable_adl_swappable() {
119 auto x = non_move_assignable_adl_swappable{0};
120 auto y = non_move_assignable_adl_swappable{1};
121 return check_swap_21(x, y) && noexcept(std::ranges::swap(x, y));
122 }
123 static_assert(check_non_move_assignable_adl_swappable());
124
125 namespace swappable_namespace {
126 enum unscoped { hello, world };
127 void swap(unscoped&, unscoped&);
128
129 enum class scoped { hello, world };
130 void swap(scoped&, scoped&);
131 } // namespace swappable_namespace
132
133 static_assert(std::swappable<swappable_namespace::unscoped>);
134 static_assert(std::swappable<swappable_namespace::scoped>);
135
check_swap_arrays()136 constexpr bool check_swap_arrays() {
137 int x[] = {0, 1, 2, 3, 4};
138 int y[] = {5, 6, 7, 8, 9};
139 return check_swap_22(x, y) && noexcept(std::ranges::swap(x, y));
140 }
141 static_assert(check_swap_arrays());
142
check_lvalue_adl_swappable_arrays()143 constexpr bool check_lvalue_adl_swappable_arrays() {
144 lvalue_adl_swappable x[] = {{0}, {1}, {2}, {3}};
145 lvalue_adl_swappable y[] = {{4}, {5}, {6}, {7}};
146 return check_swap_22(x, y) && noexcept(std::ranges::swap(x, y));
147 }
148 static_assert(check_lvalue_adl_swappable_arrays());
149
check_throwable_adl_swappable_arrays()150 constexpr bool check_throwable_adl_swappable_arrays() {
151 throwable_adl_swappable x[] = {{0}, {1}, {2}, {3}};
152 throwable_adl_swappable y[] = {{4}, {5}, {6}, {7}};
153 return check_swap_22(x, y) && !noexcept(std::ranges::swap(x, y));
154 }
155 static_assert(check_throwable_adl_swappable_arrays());
156
157 inline auto global_x = 0;
158 static_assert(check_swap_23(0, 0) &&
159 noexcept(std::ranges::swap(global_x, global_x)));
160 static_assert(check_swap_23(0, 1) &&
161 noexcept(std::ranges::swap(global_x, global_x)));
162 static_assert(check_swap_23(1, 0) &&
163 noexcept(std::ranges::swap(global_x, global_x)));
164
check_swappable_references()165 constexpr bool check_swappable_references() {
166 int x = 42;
167 int y = 64;
168 return check_swap_23<int&>(x, y) && noexcept(std::ranges::swap(x, y));
169 }
170 static_assert(check_swappable_references());
171
check_swappable_pointers()172 constexpr bool check_swappable_pointers() {
173 char const* x = "hello";
174 return check_swap_23<char const*>(x, nullptr) &&
175 noexcept(std::ranges::swap(x, x));
176 }
177 static_assert(check_swappable_pointers());
178
179 namespace union_swap {
180 union adl_swappable {
181 int x;
182 double y;
183 };
184
185 void swap(adl_swappable&, adl_swappable&);
186 void swap(adl_swappable&&, adl_swappable&&);
187 }; // namespace union_swap
188 static_assert(std::swappable<union_swap::adl_swappable>);
189 static_assert(std::swappable<union_swap::adl_swappable&>);
190 static_assert(std::swappable<union_swap::adl_swappable&&>);
191
192 // All tests for std::swappable<T> are implicitly confirmed by `check_swap`, so we only need to
193 // sanity check for a few positive cases.
194 static_assert(std::swappable<int volatile&>);
195 static_assert(std::swappable<int&&>);
196 static_assert(std::swappable<int (*)()>);
197 static_assert(std::swappable<int rvalue_adl_swappable::*>);
198 static_assert(std::swappable<int (rvalue_adl_swappable::*)()>);
199 static_assert(std::swappable<std::unique_ptr<int> >);
200
201 static_assert(!std::swappable<void>);
202 static_assert(!std::swappable<int const>);
203 static_assert(!std::swappable<int const&>);
204 static_assert(!std::swappable<int const&&>);
205 static_assert(!std::swappable<int const volatile>);
206 static_assert(!std::swappable<int const volatile&>);
207 static_assert(!std::swappable<int const volatile&&>);
208 static_assert(!std::swappable<int (&)()>);
209 static_assert(!std::swappable<DeletedMoveCtor>);
210 static_assert(!std::swappable<ImplicitlyDeletedMoveCtor>);
211 static_assert(!std::swappable<DeletedMoveAssign>);
212 static_assert(!std::swappable<ImplicitlyDeletedMoveAssign>);
213 static_assert(!std::swappable<NonMovable>);
214 static_assert(!std::swappable<DerivedFromNonMovable>);
215 static_assert(!std::swappable<HasANonMovable>);
216
217 using swap_type = std::remove_const_t<decltype(std::ranges::swap)>;
218 static_assert(std::default_initializable<swap_type>);
219 static_assert(std::move_constructible<swap_type>);
220 static_assert(std::copy_constructible<swap_type>);
221 static_assert(std::assignable_from<swap_type&, swap_type>);
222 static_assert(std::assignable_from<swap_type&, swap_type&>);
223 static_assert(std::assignable_from<swap_type&, swap_type const&>);
224 static_assert(std::assignable_from<swap_type&, swap_type const>);
225 static_assert(std::swappable<swap_type>);
226
227 template <bool is_noexcept, std::swappable T>
check_swap(expected<T> const & e)228 void check_swap(expected<T> const& e) {
229 auto a = e.y;
230 auto b = e.x;
231
232 std::ranges::swap(a, b);
233 assert(a == e.x);
234 assert(b == e.y);
235
236 std::ranges::swap(a, b);
237 assert(a == e.y);
238 assert(b == e.x);
239
240 static_assert(noexcept(std::ranges::swap(a, b)) == is_noexcept);
241 }
242
main(int,char **)243 int main(int, char**) {
244 {
245 auto const e = expected<std::deque<int> >{
246 .x = {6, 7, 8, 9},
247 .y = {0, 1, 2, 3, 4, 5},
248 };
249 check_swap</*is_noexcept=*/true>(e);
250 }
251 {
252 auto const e = expected<std::map<int, std::string> >{
253 .x = {{0, "whole"}, {1, "cashews"}},
254 .y = {{-1, "roasted"}, {2, "&"}, {-3, "salted"}},
255 };
256 check_swap</*is_noexcept=*/true>(e);
257 }
258 {
259 auto const e = expected<std::string>{
260 .x = "hello there",
261 .y = "general kenobi",
262 };
263 check_swap</*is_noexcept=*/true>(e);
264 }
265 {
266 auto const e = expected<std::optional<lvalue_adl_swappable> >{
267 .x = {10},
268 .y = {20},
269 };
270 check_swap</*is_noexcept=*/true>(e);
271 }
272 {
273 auto const e = expected<std::optional<throwable_adl_swappable> >{
274 .x = {10},
275 .y = {20},
276 };
277 check_swap</*is_noexcept=*/false>(e);
278 }
279 {
280 auto const e = expected<std::unordered_map<int, std::string> >{
281 .x = {{0, "whole"}, {1, "cashews"}},
282 .y = {{-1, "roasted"}, {2, "&"}, {-3, "salted"}},
283 };
284 check_swap</*is_noexcept=*/true>(e);
285 }
286 {
287 auto const e = expected<std::vector<int> >{
288 .x = {0, 1, 2, 3, 4, 5},
289 .y = {6, 7, 8, 9},
290 };
291
292 check_swap</*is_noexcept=*/true>(e);
293 }
294 return 0;
295 }
296