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> > 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> 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> > 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 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 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 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 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 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 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 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 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 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 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 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 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> 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 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