diff --git a/libcxx/include/concepts b/libcxx/include/concepts --- a/libcxx/include/concepts +++ b/libcxx/include/concepts @@ -243,6 +243,88 @@ concept move_constructible = constructible_from<_Tp, _Tp> && convertible_to<_Tp, _Tp>; +// [concept.swappable] +namespace ranges::__swap { + // Deleted to inhibit ADL + template + void swap(_Tp&, _Tp&) = delete; + + template + concept __class_or_enum = is_class_v<_Tp> || is_enum_v<_Tp>; + + // [1] + template + concept __unqualified_swappable_with = + (__class_or_enum> || __class_or_enum>) && + requires(_Tp&& __t, _Up&& __u) { + swap(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)); + }; + + struct __fn; + + template + concept __swappable_arrays = + !__unqualified_swappable_with<_Tp(&)[_Size], _Up(&)[_Size]> && + extent_v<_Tp> == extent_v<_Up> && + requires(_Tp(& __t)[_Size], _Up(& __u)[_Size], const __fn& __swap) { + __swap(__t[0], __u[0]); + }; + + template + concept __exchangeable = + !__unqualified_swappable_with<_Tp&, _Tp&> && + move_constructible<_Tp> && + assignable_from<_Tp&, _Tp>; + + struct __fn { + // 2.1 `S` is `(void)swap(E1, E2)`* if `E1` or `E2` has class or enumeration type and... + // *The name `swap` is used here unqualified. + template + requires __unqualified_swappable_with<_Tp, _Up> + constexpr void operator()(_Tp&& __t, _Up&& __u) const + noexcept(noexcept(swap(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)))) + { + swap(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)); + } + + // 2.2 Otherwise, if `E1` and `E2` are lvalues of array types with equal extent and... + template + requires __swappable_arrays<_Tp, _Up, _Size> + constexpr void operator()(_Tp(& __t)[_Size], _Up(& __u)[_Size]) const + noexcept(noexcept((*this)(*__t, *__u))) + { + for (size_t __i = 0; __i < _Size; ++__i) { + (*this)(__t[__i], __u[__i]); + } + } + + // 2.3 Otherwise, if `E1` and `E2` are lvalues of the same type `T` that models... + template<__exchangeable _Tp> + constexpr void operator()(_Tp& __x, _Tp& __y) const + noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_assignable_v<_Tp>) + { + __y = _VSTD::exchange(__x, std::move(__y)); + } + }; +} + +namespace ranges::inline __cpo { + inline constexpr auto swap = __swap::__fn{}; +} + +template +concept swappable = requires(_Tp& __a, _Tp& __b) { ranges::swap(__a, __b); }; + +template +concept swappable_with = + common_reference_with<_Tp&, _Up&> && + requires(_Tp&& __t, _Up&& __u) { + ranges::swap(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Tp>(__t)); + ranges::swap(_VSTD::forward<_Up>(__u), _VSTD::forward<_Up>(__u)); + ranges::swap(_VSTD::forward<_Tp>(__t), _VSTD::forward<_Up>(__u)); + ranges::swap(_VSTD::forward<_Up>(__u), _VSTD::forward<_Tp>(__t)); + }; + // [concept.copyconstructible] template concept copy_constructible = diff --git a/libcxx/test/std/concepts/lang/swappable.h b/libcxx/test/std/concepts/lang/swappable.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/concepts/lang/swappable.h @@ -0,0 +1,266 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef TEST_STD_CONCEPTS_LANG_SWAPPABLE_H +#define TEST_STD_CONCEPTS_LANG_SWAPPABLE_H + +#include + +// `adl_swappable` indicates it's been swapped using ADL by maintaining a pointer to itself that +// isn't a part of the exchange. This is well-formed since we say that two `adl_swappable` objects +// are equal only if their respective `value` subobjects are equal and their respective `this` +// subobjects store the addresses of those respective `adl_swappable` objects. +class lvalue_adl_swappable { +public: + lvalue_adl_swappable() = default; + + constexpr lvalue_adl_swappable(int value) noexcept : value_(value) {} + + constexpr lvalue_adl_swappable(lvalue_adl_swappable&& other) noexcept + : value_(std::move(other.value_)), + this_(this) {} + + constexpr lvalue_adl_swappable(lvalue_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr lvalue_adl_swappable& + operator=(lvalue_adl_swappable other) noexcept { + value_ = other.value_; + return *this; + } + + constexpr friend void swap(lvalue_adl_swappable& x, + lvalue_adl_swappable& y) noexcept { + std::ranges::swap(x.value_, y.value_); + } + + constexpr bool operator==(lvalue_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + lvalue_adl_swappable* this_ = this; +}; + +class lvalue_rvalue_adl_swappable { +public: + lvalue_rvalue_adl_swappable() = default; + + constexpr lvalue_rvalue_adl_swappable(int value) noexcept : value_(value) {} + + constexpr + lvalue_rvalue_adl_swappable(lvalue_rvalue_adl_swappable&& other) noexcept + : value_(std::move(other.value_)), + this_(this) {} + + constexpr + lvalue_rvalue_adl_swappable(lvalue_rvalue_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr lvalue_rvalue_adl_swappable& + operator=(lvalue_rvalue_adl_swappable other) noexcept { + value_ = other.value_; + return *this; + } + + constexpr friend void swap(lvalue_rvalue_adl_swappable& x, + lvalue_rvalue_adl_swappable&& y) noexcept { + std::ranges::swap(x.value_, y.value_); + } + + constexpr bool + operator==(lvalue_rvalue_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + lvalue_rvalue_adl_swappable* this_ = this; +}; + +class rvalue_lvalue_adl_swappable { +public: + rvalue_lvalue_adl_swappable() = default; + + constexpr rvalue_lvalue_adl_swappable(int value) noexcept : value_(value) {} + + constexpr + rvalue_lvalue_adl_swappable(rvalue_lvalue_adl_swappable&& other) noexcept + : value_(std::move(other.value_)), + this_(this) {} + + constexpr + rvalue_lvalue_adl_swappable(rvalue_lvalue_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr rvalue_lvalue_adl_swappable& + operator=(rvalue_lvalue_adl_swappable other) noexcept { + value_ = other.value_; + return *this; + } + + constexpr friend void swap(rvalue_lvalue_adl_swappable&& x, + rvalue_lvalue_adl_swappable& y) noexcept { + std::ranges::swap(x.value_, y.value_); + } + + constexpr bool + operator==(rvalue_lvalue_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + rvalue_lvalue_adl_swappable* this_ = this; +}; + +class rvalue_adl_swappable { +public: + rvalue_adl_swappable() = default; + + constexpr rvalue_adl_swappable(int value) noexcept : value_(value) {} + + constexpr rvalue_adl_swappable(rvalue_adl_swappable&& other) noexcept + : value_(std::move(other.value_)), + this_(this) {} + + constexpr rvalue_adl_swappable(rvalue_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr rvalue_adl_swappable& + operator=(rvalue_adl_swappable other) noexcept { + value_ = other.value_; + return *this; + } + + constexpr friend void swap(rvalue_adl_swappable&& x, + rvalue_adl_swappable&& y) noexcept { + std::ranges::swap(x.value_, y.value_); + } + + constexpr bool operator==(rvalue_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + rvalue_adl_swappable* this_ = this; +}; + +class non_move_constructible_adl_swappable { +public: + non_move_constructible_adl_swappable() = default; + + constexpr non_move_constructible_adl_swappable(int value) noexcept + : value_(value) {} + + constexpr non_move_constructible_adl_swappable( + non_move_constructible_adl_swappable&& other) noexcept + : value_(std::move(other.value_)), + this_(this) {} + + constexpr non_move_constructible_adl_swappable( + non_move_constructible_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr non_move_constructible_adl_swappable& + operator=(non_move_constructible_adl_swappable other) noexcept { + value_ = other.value_; + return *this; + } + + constexpr friend void swap(non_move_constructible_adl_swappable& x, + non_move_constructible_adl_swappable& y) noexcept { + std::ranges::swap(x.value_, y.value_); + } + + constexpr bool + operator==(non_move_constructible_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + non_move_constructible_adl_swappable* this_ = this; +}; + +class non_move_assignable_adl_swappable { +public: + non_move_assignable_adl_swappable() = default; + + constexpr non_move_assignable_adl_swappable(int value) noexcept + : value_(value) {} + + non_move_assignable_adl_swappable(non_move_assignable_adl_swappable&& other) = + delete; + + constexpr non_move_assignable_adl_swappable( + non_move_assignable_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr non_move_assignable_adl_swappable& + operator=(non_move_assignable_adl_swappable&& other) noexcept = delete; + + constexpr friend void swap(non_move_assignable_adl_swappable& x, + non_move_assignable_adl_swappable& y) noexcept { + std::ranges::swap(x.value_, y.value_); + } + + constexpr bool + operator==(non_move_assignable_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + non_move_assignable_adl_swappable* this_ = this; +}; + +class throwable_adl_swappable { +public: + throwable_adl_swappable() = default; + + constexpr throwable_adl_swappable(int value) noexcept : value_(value) {} + + constexpr throwable_adl_swappable(throwable_adl_swappable&& other) noexcept + : value_(std::move(other.value_)), + this_(this) {} + + constexpr + throwable_adl_swappable(throwable_adl_swappable const& other) noexcept + : value_(other.value_), + this_(this) {} + + constexpr throwable_adl_swappable& + operator=(throwable_adl_swappable other) noexcept { + value_ = other.value_; + return *this; + } + + constexpr friend void swap(throwable_adl_swappable& X, + throwable_adl_swappable& Y) noexcept(false) { + std::ranges::swap(X.value_, Y.value_); + } + + constexpr bool + operator==(throwable_adl_swappable const& other) const noexcept { + return value_ == other.value_ && this_ == this && other.this_ == &other; + } + +private: + int value_{}; + throwable_adl_swappable* this_ = this; +}; + +#endif // TEST_STD_CONCEPTS_LANG_SWAPPABLE_H diff --git a/libcxx/test/std/concepts/lang/swappable.pass.cpp b/libcxx/test/std/concepts/lang/swappable.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/concepts/lang/swappable.pass.cpp @@ -0,0 +1,281 @@ +//===----------------------------------------------------------------------===// +// +// 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 "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()); + +// 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); + +struct non_move_constructible { + non_move_constructible(non_move_constructible&&) = delete; + non_move_constructible& operator=(non_move_constructible&&); +}; +static_assert(!std::move_constructible); +static_assert( + std::assignable_from); +static_assert(!std::swappable); + +struct non_move_assignable { + non_move_assignable(non_move_assignable&&); + non_move_assignable& operator=(non_move_assignable&&) = delete; +}; +static_assert(std::move_constructible); +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; +} diff --git a/libcxx/test/std/concepts/lang/swappable_with.compile.pass.cpp b/libcxx/test/std/concepts/lang/swappable_with.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/concepts/lang/swappable_with.compile.pass.cpp @@ -0,0 +1,613 @@ +//===----------------------------------------------------------------------===// +// +// 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_with = // see below + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swappable.h" + +template +constexpr bool check_swappable_with_impl() { + static_assert(std::swappable_with == std::swappable_with); + return std::swappable_with; +} + +template +constexpr bool check_swappable_with() { + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert( + !check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert( + !check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert( + !check_swappable_with_impl()); + + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert(!check_swappable_with_impl()); + static_assert( + !check_swappable_with_impl()); + return check_swappable_with_impl(); +} + +template +constexpr bool check_swappable_with_including_lvalue_ref_to_volatile() { + constexpr auto result = check_swappable_with(); + static_assert(check_swappable_with_impl() == + result); + return result; +} + +namespace fundamental { +static_assert( + check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert( + check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert( + !check_swappable_with_including_lvalue_ref_to_volatile()); + +static_assert( + check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert( + !check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (*)(), int (*)()>()); +static_assert( + !check_swappable_with_including_lvalue_ref_to_volatile()); + +struct S {}; +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int S::*, int S::*>()); +static_assert( + !check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)(), int (S::*)()>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int, int (S::*)()>()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() noexcept, int (S::*)() noexcept>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() noexcept, int (S::*)()>()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() const, int (S::*)() const>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() const, int (S::*)()>()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() const noexcept, int (S::*)() const noexcept>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() const, int (S::*)() const noexcept>()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() volatile, int (S::*)() volatile>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() volatile, int (S::*)()>()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() const volatile, int (S::*)() const volatile>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int (S::*)() const volatile, int (S::*)()>()); + +static_assert( + check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert( + !check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5], double[5]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5], double[6]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], double[5]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], double[6]>()); +static_assert(check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], int[5][6]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], int[5][4]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], int[6][5]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], double[5][6]>()); +static_assert(!check_swappable_with_including_lvalue_ref_to_volatile< + int[5][6], double[6][5]>()); + +// always false +static_assert(!check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); +} // namespace fundamental + +namespace adl { +static_assert( + check_swappable_with()); +static_assert(check_swappable_with()); +static_assert(check_swappable_with()); +static_assert( + check_swappable_with_impl()); +static_assert(!check_swappable_with_impl()); + +struct s1 {}; +struct no_common_reference_with_s1 { + friend void swap(s1&, no_common_reference_with_s1&); + friend void swap(no_common_reference_with_s1&, s1&); +}; +static_assert(!check_swappable_with()); + +struct one_way_swappable_with_s1 { + friend void swap(s1&, one_way_swappable_with_s1&); + operator s1(); +}; +static_assert(std::common_reference_with); +static_assert(!check_swappable_with()); + +struct one_way_swappable_with_s1_other_way { + friend void swap(one_way_swappable_with_s1_other_way&, s1&); + operator s1(); +}; +static_assert( + std::common_reference_with); +static_assert(!check_swappable_with()); + +struct can_swap_with_s1_but_not_swappable { + can_swap_with_s1_but_not_swappable(can_swap_with_s1_but_not_swappable&&) = + delete; + friend void swap(s1&, can_swap_with_s1_but_not_swappable&); + friend void swap(can_swap_with_s1_but_not_swappable&, s1&); + + operator s1() const; +}; +static_assert( + std::common_reference_with); +static_assert(!std::swappable); +static_assert( + !check_swappable_with()); + +struct swappable_with_s1 { + friend void swap(s1&, swappable_with_s1&); + friend void swap(swappable_with_s1&, s1&); + operator s1() const; +}; +static_assert(check_swappable_with()); + +struct swappable_with_const_s1_but_not_swappable { + swappable_with_const_s1_but_not_swappable( + swappable_with_const_s1_but_not_swappable const&); + swappable_with_const_s1_but_not_swappable( + swappable_with_const_s1_but_not_swappable const&&); + swappable_with_const_s1_but_not_swappable& + operator=(swappable_with_const_s1_but_not_swappable const&); + swappable_with_const_s1_but_not_swappable& + operator=(swappable_with_const_s1_but_not_swappable const&&); + + friend void swap(s1 const&, swappable_with_const_s1_but_not_swappable const&); + friend void swap(swappable_with_const_s1_but_not_swappable const&, s1 const&); + + operator s1 const &() const; +}; +static_assert( + !std::swappable); +static_assert(!std::swappable_with< + swappable_with_const_s1_but_not_swappable const&, s1 const&>); + +struct swappable_with_volatile_s1_but_not_swappable { + swappable_with_volatile_s1_but_not_swappable( + swappable_with_volatile_s1_but_not_swappable volatile&); + swappable_with_volatile_s1_but_not_swappable( + swappable_with_volatile_s1_but_not_swappable volatile&&); + swappable_with_volatile_s1_but_not_swappable& + operator=(swappable_with_volatile_s1_but_not_swappable volatile&); + swappable_with_volatile_s1_but_not_swappable& + operator=(swappable_with_volatile_s1_but_not_swappable volatile&&); + + friend void swap(s1 volatile&, + swappable_with_volatile_s1_but_not_swappable volatile&); + friend void swap(swappable_with_volatile_s1_but_not_swappable volatile&, + s1 volatile&); + + operator s1 volatile &() volatile; +}; +static_assert( + !std::swappable); +static_assert( + !std::swappable_with); + +struct swappable_with_cv_s1_but_not_swappable { + swappable_with_cv_s1_but_not_swappable( + swappable_with_cv_s1_but_not_swappable const volatile&); + swappable_with_cv_s1_but_not_swappable( + swappable_with_cv_s1_but_not_swappable const volatile&&); + swappable_with_cv_s1_but_not_swappable& + operator=(swappable_with_cv_s1_but_not_swappable const volatile&); + swappable_with_cv_s1_but_not_swappable& + operator=(swappable_with_cv_s1_but_not_swappable const volatile&&); + + friend void swap(s1 const volatile&, + swappable_with_cv_s1_but_not_swappable const volatile&); + friend void swap(swappable_with_cv_s1_but_not_swappable const volatile&, + s1 const volatile&); + + operator s1 const volatile &() const volatile; +}; +static_assert( + !std::swappable); +static_assert( + !std::swappable_with); + +struct s2 { + friend void swap(s2 const&, s2 const&); + friend void swap(s2 volatile&, s2 volatile&); + friend void swap(s2 const volatile&, s2 const volatile&); +}; + +struct swappable_with_const_s2 { + swappable_with_const_s2(swappable_with_const_s2 const&); + swappable_with_const_s2(swappable_with_const_s2 const&&); + swappable_with_const_s2& operator=(swappable_with_const_s2 const&); + swappable_with_const_s2& operator=(swappable_with_const_s2 const&&); + + friend void swap(swappable_with_const_s2 const&, + swappable_with_const_s2 const&); + friend void swap(s2 const&, swappable_with_const_s2 const&); + friend void swap(swappable_with_const_s2 const&, s2 const&); + + operator s2 const &() const; +}; +static_assert(std::swappable_with); + +struct swappable_with_volatile_s2 { + swappable_with_volatile_s2(swappable_with_volatile_s2 volatile&); + swappable_with_volatile_s2(swappable_with_volatile_s2 volatile&&); + swappable_with_volatile_s2& operator=(swappable_with_volatile_s2 volatile&); + swappable_with_volatile_s2& operator=(swappable_with_volatile_s2 volatile&&); + + friend void swap(swappable_with_volatile_s2 volatile&, + swappable_with_volatile_s2 volatile&); + friend void swap(s2 volatile&, swappable_with_volatile_s2 volatile&); + friend void swap(swappable_with_volatile_s2 volatile&, s2 volatile&); + + operator s2 volatile &() volatile; +}; +static_assert( + std::swappable_with); + +struct swappable_with_cv_s2 { + swappable_with_cv_s2(swappable_with_cv_s2 const volatile&); + swappable_with_cv_s2(swappable_with_cv_s2 const volatile&&); + swappable_with_cv_s2& operator=(swappable_with_cv_s2 const volatile&); + swappable_with_cv_s2& operator=(swappable_with_cv_s2 const volatile&&); + + friend void swap(swappable_with_cv_s2 const volatile&, + swappable_with_cv_s2 const volatile&); + friend void swap(s2 const volatile&, swappable_with_cv_s2 const volatile&); + friend void swap(swappable_with_cv_s2 const volatile&, s2 const volatile&); + + operator s2 const volatile &() const volatile; +}; +static_assert(std::swappable_with); + +struct swappable_with_rvalue_ref_to_s1_but_not_swappable { + friend void swap(swappable_with_rvalue_ref_to_s1_but_not_swappable&&, + swappable_with_rvalue_ref_to_s1_but_not_swappable&&); + friend void swap(s1&&, swappable_with_rvalue_ref_to_s1_but_not_swappable&&); + friend void swap(swappable_with_rvalue_ref_to_s1_but_not_swappable&&, s1&&); + + operator s1() const; +}; +static_assert( + !std::swappable); +static_assert( + !std::swappable_with< + swappable_with_rvalue_ref_to_s1_but_not_swappable const&&, s1 const&&>); + +struct swappable_with_rvalue_ref_to_const_s1_but_not_swappable { + friend void + swap(s1 const&&, + swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&); + friend void + swap(swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&, + s1 const&&); + + operator s1 const() const; +}; +static_assert(!std::swappable< + swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&>); +static_assert(!std::swappable_with< + swappable_with_rvalue_ref_to_const_s1_but_not_swappable const&&, + s1 const&&>); + +struct swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable { + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable( + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&); + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable( + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&); + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable& operator=( + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&); + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable& operator=( + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&); + + friend void + swap(s1 volatile&&, + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&); + friend void + swap(swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&, + s1 volatile&&); + + operator s1 volatile &&() volatile&&; +}; +static_assert( + !std::swappable< + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&>); +static_assert( + !std::swappable_with< + swappable_with_rvalue_ref_to_volatile_s1_but_not_swappable volatile&&, + s1 volatile&&>); + +struct swappable_with_rvalue_ref_to_cv_s1_but_not_swappable { + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable( + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&); + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable( + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&); + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable& operator=( + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&); + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable& operator=( + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&); + + friend void + swap(s1 const volatile&&, + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&); + friend void + swap(swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&, + s1 const volatile&&); + + operator s1 const volatile &&() const volatile&&; +}; +static_assert( + !std::swappable< + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&>); +static_assert( + !std::swappable_with< + swappable_with_rvalue_ref_to_cv_s1_but_not_swappable const volatile&&, + s1 const volatile&&>); + +struct s3 { + friend void swap(s3&&, s3&&); + friend void swap(s3 const&&, s3 const&&); + friend void swap(s3 volatile&&, s3 volatile&&); + friend void swap(s3 const volatile&&, s3 const volatile&&); +}; + +struct swappable_with_rvalue_ref_to_s3 { + friend void swap(swappable_with_rvalue_ref_to_s3&&, + swappable_with_rvalue_ref_to_s3&&); + friend void swap(s3&&, swappable_with_rvalue_ref_to_s3&&); + friend void swap(swappable_with_rvalue_ref_to_s3&&, s3&&); + + operator s3() const; +}; +static_assert(std::swappable_with); + +struct swappable_with_rvalue_ref_to_const_s3 { + swappable_with_rvalue_ref_to_const_s3( + swappable_with_rvalue_ref_to_const_s3 const&); + swappable_with_rvalue_ref_to_const_s3( + swappable_with_rvalue_ref_to_const_s3 const&&); + swappable_with_rvalue_ref_to_const_s3& + operator=(swappable_with_rvalue_ref_to_const_s3 const&); + swappable_with_rvalue_ref_to_const_s3& + operator=(swappable_with_rvalue_ref_to_const_s3 const&&); + + friend void swap(swappable_with_rvalue_ref_to_const_s3 const&&, + swappable_with_rvalue_ref_to_const_s3 const&&); + friend void swap(s3 const&&, swappable_with_rvalue_ref_to_const_s3 const&&); + friend void swap(swappable_with_rvalue_ref_to_const_s3 const&&, s3 const&&); + + operator s3() const; +}; +static_assert(std::swappable_with); + +struct swappable_with_rvalue_ref_to_volatile_s3 { + swappable_with_rvalue_ref_to_volatile_s3( + swappable_with_rvalue_ref_to_volatile_s3 volatile&); + swappable_with_rvalue_ref_to_volatile_s3( + swappable_with_rvalue_ref_to_volatile_s3 volatile&&); + swappable_with_rvalue_ref_to_volatile_s3& + operator=(swappable_with_rvalue_ref_to_volatile_s3 volatile&); + swappable_with_rvalue_ref_to_volatile_s3& + operator=(swappable_with_rvalue_ref_to_volatile_s3 volatile&&); + + friend void swap(swappable_with_rvalue_ref_to_volatile_s3 volatile&&, + swappable_with_rvalue_ref_to_volatile_s3 volatile&&); + friend void swap(s3 volatile&&, + swappable_with_rvalue_ref_to_volatile_s3 volatile&&); + friend void swap(swappable_with_rvalue_ref_to_volatile_s3 volatile&&, + s3 volatile&&); + + operator s3 volatile &() volatile; +}; +static_assert( + std::swappable_with); + +// struct swappable_with_cv_s2 { +// swappable_with_cv_s2(swappable_with_cv_s2 const volatile&); +// swappable_with_cv_s2(swappable_with_cv_s2 const volatile&&); +// swappable_with_cv_s2& operator=(swappable_with_cv_s2 const volatile&); +// swappable_with_cv_s2& operator=(swappable_with_cv_s2 const volatile&&); + +// friend void swap(swappable_with_cv_s2 const volatile&, swappable_with_cv_s2 const volatile&); +// friend void swap(s2 const volatile&, swappable_with_cv_s2 const volatile&); +// friend void swap(swappable_with_cv_s2 const volatile&, s2 const volatile&); + +// operator s2 const volatile&() const volatile; +// }; +// static_assert(std::swappable_with); +} // namespace adl + +namespace standard_types { +static_assert( + check_swappable_with, std::array >()); +static_assert( + !check_swappable_with, std::array >()); +static_assert(check_swappable_with, std::deque >()); +static_assert(!check_swappable_with, std::vector >()); +static_assert( + check_swappable_with, std::forward_list >()); +static_assert( + !check_swappable_with, std::vector >()); +static_assert(check_swappable_with, std::list >()); +static_assert(!check_swappable_with, std::vector >()); + +#ifndef _LIBCPP_HAS_NO_THREADS +static_assert(!check_swappable_with, + std::lock_guard >()); +static_assert( + !check_swappable_with, std::vector >()); +static_assert(!check_swappable_with()); +static_assert(!check_swappable_with >()); +#endif + +static_assert( + check_swappable_with, std::map >()); +static_assert(!check_swappable_with, std::vector >()); +static_assert(check_swappable_with >, + std::optional > >()); +static_assert(!check_swappable_with >, + std::vector >()); +static_assert(check_swappable_with, std::vector >()); +static_assert(!check_swappable_with, int>()); +} // namespace standard_types + +int main(int, char**) { return 0; }