//===----------------------------------------------------------------------===// // // 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(swap(x,y))); #include #include #include #include #include "../../types.h" #include "test_macros.h" // Test constraint static_assert(std::is_swappable_v>); struct NotSwappable { NotSwappable& operator=(const NotSwappable&) = delete; }; void swap(NotSwappable&, NotSwappable&) = delete; // !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>); // Test noexcept struct MoveMayThrow { MoveMayThrow(MoveMayThrow&&) noexcept(false); friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} }; template concept FreeSwapNoexcept = requires(std::expected x, std::expected y) { { swap(x, y) } noexcept; }; static_assert(FreeSwapNoexcept); // !is_nothrow_move_constructible_v static_assert(!FreeSwapNoexcept); struct SwapMayThrow { friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} }; // !is_nothrow_swappable_v static_assert(!FreeSwapNoexcept); constexpr bool test() { // this->has_value() && rhs.has_value() { std::expected x; std::expected y; swap(x, y); assert(x.has_value()); assert(y.has_value()); } // !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() { Traced::state s{}; std::expected e1(std::in_place); std::expected e2(std::unexpect, s, 10); swap(e1, e2); assert(!e1.has_value()); assert(e1.error().data_ == 10); assert(e2.has_value()); assert(s.moveCtorCalled); assert(s.dtorCalled); } // !this->has_value() && rhs.has_value() { Traced::state s{}; std::expected e1(std::unexpect, s, 10); std::expected e2(std::in_place); swap(e1, e2); assert(e1.has_value()); assert(!e2.has_value()); assert(e2.error().data_ == 10); assert(s.moveCtorCalled); assert(s.dtorCalled); } // TailClobberer { std::expected> x(std::in_place); std::expected> y(std::unexpect); swap(x, y); // The next line would fail if adjusting the "has value" flag happened // _before_ constructing the member object 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() { bool e1Destroyed = false; std::expected e1(std::unexpect, e1Destroyed); std::expected e2(std::in_place); try { swap(e1, e2); assert(false); } catch (Except) { assert(!e1.has_value()); assert(e2.has_value()); assert(!e1Destroyed); } } // e1.has_value() && !e2.has_value() { bool e2Destroyed = false; std::expected e1(std::in_place); std::expected e2(std::unexpect, e2Destroyed); try { swap(e1, e2); assert(false); } catch (Except) { assert(e1.has_value()); assert(!e2.has_value()); assert(!e2Destroyed); } } // TailClobberer { std::expected> x(std::in_place); std::expected> y(std::unexpect); try { swap(x, y); assert(false); } catch (Except) { // This would fail if `TailClobbererNonTrivialMove<0, false, true>` // clobbered the flag before throwing the exception. assert(x.has_value()); assert(!y.has_value()); } } #endif // TEST_HAS_NO_EXCEPTIONS } int main(int, char**) { test(); static_assert(test()); testException(); return 0; }