//===----------------------------------------------------------------------===// // // 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 // constexpr void swap(expected& rhs) noexcept(see below); // // Constraints: // is_swappable_v is true and is_move_constructible_v is true. // // Throws: Any exception thrown by the expressions in the Effects. // // Remarks: The exception specification is equivalent to: // is_nothrow_move_constructible_v && is_nothrow_swappable_v. #include #include #include #include #include "../../types.h" #include "test_macros.h" // Test Constraints: template concept HasMemberSwap = requires(std::expected x, std::expected y) { x.swap(y); }; static_assert(HasMemberSwap); struct NotSwappable {}; void swap(NotSwappable&, NotSwappable&) = delete; // !is_swappable_v static_assert(!HasMemberSwap); struct NotMoveConstructible { NotMoveConstructible(NotMoveConstructible&&) = delete; friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} }; // !is_move_constructible_v static_assert(!HasMemberSwap); // Test noexcept struct MoveMayThrow { MoveMayThrow(MoveMayThrow&&) noexcept(false); friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} }; template concept MemberSwapNoexcept = // requires(std::expected x, std::expected y) { { x.swap(y) } noexcept; }; static_assert(MemberSwapNoexcept); // !is_nothrow_move_constructible_v static_assert(!MemberSwapNoexcept); struct SwapMayThrow { friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} }; // !is_nothrow_swappable_v static_assert(!MemberSwapNoexcept); constexpr bool test() { // this->has_value() && rhs.has_value() { std::expected x; std::expected y; x.swap(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); x.swap(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); e1.swap(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); e1.swap(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); x.swap(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()); } // CheckForInvalidWrites { { CheckForInvalidWrites x(std::unexpect); CheckForInvalidWrites y; x.swap(y); assert(x.check()); assert(y.check()); } { CheckForInvalidWrites x(std::unexpect); CheckForInvalidWrites y; x.swap(y); assert(x.check()); assert(y.check()); } } 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 { e1.swap(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 { e1.swap(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 { x.swap(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; }