308 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// template<class T>12// concept swappable = // see below13 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 30template <class T>31struct expected {32 T x;33 T y;34};35 36// clang-format off37// Checks [concept.swappable]/2.138template <class T, class U>39requires std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U> > &&40 std::swappable<std::remove_cvref_t<T> >41constexpr 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.248template <std::swappable T, std::size_t N>49constexpr 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.360template <std::swappable T>61requires std::copy_constructible<std::remove_cvref_t<T> >62constexpr 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 on68 69constexpr 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}76static_assert(check_lvalue_adl_swappable());77 78constexpr 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}83static_assert(check_rvalue_adl_swappable());84 85constexpr 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}91static_assert(check_lvalue_rvalue_adl_swappable());92 93constexpr 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}99static_assert(check_rvalue_lvalue_adl_swappable());100 101constexpr 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}108static_assert(check_throwable_swappable());109 110constexpr 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}117static_assert(check_non_move_constructible_adl_swappable());118 119constexpr 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}126static_assert(check_non_move_assignable_adl_swappable());127 128namespace swappable_namespace {129enum unscoped { hello, world };130void swap(unscoped&, unscoped&);131 132enum class scoped { hello, world };133void swap(scoped&, scoped&);134} // namespace swappable_namespace135 136static_assert(std::swappable<swappable_namespace::unscoped>);137static_assert(std::swappable<swappable_namespace::scoped>);138 139constexpr 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}146static_assert(check_swap_arrays());147 148constexpr 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}155static_assert(check_lvalue_adl_swappable_arrays());156 157constexpr 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}164static_assert(check_throwable_adl_swappable_arrays());165 166auto global_x = 0;167ASSERT_NOEXCEPT(std::ranges::swap(global_x, global_x));168static_assert(check_swap_23(0, 0));169static_assert(check_swap_23(0, 1));170static_assert(check_swap_23(1, 0));171 172constexpr 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}179static_assert(check_swappable_references());180 181constexpr 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}187static_assert(check_swappable_pointers());188 189namespace union_swap {190union adl_swappable {191 int x;192 double y;193};194 195void swap(adl_swappable&, adl_swappable&);196void swap(adl_swappable&&, adl_swappable&&);197} // namespace union_swap198static_assert(std::swappable<union_swap::adl_swappable>);199static_assert(std::swappable<union_swap::adl_swappable&>);200static_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 to203// sanity check for a few positive cases.204static_assert(std::swappable<int volatile&>);205static_assert(std::swappable<int&&>);206static_assert(std::swappable<int (*)()>);207static_assert(std::swappable<int rvalue_adl_swappable::*>);208static_assert(std::swappable<int (rvalue_adl_swappable::*)()>);209static_assert(std::swappable<std::unique_ptr<int> >);210 211static_assert(!std::swappable<void>);212static_assert(!std::swappable<int const>);213static_assert(!std::swappable<int const&>);214static_assert(!std::swappable<int const&&>);215static_assert(!std::swappable<int const volatile>);216static_assert(!std::swappable<int const volatile&>);217static_assert(!std::swappable<int const volatile&&>);218static_assert(!std::swappable<int (&)()>);219static_assert(!std::swappable<DeletedMoveCtor>);220static_assert(!std::swappable<ImplicitlyDeletedMoveCtor>);221static_assert(!std::swappable<DeletedMoveAssign>);222static_assert(!std::swappable<ImplicitlyDeletedMoveAssign>);223static_assert(!std::swappable<NonMovable>);224static_assert(!std::swappable<DerivedFromNonMovable>);225static_assert(!std::swappable<HasANonMovable>);226 227using swap_type = std::remove_const_t<decltype(std::ranges::swap)>;228static_assert(std::default_initializable<swap_type>);229static_assert(std::move_constructible<swap_type>);230static_assert(std::copy_constructible<swap_type>);231static_assert(std::assignable_from<swap_type&, swap_type>);232static_assert(std::assignable_from<swap_type&, swap_type&>);233static_assert(std::assignable_from<swap_type&, swap_type const&>);234static_assert(std::assignable_from<swap_type&, swap_type const>);235static_assert(std::swappable<swap_type>);236 237enum class nothrow { no, yes };238 239template <nothrow is_noexcept, std::swappable T>240void 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 255int 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