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