//===----------------------------------------------------------------------===// // // 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, c++20 // friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); #include #include #include #include #include "../../types.h" #include "test_macros.h" // Test Constraints: struct NotSwappable { NotSwappable operator=(const NotSwappable&) = delete; }; void swap(NotSwappable&, NotSwappable&) = delete; static_assert(std::is_swappable_v>); // !is_swappable_v static_assert(!std::is_swappable_v>); // !is_swappable_v static_assert(!std::is_swappable_v>); struct NotMoveConstructible { NotMoveConstructible(NotMoveConstructible&&) = delete; friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} }; // !is_move_constructible_v static_assert(!std::is_swappable_v>); // !is_move_constructible_v static_assert(!std::is_swappable_v>); struct MoveMayThrow { MoveMayThrow(MoveMayThrow&&) noexcept(false); friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} }; // !is_nothrow_move_constructible_v && is_nothrow_move_constructible_v static_assert(std::is_swappable_v>); // is_nothrow_move_constructible_v && !is_nothrow_move_constructible_v static_assert(std::is_swappable_v>); // !is_nothrow_move_constructible_v && !is_nothrow_move_constructible_v static_assert(!std::is_swappable_v>); // Test noexcept static_assert(std::is_nothrow_swappable_v>); // !is_nothrow_move_constructible_v static_assert(!std::is_nothrow_swappable_v>); // !is_nothrow_move_constructible_v static_assert(!std::is_nothrow_swappable_v>); struct SwapMayThrow { friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} }; // !is_nothrow_swappable_v static_assert(!std::is_nothrow_swappable_v>); // !is_nothrow_swappable_v static_assert(!std::is_nothrow_swappable_v>); constexpr bool test() { // this->has_value() && rhs.has_value() { std::expected x(std::in_place, 5); std::expected y(std::in_place, 10); swap(x, y); assert(x.has_value()); assert(x->i == 10); assert(x->adlSwapCalled); assert(y.has_value()); assert(y->i == 5); assert(y->adlSwapCalled); } // !this->has_value() && !rhs.has_value() { std::expected x(std::unexpect, 5); std::expected y(std::unexpect, 10); swap(x, y); assert(!x.has_value()); assert(x.error().i == 10); assert(x.error().adlSwapCalled); assert(!y.has_value()); assert(y.error().i == 5); assert(y.error().adlSwapCalled); } // this->has_value() && !rhs.has_value() // && is_nothrow_move_constructible_v { std::expected, TrackedMove> e1(std::in_place, 5); std::expected, TrackedMove> e2(std::unexpect, 10); swap(e1, e2); assert(!e1.has_value()); assert(e1.error().i == 10); assert(e2.has_value()); assert(e2->i == 5); assert(e1.error().numberOfMoves == 2); assert(!e1.error().swapCalled); assert(e2->numberOfMoves == 1); assert(!e2->swapCalled); } // this->has_value() && !rhs.has_value() // && !is_nothrow_move_constructible_v { std::expected, TrackedMove> e1(std::in_place, 5); std::expected, TrackedMove> e2(std::unexpect, 10); swap(e1, e2); assert(!e1.has_value()); assert(e1.error().i == 10); assert(e2.has_value()); assert(e2->i == 5); assert(e1.error().numberOfMoves == 1); assert(!e1.error().swapCalled); assert(e2->numberOfMoves == 2); assert(!e2->swapCalled); } // !this->has_value() && rhs.has_value() // && is_nothrow_move_constructible_v { std::expected, TrackedMove> e1(std::unexpect, 10); std::expected, TrackedMove> e2(std::in_place, 5); swap(e1, e2); assert(e1.has_value()); assert(e1->i == 5); assert(!e2.has_value()); assert(e2.error().i == 10); assert(e1->numberOfMoves == 1); assert(!e1->swapCalled); assert(e2.error().numberOfMoves == 2); assert(!e2.error().swapCalled); } // !this->has_value() && rhs.has_value() // && !is_nothrow_move_constructible_v { std::expected, TrackedMove> e1(std::unexpect, 10); std::expected, TrackedMove> e2(std::in_place, 5); swap(e1, e2); assert(e1.has_value()); assert(e1->i == 5); assert(!e2.has_value()); assert(e2.error().i == 10); assert(e1->numberOfMoves == 2); assert(!e1->swapCalled); assert(e2.error().numberOfMoves == 1); assert(!e2.error().swapCalled); } // TailClobberer { // is_nothrow_move_constructible_v { std::expected, TailClobbererNonTrivialMove<1, true>> x(std::in_place); std::expected, TailClobbererNonTrivialMove<1, true>> y(std::unexpect); swap(x, y); // Both of these would fail if adjusting the "has value" flags happened // _before_ constructing the member objects inside the `swap`. assert(!x.has_value()); assert(y.has_value()); } // !is_nothrow_move_constructible_v { std::expected, TailClobbererNonTrivialMove<1, false>> x(std::in_place); std::expected, TailClobbererNonTrivialMove<1, false>> y(std::unexpect); swap(x, y); // Both of these would fail if adjusting the "has value" flags happened // _before_ constructing the member objects inside the `swap`. assert(!x.has_value()); assert(y.has_value()); } } return true; } void testException() { #ifndef TEST_HAS_NO_EXCEPTIONS // !e1.has_value() && e2.has_value() { std::expected e1(std::unexpect, 5); std::expected e2(std::in_place); try { swap(e1, e2); assert(false); } catch (Except) { assert(!e1.has_value()); assert(e1.error() == 5); } } // e1.has_value() && !e2.has_value() { std::expected e1(5); std::expected e2(std::unexpect); try { swap(e1, e2); assert(false); } catch (Except) { assert(e1.has_value()); assert(*e1 == 5); } } // TailClobberer { // is_nothrow_move_constructible_v { std::expected, TailClobbererNonTrivialMove<1>> x(std::in_place); std::expected, TailClobbererNonTrivialMove<1>> y(std::unexpect); try { swap(x, y); assert(false); } catch (Except) { assert(x.has_value()); // This would fail if `TailClobbererNonTrivialMove<1>` clobbered the // flag when rolling back the swap. assert(!y.has_value()); } } // !is_nothrow_move_constructible_v { std::expected, TailClobbererNonTrivialMove<1, false, true>> x(std::in_place); std::expected, TailClobbererNonTrivialMove<1, false, true>> y(std::unexpect); try { swap(x, y); assert(false); } catch (Except) { // This would fail if `TailClobbererNonTrivialMove<0>` clobbered the // flag when rolling back the swap. assert(x.has_value()); assert(!y.has_value()); } } } #endif // TEST_HAS_NO_EXCEPTIONS } int main(int, char**) { test(); static_assert(test()); testException(); return 0; }