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