1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
12 // concept swappable = // see below
23 #include <unordered_map>
26 #include "test_macros.h"
27 #include "type_classification/moveconstructible.h"
28 #include "type_classification/swappable.h"
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
> >
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
;
47 // Checks [concept.swappable]/2.2
48 template <std::swappable T
, std::size_t N
>
49 constexpr bool check_swap_22(T (&x
)[N
], T (&y
)[N
]) {
51 std::copy(y
, y
+ N
, e
.x
);
52 std::copy(x
, x
+ N
, e
.y
);
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
);
59 // Checks [concept.swappable]/2.3
60 template <std::swappable T
>
61 requires
std::copy_constructible
<std::remove_cvref_t
<T
> >
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
;
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
));
76 static_assert(check_lvalue_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)));
83 static_assert(check_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)));
91 static_assert(check_lvalue_rvalue_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
));
99 static_assert(check_rvalue_lvalue_adl_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
));
108 static_assert(check_throwable_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
));
117 static_assert(check_non_move_constructible_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
));
126 static_assert(check_non_move_assignable_adl_swappable());
128 namespace swappable_namespace
{
129 enum unscoped
{ hello
, world
};
130 void swap(unscoped
&, unscoped
&);
132 enum class scoped
{ hello
, world
};
133 void swap(scoped
&, scoped
&);
134 } // namespace swappable_namespace
136 static_assert(std::swappable
<swappable_namespace::unscoped
>);
137 static_assert(std::swappable
<swappable_namespace::scoped
>);
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
));
146 static_assert(check_swap_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
));
155 static_assert(check_lvalue_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
));
164 static_assert(check_throwable_adl_swappable_arrays());
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));
172 constexpr bool check_swappable_references() {
175 ASSERT_NOEXCEPT(std::ranges::swap(x
, y
));
176 assert(check_swap_23(x
, y
));
179 static_assert(check_swappable_references());
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
, {}));
187 static_assert(check_swappable_pointers());
189 namespace union_swap
{
190 union adl_swappable
{
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
&&>);
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> >);
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
>);
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
>);
237 enum class nothrow
{ no
, yes
};
239 template <nothrow is_noexcept
, std::swappable T
>
240 void check_swap(expected
<T
> const& e
) {
244 std::ranges::swap(a
, b
);
248 std::ranges::swap(a
, b
);
252 static_assert(noexcept(std::ranges::swap(a
, b
)) == bool(is_noexcept
));
255 int main(int, char**) {
257 auto const e
= expected
<std::deque
<int> >{
259 .y
= {0, 1, 2, 3, 4, 5},
261 check_swap
<nothrow::yes
>(e
);
264 auto const e
= expected
<std::map
<int, std::string
> >{
265 .x
= {{0, "whole"}, {1, "cashews"}},
266 .y
= {{-1, "roasted"}, {2, "&"}, {-3, "salted"}},
268 check_swap
<nothrow::yes
>(e
);
271 auto const e
= expected
<std::string
>{
273 .y
= "general kenobi",
275 check_swap
<nothrow::yes
>(e
);
278 auto const e
= expected
<std::optional
<lvalue_adl_swappable
> >{
282 check_swap
<nothrow::yes
>(e
);
285 auto const e
= expected
<std::optional
<throwable_adl_swappable
> >{
289 check_swap
<nothrow::no
>(e
);
292 auto const e
= expected
<std::unordered_map
<int, std::string
> >{
293 .x
= {{0, "whole"}, {1, "cashews"}},
294 .y
= {{-1, "roasted"}, {2, "&"}, {-3, "salted"}},
296 check_swap
<nothrow::yes
>(e
);
299 auto const e
= expected
<std::vector
<int> >{
300 .x
= {0, 1, 2, 3, 4, 5},
304 check_swap
<nothrow::yes
>(e
);