xref: /llvm-project/libcxx/test/std/concepts/concepts.lang/concept.swappable/swappable.pass.cpp (revision cb71d77cc8cfb1dce7f0ae2ec51880f817d1a09b)
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> >
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>
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> >
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 
70 constexpr bool check_lvalue_adl_swappable() {
71   auto x = lvalue_adl_swappable(0);
72   auto y = lvalue_adl_swappable(1);
73   ASSERT_NOEXCEPT(std::ranges::swap(x, y));
74   assert(check_swap_21(x, y));
75   return true;
76 }
77 static_assert(check_lvalue_adl_swappable());
78 
79 constexpr bool check_rvalue_adl_swappable() {
80   ASSERT_NOEXCEPT(std::ranges::swap(rvalue_adl_swappable(0), rvalue_adl_swappable(1)));
81   assert(check_swap_21(rvalue_adl_swappable(0), rvalue_adl_swappable(1)));
82   return true;
83 }
84 static_assert(check_rvalue_adl_swappable());
85 
86 constexpr bool check_lvalue_rvalue_adl_swappable() {
87   auto x = lvalue_rvalue_adl_swappable(0);
88   ASSERT_NOEXCEPT(std::ranges::swap(x, lvalue_rvalue_adl_swappable(1)));
89   assert(check_swap_21(x, lvalue_rvalue_adl_swappable(1)));
90   return true;
91 }
92 static_assert(check_lvalue_rvalue_adl_swappable());
93 
94 constexpr bool check_rvalue_lvalue_adl_swappable() {
95   auto x = rvalue_lvalue_adl_swappable(0);
96   ASSERT_NOEXCEPT(std::ranges::swap(rvalue_lvalue_adl_swappable(1), x));
97   assert(check_swap_21(rvalue_lvalue_adl_swappable(1), x));
98   return true;
99 }
100 static_assert(check_rvalue_lvalue_adl_swappable());
101 
102 constexpr bool check_throwable_swappable() {
103   auto x = throwable_adl_swappable{0};
104   auto y = throwable_adl_swappable{1};
105   ASSERT_NOT_NOEXCEPT(std::ranges::swap(x, y));
106   assert(check_swap_21(x, y));
107   return true;
108 }
109 static_assert(check_throwable_swappable());
110 
111 constexpr bool check_non_move_constructible_adl_swappable() {
112   auto x = non_move_constructible_adl_swappable{0};
113   auto y = non_move_constructible_adl_swappable{1};
114   ASSERT_NOEXCEPT(std::ranges::swap(x, y));
115   assert(check_swap_21(x, y));
116   return true;
117 }
118 static_assert(check_non_move_constructible_adl_swappable());
119 
120 constexpr bool check_non_move_assignable_adl_swappable() {
121   auto x = non_move_assignable_adl_swappable{0};
122   auto y = non_move_assignable_adl_swappable{1};
123   ASSERT_NOEXCEPT(std::ranges::swap(x, y));
124   assert(check_swap_21(x, y));
125   return true;
126 }
127 static_assert(check_non_move_assignable_adl_swappable());
128 
129 namespace swappable_namespace {
130 enum unscoped { hello, world };
131 void swap(unscoped&, unscoped&);
132 
133 enum class scoped { hello, world };
134 void swap(scoped&, scoped&);
135 } // namespace swappable_namespace
136 
137 static_assert(std::swappable<swappable_namespace::unscoped>);
138 static_assert(std::swappable<swappable_namespace::scoped>);
139 
140 constexpr bool check_swap_arrays() {
141   int x[] = {0, 1, 2, 3, 4};
142   int y[] = {5, 6, 7, 8, 9};
143   ASSERT_NOEXCEPT(std::ranges::swap(x, y));
144   assert(check_swap_22(x, y));
145   return true;
146 }
147 static_assert(check_swap_arrays());
148 
149 constexpr bool check_lvalue_adl_swappable_arrays() {
150   lvalue_adl_swappable x[] = {{0}, {1}, {2}, {3}};
151   lvalue_adl_swappable y[] = {{4}, {5}, {6}, {7}};
152   ASSERT_NOEXCEPT(std::ranges::swap(x, y));
153   assert(check_swap_22(x, y));
154   return true;
155 }
156 static_assert(check_lvalue_adl_swappable_arrays());
157 
158 constexpr bool check_throwable_adl_swappable_arrays() {
159   throwable_adl_swappable x[] = {{0}, {1}, {2}, {3}};
160   throwable_adl_swappable y[] = {{4}, {5}, {6}, {7}};
161   ASSERT_NOT_NOEXCEPT(std::ranges::swap(x, y));
162   assert(check_swap_22(x, y));
163   return true;
164 }
165 static_assert(check_throwable_adl_swappable_arrays());
166 
167 auto global_x = 0;
168 ASSERT_NOEXCEPT(std::ranges::swap(global_x, global_x));
169 static_assert(check_swap_23(0, 0));
170 static_assert(check_swap_23(0, 1));
171 static_assert(check_swap_23(1, 0));
172 
173 constexpr bool check_swappable_references() {
174   int x = 42;
175   int y = 64;
176   ASSERT_NOEXCEPT(std::ranges::swap(x, y));
177   assert(check_swap_23(x, y));
178   return true;
179 }
180 static_assert(check_swappable_references());
181 
182 constexpr bool check_swappable_pointers() {
183   char const* x = "hello";
184   ASSERT_NOEXCEPT(std::ranges::swap(x, x));
185   assert(check_swap_23(x, {}));
186   return true;
187 }
188 static_assert(check_swappable_pointers());
189 
190 namespace union_swap {
191 union adl_swappable {
192   int x;
193   double y;
194 };
195 
196 void swap(adl_swappable&, adl_swappable&);
197 void swap(adl_swappable&&, adl_swappable&&);
198 } // namespace union_swap
199 static_assert(std::swappable<union_swap::adl_swappable>);
200 static_assert(std::swappable<union_swap::adl_swappable&>);
201 static_assert(std::swappable<union_swap::adl_swappable&&>);
202 
203 // All tests for std::swappable<T> are implicitly confirmed by `check_swap`, so we only need to
204 // sanity check for a few positive cases.
205 static_assert(std::swappable<int volatile&>);
206 static_assert(std::swappable<int&&>);
207 static_assert(std::swappable<int (*)()>);
208 static_assert(std::swappable<int rvalue_adl_swappable::*>);
209 static_assert(std::swappable<int (rvalue_adl_swappable::*)()>);
210 static_assert(std::swappable<std::unique_ptr<int> >);
211 
212 static_assert(!std::swappable<void>);
213 static_assert(!std::swappable<int const>);
214 static_assert(!std::swappable<int const&>);
215 static_assert(!std::swappable<int const&&>);
216 static_assert(!std::swappable<int const volatile>);
217 static_assert(!std::swappable<int const volatile&>);
218 static_assert(!std::swappable<int const volatile&&>);
219 static_assert(!std::swappable<int (&)()>);
220 static_assert(!std::swappable<DeletedMoveCtor>);
221 static_assert(!std::swappable<ImplicitlyDeletedMoveCtor>);
222 static_assert(!std::swappable<DeletedMoveAssign>);
223 static_assert(!std::swappable<ImplicitlyDeletedMoveAssign>);
224 static_assert(!std::swappable<NonMovable>);
225 static_assert(!std::swappable<DerivedFromNonMovable>);
226 static_assert(!std::swappable<HasANonMovable>);
227 
228 using swap_type = std::remove_const_t<decltype(std::ranges::swap)>;
229 static_assert(std::default_initializable<swap_type>);
230 static_assert(std::move_constructible<swap_type>);
231 static_assert(std::copy_constructible<swap_type>);
232 static_assert(std::assignable_from<swap_type&, swap_type>);
233 static_assert(std::assignable_from<swap_type&, swap_type&>);
234 static_assert(std::assignable_from<swap_type&, swap_type const&>);
235 static_assert(std::assignable_from<swap_type&, swap_type const>);
236 static_assert(std::swappable<swap_type>);
237 
238 enum class nothrow { no, yes };
239 
240 template <nothrow is_noexcept, std::swappable T>
241 void check_swap(expected<T> const& e) {
242   auto a = e.y;
243   auto b = e.x;
244 
245   std::ranges::swap(a, b);
246   assert(a == e.x);
247   assert(b == e.y);
248 
249   std::ranges::swap(a, b);
250   assert(a == e.y);
251   assert(b == e.x);
252 
253   static_assert(noexcept(std::ranges::swap(a, b)) == bool(is_noexcept));
254 }
255 
256 int main(int, char**) {
257   {
258     auto const e = expected<std::deque<int> >{
259         .x = {6, 7, 8, 9},
260         .y = {0, 1, 2, 3, 4, 5},
261     };
262     check_swap<nothrow::yes>(e);
263   }
264   {
265     auto const e = expected<std::map<int, std::string> >{
266         .x = {{0, "whole"}, {1, "cashews"}},
267         .y = {{-1, "roasted"}, {2, "&"}, {-3, "salted"}},
268     };
269     check_swap<nothrow::yes>(e);
270   }
271   {
272     auto const e = expected<std::string>{
273         .x = "hello there",
274         .y = "general kenobi",
275     };
276     check_swap<nothrow::yes>(e);
277   }
278   {
279     auto const e = expected<std::optional<lvalue_adl_swappable> >{
280         .x = {10},
281         .y = {20},
282     };
283     check_swap<nothrow::yes>(e);
284   }
285   {
286     auto const e = expected<std::optional<throwable_adl_swappable> >{
287         .x = {10},
288         .y = {20},
289     };
290     check_swap<nothrow::no>(e);
291   }
292   {
293     auto const e = expected<std::unordered_map<int, std::string> >{
294         .x = {{0, "whole"}, {1, "cashews"}},
295         .y = {{-1, "roasted"}, {2, "&"}, {-3, "salted"}},
296     };
297     check_swap<nothrow::yes>(e);
298   }
299   {
300     auto const e = expected<std::vector<int> >{
301         .x = {0, 1, 2, 3, 4, 5},
302         .y = {6, 7, 8, 9},
303     };
304 
305     check_swap<nothrow::yes>(e);
306   }
307   return 0;
308 }
309