1e356f681SHui Xie //===----------------------------------------------------------------------===// 2*6a54dfbfSLouis Dionne // 3e356f681SHui Xie // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e356f681SHui Xie // See https://llvm.org/LICENSE.txt for license information. 5e356f681SHui Xie // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e356f681SHui Xie // 7e356f681SHui Xie //===----------------------------------------------------------------------===// 8e356f681SHui Xie 9e356f681SHui Xie // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 10e356f681SHui Xie 11e356f681SHui Xie // constexpr void swap(expected& rhs) noexcept(see below); 12e356f681SHui Xie // 13e356f681SHui Xie // Constraints: 14e356f681SHui Xie // is_swappable_v<E> is true and is_move_constructible_v<E> is true. 15e356f681SHui Xie // 16e356f681SHui Xie // Throws: Any exception thrown by the expressions in the Effects. 17e356f681SHui Xie // 18e356f681SHui Xie // Remarks: The exception specification is equivalent to: 19e356f681SHui Xie // is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E>. 20e356f681SHui Xie 21e356f681SHui Xie #include <cassert> 22e356f681SHui Xie #include <expected> 23e356f681SHui Xie #include <type_traits> 24e356f681SHui Xie #include <utility> 25e356f681SHui Xie 26e356f681SHui Xie #include "../../types.h" 27e356f681SHui Xie #include "test_macros.h" 28e356f681SHui Xie 29e356f681SHui Xie // Test Constraints: 30e356f681SHui Xie template <class E> 31e356f681SHui Xie concept HasMemberSwap = requires(std::expected<void, E> x, std::expected<void, E> y) { x.swap(y); }; 32e356f681SHui Xie 33e356f681SHui Xie static_assert(HasMemberSwap<int>); 34e356f681SHui Xie 35e356f681SHui Xie struct NotSwappable {}; 36e356f681SHui Xie void swap(NotSwappable&, NotSwappable&) = delete; 37e356f681SHui Xie 38e356f681SHui Xie // !is_swappable_v<E> 39e356f681SHui Xie static_assert(!HasMemberSwap<NotSwappable>); 40e356f681SHui Xie 41f5832babSStephan T. Lavavej struct NotMoveConstructible { 42f5832babSStephan T. Lavavej NotMoveConstructible(NotMoveConstructible&&) = delete; 43f5832babSStephan T. Lavavej friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} 44e356f681SHui Xie }; 45e356f681SHui Xie 46e356f681SHui Xie // !is_move_constructible_v<E> 47f5832babSStephan T. Lavavej static_assert(!HasMemberSwap<NotMoveConstructible>); 48e356f681SHui Xie 49e356f681SHui Xie // Test noexcept 50e356f681SHui Xie struct MoveMayThrow { 51e356f681SHui Xie MoveMayThrow(MoveMayThrow&&) noexcept(false); 52e356f681SHui Xie friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} 53e356f681SHui Xie }; 54e356f681SHui Xie 55e356f681SHui Xie template <class E> 56134c9159SJan Kokemüller concept MemberSwapNoexcept = // 57e356f681SHui Xie requires(std::expected<void, E> x, std::expected<void, E> y) { 58e356f681SHui Xie { x.swap(y) } noexcept; 59e356f681SHui Xie }; 60e356f681SHui Xie 61e356f681SHui Xie static_assert(MemberSwapNoexcept<int>); 62e356f681SHui Xie 63e356f681SHui Xie // !is_nothrow_move_constructible_v<E> 64e356f681SHui Xie static_assert(!MemberSwapNoexcept<MoveMayThrow>); 65e356f681SHui Xie 66e356f681SHui Xie struct SwapMayThrow { 67e356f681SHui Xie friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} 68e356f681SHui Xie }; 69e356f681SHui Xie 70e356f681SHui Xie // !is_nothrow_swappable_v<E> 71e356f681SHui Xie static_assert(!MemberSwapNoexcept<SwapMayThrow>); 72e356f681SHui Xie 73e356f681SHui Xie constexpr bool test() { 74e356f681SHui Xie // this->has_value() && rhs.has_value() 75e356f681SHui Xie { 76e356f681SHui Xie std::expected<void, int> x; 77e356f681SHui Xie std::expected<void, int> y; 78e356f681SHui Xie x.swap(y); 79e356f681SHui Xie 80e356f681SHui Xie assert(x.has_value()); 81e356f681SHui Xie assert(y.has_value()); 82e356f681SHui Xie } 83e356f681SHui Xie 84e356f681SHui Xie // !this->has_value() && !rhs.has_value() 85e356f681SHui Xie { 86e356f681SHui Xie std::expected<void, ADLSwap> x(std::unexpect, 5); 87e356f681SHui Xie std::expected<void, ADLSwap> y(std::unexpect, 10); 88e356f681SHui Xie x.swap(y); 89e356f681SHui Xie 90e356f681SHui Xie assert(!x.has_value()); 91e356f681SHui Xie assert(x.error().i == 10); 92e356f681SHui Xie assert(x.error().adlSwapCalled); 93e356f681SHui Xie assert(!y.has_value()); 94e356f681SHui Xie assert(y.error().i == 5); 95e356f681SHui Xie assert(y.error().adlSwapCalled); 96e356f681SHui Xie } 97e356f681SHui Xie 98e356f681SHui Xie // this->has_value() && !rhs.has_value() 99e356f681SHui Xie { 100e356f681SHui Xie Traced::state s{}; 101e356f681SHui Xie std::expected<void, Traced> e1(std::in_place); 102e356f681SHui Xie std::expected<void, Traced> e2(std::unexpect, s, 10); 103e356f681SHui Xie 104e356f681SHui Xie e1.swap(e2); 105e356f681SHui Xie 106e356f681SHui Xie assert(!e1.has_value()); 107e356f681SHui Xie assert(e1.error().data_ == 10); 108e356f681SHui Xie assert(e2.has_value()); 109e356f681SHui Xie 110e356f681SHui Xie assert(s.moveCtorCalled); 111e356f681SHui Xie assert(s.dtorCalled); 112e356f681SHui Xie } 113e356f681SHui Xie 114e356f681SHui Xie // !this->has_value() && rhs.has_value() 115e356f681SHui Xie { 116e356f681SHui Xie Traced::state s{}; 117e356f681SHui Xie std::expected<void, Traced> e1(std::unexpect, s, 10); 118e356f681SHui Xie std::expected<void, Traced> e2(std::in_place); 119e356f681SHui Xie 120e356f681SHui Xie e1.swap(e2); 121e356f681SHui Xie 122e356f681SHui Xie assert(e1.has_value()); 123e356f681SHui Xie assert(!e2.has_value()); 124e356f681SHui Xie assert(e2.error().data_ == 10); 125e356f681SHui Xie 126e356f681SHui Xie assert(s.moveCtorCalled); 127e356f681SHui Xie assert(s.dtorCalled); 128e356f681SHui Xie } 129e356f681SHui Xie 130134c9159SJan Kokemüller // TailClobberer 131134c9159SJan Kokemüller { 132134c9159SJan Kokemüller std::expected<void, TailClobbererNonTrivialMove<1>> x(std::in_place); 133134c9159SJan Kokemüller std::expected<void, TailClobbererNonTrivialMove<1>> y(std::unexpect); 134134c9159SJan Kokemüller 135134c9159SJan Kokemüller x.swap(y); 136134c9159SJan Kokemüller 137134c9159SJan Kokemüller // The next line would fail if adjusting the "has value" flag happened 138134c9159SJan Kokemüller // _before_ constructing the member object inside the `swap`. 139134c9159SJan Kokemüller assert(!x.has_value()); 140134c9159SJan Kokemüller assert(y.has_value()); 141134c9159SJan Kokemüller } 142134c9159SJan Kokemüller 1434f469053SJan Kokemüller // CheckForInvalidWrites 1444f469053SJan Kokemüller { 1454f469053SJan Kokemüller { 1464f469053SJan Kokemüller CheckForInvalidWrites<true, true> x(std::unexpect); 1474f469053SJan Kokemüller CheckForInvalidWrites<true, true> y; 1484f469053SJan Kokemüller 1494f469053SJan Kokemüller x.swap(y); 1504f469053SJan Kokemüller 1514f469053SJan Kokemüller assert(x.check()); 1524f469053SJan Kokemüller assert(y.check()); 1534f469053SJan Kokemüller } 1544f469053SJan Kokemüller { 1554f469053SJan Kokemüller CheckForInvalidWrites<false, true> x(std::unexpect); 1564f469053SJan Kokemüller CheckForInvalidWrites<false, true> y; 1574f469053SJan Kokemüller 1584f469053SJan Kokemüller x.swap(y); 1594f469053SJan Kokemüller 1604f469053SJan Kokemüller assert(x.check()); 1614f469053SJan Kokemüller assert(y.check()); 1624f469053SJan Kokemüller } 1634f469053SJan Kokemüller } 1644f469053SJan Kokemüller 165e356f681SHui Xie return true; 166e356f681SHui Xie } 167e356f681SHui Xie 168e356f681SHui Xie void testException() { 169e356f681SHui Xie #ifndef TEST_HAS_NO_EXCEPTIONS 170e356f681SHui Xie // !e1.has_value() && e2.has_value() 171e356f681SHui Xie { 172e356f681SHui Xie bool e1Destroyed = false; 173e356f681SHui Xie std::expected<void, ThrowOnMove> e1(std::unexpect, e1Destroyed); 174e356f681SHui Xie std::expected<void, ThrowOnMove> e2(std::in_place); 175e356f681SHui Xie try { 176e356f681SHui Xie e1.swap(e2); 177e356f681SHui Xie assert(false); 178e356f681SHui Xie } catch (Except) { 179e356f681SHui Xie assert(!e1.has_value()); 180e356f681SHui Xie assert(e2.has_value()); 181e356f681SHui Xie assert(!e1Destroyed); 182e356f681SHui Xie } 183e356f681SHui Xie } 184e356f681SHui Xie 185e356f681SHui Xie // e1.has_value() && !e2.has_value() 186e356f681SHui Xie { 187e356f681SHui Xie bool e2Destroyed = false; 188e356f681SHui Xie std::expected<void, ThrowOnMove> e1(std::in_place); 189e356f681SHui Xie std::expected<void, ThrowOnMove> e2(std::unexpect, e2Destroyed); 190e356f681SHui Xie try { 191e356f681SHui Xie e1.swap(e2); 192e356f681SHui Xie assert(false); 193e356f681SHui Xie } catch (Except) { 194e356f681SHui Xie assert(e1.has_value()); 195e356f681SHui Xie assert(!e2.has_value()); 196e356f681SHui Xie assert(!e2Destroyed); 197e356f681SHui Xie } 198e356f681SHui Xie } 199134c9159SJan Kokemüller 200134c9159SJan Kokemüller // TailClobberer 201134c9159SJan Kokemüller { 202134c9159SJan Kokemüller std::expected<void, TailClobbererNonTrivialMove<0, false, true>> x(std::in_place); 203134c9159SJan Kokemüller std::expected<void, TailClobbererNonTrivialMove<0, false, true>> y(std::unexpect); 204134c9159SJan Kokemüller try { 205134c9159SJan Kokemüller x.swap(y); 206134c9159SJan Kokemüller assert(false); 207134c9159SJan Kokemüller } catch (Except) { 208134c9159SJan Kokemüller // This would fail if `TailClobbererNonTrivialMove<0, false, true>` 209134c9159SJan Kokemüller // clobbered the flag before throwing the exception. 210134c9159SJan Kokemüller assert(x.has_value()); 211134c9159SJan Kokemüller assert(!y.has_value()); 212134c9159SJan Kokemüller } 213134c9159SJan Kokemüller } 214e356f681SHui Xie #endif // TEST_HAS_NO_EXCEPTIONS 215e356f681SHui Xie } 216e356f681SHui Xie 217e356f681SHui Xie int main(int, char**) { 218e356f681SHui Xie test(); 219e356f681SHui Xie static_assert(test()); 220e356f681SHui Xie testException(); 221e356f681SHui Xie return 0; 222e356f681SHui Xie } 223