//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // UNSUPPORTED: libcpp-no-concepts // template // concept swappable = // see below #include #include #include #include #include #include #include #include #include #include #include "test_macros.h" #include "type_classification/moveconstructible.h" #include "type_classification/swappable.h" template struct expected { T x; T y; }; // clang-format off // Checks [concept.swappable]/2.1 template requires std::same_as, std::remove_cvref_t > && std::swappable > constexpr bool check_swap_21(T&& x, U&& y) { expected > const e{y, x}; std::ranges::swap(std::forward(x), std::forward(y)); return x == e.x && y == e.y; } // Checks [concept.swappable]/2.2 template constexpr bool check_swap_22(T (&x)[N], T (&y)[N]) { expected e; std::copy(y, y + N, e.x); std::copy(x, x + N, e.y); std::ranges::swap(x, y); return std::equal(x, x + N, e.x, e.x + N) && std::equal(y, y + N, e.y, e.y + N); } // Checks [concept.swappable]/2.3 template requires std::copy_constructible > constexpr bool check_swap_23(T x, T y) { expected > const e{y, x}; std::ranges::swap(x, y); return x == e.x && y == e.y; } // clang-format on constexpr bool check_lvalue_adl_swappable() { auto x = lvalue_adl_swappable(0); auto y = lvalue_adl_swappable(1); constexpr auto is_noexcept = noexcept(std::ranges::swap(x, y)); return check_swap_21(x, y) && is_noexcept; } static_assert(check_lvalue_adl_swappable()); constexpr bool check_rvalue_adl_swappable() { constexpr auto is_noexcept = noexcept( std::ranges::swap(rvalue_adl_swappable(0), rvalue_adl_swappable(1))); return check_swap_21(rvalue_adl_swappable(0), rvalue_adl_swappable(1)) && is_noexcept; } static_assert(check_rvalue_adl_swappable()); constexpr bool check_lvalue_rvalue_adl_swappable() { auto x = lvalue_rvalue_adl_swappable(0); constexpr auto is_noexcept = noexcept(std::ranges::swap(x, lvalue_rvalue_adl_swappable(1))); return check_swap_21(x, lvalue_rvalue_adl_swappable(1)) && is_noexcept; } static_assert(check_lvalue_rvalue_adl_swappable()); constexpr bool check_rvalue_lvalue_adl_swappable() { auto x = rvalue_lvalue_adl_swappable(0); constexpr auto is_noexcept = noexcept(std::ranges::swap(rvalue_lvalue_adl_swappable(1), x)); return check_swap_21(rvalue_lvalue_adl_swappable(1), x) && is_noexcept; } static_assert(check_rvalue_lvalue_adl_swappable()); constexpr bool check_throwable_swappable() { auto x = throwable_adl_swappable{0}; auto y = throwable_adl_swappable{1}; constexpr auto not_noexcept = !noexcept(std::ranges::swap(x, y)); return check_swap_21(x, y) && not_noexcept; } static_assert(check_throwable_swappable()); constexpr bool check_non_move_constructible_adl_swappable() { auto x = non_move_constructible_adl_swappable{0}; auto y = non_move_constructible_adl_swappable{1}; constexpr auto is_noexcept = noexcept(std::ranges::swap(x, y)); return check_swap_21(x, y) && is_noexcept; } static_assert(check_non_move_constructible_adl_swappable()); constexpr bool check_non_move_assignable_adl_swappable() { auto x = non_move_assignable_adl_swappable{0}; auto y = non_move_assignable_adl_swappable{1}; return check_swap_21(x, y) && noexcept(std::ranges::swap(x, y)); } static_assert(check_non_move_assignable_adl_swappable()); namespace swappable_namespace { enum unscoped { hello, world }; void swap(unscoped&, unscoped&); enum class scoped { hello, world }; void swap(scoped&, scoped&); } // namespace swappable_namespace static_assert(std::swappable); static_assert(std::swappable); constexpr bool check_swap_arrays() { int x[] = {0, 1, 2, 3, 4}; int y[] = {5, 6, 7, 8, 9}; return check_swap_22(x, y) && noexcept(std::ranges::swap(x, y)); } static_assert(check_swap_arrays()); constexpr bool check_lvalue_adl_swappable_arrays() { lvalue_adl_swappable x[] = {{0}, {1}, {2}, {3}}; lvalue_adl_swappable y[] = {{4}, {5}, {6}, {7}}; return check_swap_22(x, y) && noexcept(std::ranges::swap(x, y)); } static_assert(check_lvalue_adl_swappable_arrays()); constexpr bool check_throwable_adl_swappable_arrays() { throwable_adl_swappable x[] = {{0}, {1}, {2}, {3}}; throwable_adl_swappable y[] = {{4}, {5}, {6}, {7}}; return check_swap_22(x, y) && !noexcept(std::ranges::swap(x, y)); } static_assert(check_throwable_adl_swappable_arrays()); inline auto global_x = 0; static_assert(check_swap_23(0, 0) && noexcept(std::ranges::swap(global_x, global_x))); static_assert(check_swap_23(0, 1) && noexcept(std::ranges::swap(global_x, global_x))); static_assert(check_swap_23(1, 0) && noexcept(std::ranges::swap(global_x, global_x))); constexpr bool check_swappable_references() { int x = 42; int y = 64; return check_swap_23(x, y) && noexcept(std::ranges::swap(x, y)); } static_assert(check_swappable_references()); constexpr bool check_swappable_pointers() { char const* x = "hello"; return check_swap_23(x, nullptr) && noexcept(std::ranges::swap(x, x)); } static_assert(check_swappable_pointers()); namespace union_swap { union adl_swappable { int x; double y; }; void swap(adl_swappable&, adl_swappable&); void swap(adl_swappable&&, adl_swappable&&); }; // namespace union_swap static_assert(std::swappable); static_assert(std::swappable); static_assert(std::swappable); // All tests for std::swappable are implicitly confirmed by `check_swap`, so we only need to // sanity check for a few positive cases. static_assert(std::swappable); static_assert(std::swappable); static_assert(std::swappable); static_assert(std::swappable); static_assert(std::swappable); static_assert(std::swappable >); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); static_assert(!std::swappable); using swap_type = std::remove_const_t; static_assert(std::default_initializable); static_assert(std::move_constructible); static_assert(std::copy_constructible); static_assert(std::assignable_from); static_assert(std::assignable_from); static_assert(std::assignable_from); static_assert(std::assignable_from); static_assert(std::swappable); template void check_swap(expected const& e) { auto a = e.y; auto b = e.x; std::ranges::swap(a, b); assert(a == e.x); assert(b == e.y); std::ranges::swap(a, b); assert(a == e.y); assert(b == e.x); static_assert(noexcept(std::ranges::swap(a, b)) == is_noexcept); } int main(int, char**) { { auto const e = expected >{ .x = {6, 7, 8, 9}, .y = {0, 1, 2, 3, 4, 5}, }; check_swap(e); } { auto const e = expected >{ .x = {{0, "whole"}, {1, "cashews"}}, .y = {{-1, "roasted"}, {2, "&"}, {-3, "salted"}}, }; check_swap(e); } { auto const e = expected{ .x = "hello there", .y = "general kenobi", }; check_swap(e); } { auto const e = expected >{ .x = {10}, .y = {20}, }; check_swap(e); } { auto const e = expected >{ .x = {10}, .y = {20}, }; check_swap(e); } { auto const e = expected >{ .x = {{0, "whole"}, {1, "cashews"}}, .y = {{-1, "roasted"}, {2, "&"}, {-3, "salted"}}, }; check_swap(e); } { auto const e = expected >{ .x = {0, 1, 2, 3, 4, 5}, .y = {6, 7, 8, 9}, }; check_swap(e); } return 0; }