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 expected& operator=(expected&& rhs) noexcept(see below); 12e356f681SHui Xie // 13e356f681SHui Xie // Constraints: 14e356f681SHui Xie // - is_move_constructible_v<T> is true and 15e356f681SHui Xie // - is_move_assignable_v<T> is true and 16e356f681SHui Xie // - is_move_constructible_v<E> is true and 17e356f681SHui Xie // - is_move_assignable_v<E> is true and 18e356f681SHui Xie // - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true. 19e356f681SHui Xie // 20e356f681SHui Xie // Effects: 21e356f681SHui Xie // - If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs). 22e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to: 23e356f681SHui Xie // reinit-expected(unex, val, std::move(rhs.error())) 24e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to: 25e356f681SHui Xie // reinit-expected(val, unex, std::move(*rhs)) 26e356f681SHui Xie // - Otherwise, equivalent to unex = std::move(rhs.error()). 27e356f681SHui Xie // - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this; 28e356f681SHui Xie // 29e356f681SHui Xie // Returns: *this. 30e356f681SHui Xie // 31e356f681SHui Xie // Remarks: The exception specification is equivalent to: 32e356f681SHui Xie // is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T> && 33e356f681SHui Xie // is_nothrow_move_assignable_v<E> && is_nothrow_move_constructible_v<E> 34e356f681SHui Xie 35e356f681SHui Xie #include <cassert> 36e356f681SHui Xie #include <concepts> 37e356f681SHui Xie #include <expected> 38e356f681SHui Xie #include <type_traits> 39e356f681SHui Xie #include <utility> 40e356f681SHui Xie 41e356f681SHui Xie #include "../../types.h" 42e356f681SHui Xie #include "test_macros.h" 43e356f681SHui Xie 44e356f681SHui Xie struct NotMoveConstructible { 45e356f681SHui Xie NotMoveConstructible(NotMoveConstructible&&) = delete; 46e356f681SHui Xie NotMoveConstructible& operator=(NotMoveConstructible&&) = default; 47e356f681SHui Xie }; 48e356f681SHui Xie 49e356f681SHui Xie struct NotMoveAssignable { 50e356f681SHui Xie NotMoveAssignable(NotMoveAssignable&&) = default; 51e356f681SHui Xie NotMoveAssignable& operator=(NotMoveAssignable&&) = delete; 52e356f681SHui Xie }; 53e356f681SHui Xie 54e356f681SHui Xie struct MoveCtorMayThrow { 55e356f681SHui Xie MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {} 56e356f681SHui Xie MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default; 57e356f681SHui Xie }; 58e356f681SHui Xie 59e356f681SHui Xie // Test constraints 60e356f681SHui Xie static_assert(std::is_move_assignable_v<std::expected<int, int>>); 61e356f681SHui Xie 62e356f681SHui Xie // !is_move_assignable_v<T> 63e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<NotMoveAssignable, int>>); 64e356f681SHui Xie 65e356f681SHui Xie // !is_move_constructible_v<T> 66e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<NotMoveConstructible, int>>); 67e356f681SHui Xie 68e356f681SHui Xie // !is_move_assignable_v<E> 69e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveAssignable>>); 70e356f681SHui Xie 71e356f681SHui Xie // !is_move_constructible_v<E> 72e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveConstructible>>); 73e356f681SHui Xie 74e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E> 75e356f681SHui Xie static_assert(std::is_move_assignable_v<std::expected<MoveCtorMayThrow, int>>); 76e356f681SHui Xie 77e356f681SHui Xie // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 78e356f681SHui Xie static_assert(std::is_move_assignable_v<std::expected<int, MoveCtorMayThrow>>); 79e356f681SHui Xie 80e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 81e356f681SHui Xie static_assert(!std::is_move_assignable_v<std::expected<MoveCtorMayThrow, MoveCtorMayThrow>>); 82e356f681SHui Xie 83e356f681SHui Xie struct MoveAssignMayThrow { 84e356f681SHui Xie MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default; 85e356f681SHui Xie MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; } 86e356f681SHui Xie }; 87e356f681SHui Xie 88e356f681SHui Xie // Test noexcept 89e356f681SHui Xie static_assert(std::is_nothrow_move_assignable_v<std::expected<int, int>>); 90e356f681SHui Xie 91e356f681SHui Xie // !is_nothrow_move_assignable_v<T> 92e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveAssignMayThrow, int>>); 93e356f681SHui Xie 94e356f681SHui Xie // !is_nothrow_move_constructible_v<T> 95e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveCtorMayThrow, int>>); 96e356f681SHui Xie 97e356f681SHui Xie // !is_nothrow_move_assignable_v<E> 98e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveAssignMayThrow>>); 99e356f681SHui Xie 100e356f681SHui Xie // !is_nothrow_move_constructible_v<E> 101e356f681SHui Xie static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveCtorMayThrow>>); 102e356f681SHui Xie 103e356f681SHui Xie constexpr bool test() { 104e356f681SHui Xie // If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs). 105e356f681SHui Xie { 106e356f681SHui Xie Traced::state oldState{}; 107e356f681SHui Xie Traced::state newState{}; 108e356f681SHui Xie std::expected<Traced, int> e1(std::in_place, oldState, 5); 109e356f681SHui Xie std::expected<Traced, int> e2(std::in_place, newState, 10); 110e356f681SHui Xie decltype(auto) x = (e1 = std::move(e2)); 111e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>); 112e356f681SHui Xie assert(&x == &e1); 113e356f681SHui Xie 114e356f681SHui Xie assert(e1.has_value()); 115e356f681SHui Xie assert(e1.value().data_ == 10); 116e356f681SHui Xie assert(oldState.moveAssignCalled); 117e356f681SHui Xie } 118e356f681SHui Xie 119e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to: 120e356f681SHui Xie // reinit-expected(unex, val, rhs.error()) 121e356f681SHui Xie // E move is not noexcept 122e356f681SHui Xie // In this case, it should call the branch 123e356f681SHui Xie // 124e356f681SHui Xie // U tmp(std::move(oldval)); 125e356f681SHui Xie // destroy_at(addressof(oldval)); 126e356f681SHui Xie // try { 127e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 128e356f681SHui Xie // } catch (...) { 129e356f681SHui Xie // construct_at(addressof(oldval), std::move(tmp)); 130e356f681SHui Xie // throw; 131e356f681SHui Xie // } 132e356f681SHui Xie // 133e356f681SHui Xie { 134e356f681SHui Xie TracedNoexcept::state oldState{}; 135e356f681SHui Xie Traced::state newState{}; 136e356f681SHui Xie std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5); 137e356f681SHui Xie std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10); 138e356f681SHui Xie 139e356f681SHui Xie decltype(auto) x = (e1 = std::move(e2)); 140e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>); 141e356f681SHui Xie assert(&x == &e1); 142e356f681SHui Xie 143e356f681SHui Xie assert(!e1.has_value()); 144e356f681SHui Xie assert(e1.error().data_ == 10); 145e356f681SHui Xie 146e356f681SHui Xie assert(!oldState.moveAssignCalled); 147e356f681SHui Xie assert(oldState.moveCtorCalled); 148e356f681SHui Xie assert(oldState.dtorCalled); 149e356f681SHui Xie assert(!oldState.copyCtorCalled); 150e356f681SHui Xie assert(!newState.copyCtorCalled); 151e356f681SHui Xie assert(newState.moveCtorCalled); 152e356f681SHui Xie assert(!newState.dtorCalled); 153e356f681SHui Xie } 154e356f681SHui Xie 155e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to: 156e356f681SHui Xie // reinit-expected(unex, val, rhs.error()) 157e356f681SHui Xie // E move is noexcept 158e356f681SHui Xie // In this case, it should call the branch 159e356f681SHui Xie // 160e356f681SHui Xie // destroy_at(addressof(oldval)); 161e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 162e356f681SHui Xie // 163e356f681SHui Xie { 164e356f681SHui Xie Traced::state oldState{}; 165e356f681SHui Xie TracedNoexcept::state newState{}; 166e356f681SHui Xie std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5); 167e356f681SHui Xie std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10); 168e356f681SHui Xie 169e356f681SHui Xie decltype(auto) x = (e1 = std::move(e2)); 170e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>); 171e356f681SHui Xie assert(&x == &e1); 172e356f681SHui Xie 173e356f681SHui Xie assert(!e1.has_value()); 174e356f681SHui Xie assert(e1.error().data_ == 10); 175e356f681SHui Xie 176e356f681SHui Xie assert(!oldState.moveCtorCalled); 177e356f681SHui Xie assert(oldState.dtorCalled); 178e356f681SHui Xie assert(!oldState.copyCtorCalled); 179e356f681SHui Xie assert(!newState.copyCtorCalled); 180e356f681SHui Xie assert(newState.moveCtorCalled); 181e356f681SHui Xie assert(!newState.dtorCalled); 182e356f681SHui Xie } 183e356f681SHui Xie 184e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to: 185e356f681SHui Xie // reinit-expected(val, unex, *rhs) 186e356f681SHui Xie // T move is not noexcept 187e356f681SHui Xie // In this case, it should call the branch 188e356f681SHui Xie // 189e356f681SHui Xie // U tmp(std::move(oldval)); 190e356f681SHui Xie // destroy_at(addressof(oldval)); 191e356f681SHui Xie // try { 192e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 193e356f681SHui Xie // } catch (...) { 194e356f681SHui Xie // construct_at(addressof(oldval), std::move(tmp)); 195e356f681SHui Xie // throw; 196e356f681SHui Xie // } 197e356f681SHui Xie // 198e356f681SHui Xie { 199e356f681SHui Xie TracedNoexcept::state oldState{}; 200e356f681SHui Xie Traced::state newState{}; 201e356f681SHui Xie std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5); 202e356f681SHui Xie std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10); 203e356f681SHui Xie 204e356f681SHui Xie decltype(auto) x = (e1 = std::move(e2)); 205e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>); 206e356f681SHui Xie assert(&x == &e1); 207e356f681SHui Xie 208e356f681SHui Xie assert(e1.has_value()); 209e356f681SHui Xie assert(e1.value().data_ == 10); 210e356f681SHui Xie 211e356f681SHui Xie assert(oldState.moveCtorCalled); 212e356f681SHui Xie assert(oldState.dtorCalled); 213e356f681SHui Xie assert(!oldState.copyCtorCalled); 214e356f681SHui Xie assert(!newState.copyCtorCalled); 215e356f681SHui Xie assert(newState.moveCtorCalled); 216e356f681SHui Xie assert(!newState.dtorCalled); 217e356f681SHui Xie } 218e356f681SHui Xie 219e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to: 220e356f681SHui Xie // reinit-expected(val, unex, *rhs) 221e356f681SHui Xie // T move is noexcept 222e356f681SHui Xie // In this case, it should call the branch 223e356f681SHui Xie // 224e356f681SHui Xie // destroy_at(addressof(oldval)); 225e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 226e356f681SHui Xie // 227e356f681SHui Xie { 228e356f681SHui Xie Traced::state oldState{}; 229e356f681SHui Xie TracedNoexcept::state newState{}; 230e356f681SHui Xie std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5); 231e356f681SHui Xie std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10); 232e356f681SHui Xie 233e356f681SHui Xie decltype(auto) x = (e1 = std::move(e2)); 234e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>); 235e356f681SHui Xie assert(&x == &e1); 236e356f681SHui Xie 237e356f681SHui Xie assert(e1.has_value()); 238e356f681SHui Xie assert(e1.value().data_ == 10); 239e356f681SHui Xie 240e356f681SHui Xie assert(!oldState.moveCtorCalled); 241e356f681SHui Xie assert(oldState.dtorCalled); 242e356f681SHui Xie assert(!oldState.copyCtorCalled); 243e356f681SHui Xie assert(!newState.copyCtorCalled); 244e356f681SHui Xie assert(newState.moveCtorCalled); 245e356f681SHui Xie assert(!newState.dtorCalled); 246e356f681SHui Xie } 247e356f681SHui Xie 248e356f681SHui Xie // Otherwise, equivalent to unex = rhs.error(). 249e356f681SHui Xie { 250e356f681SHui Xie Traced::state oldState{}; 251e356f681SHui Xie Traced::state newState{}; 252e356f681SHui Xie std::expected<int, Traced> e1(std::unexpect, oldState, 5); 253e356f681SHui Xie std::expected<int, Traced> e2(std::unexpect, newState, 10); 254e356f681SHui Xie decltype(auto) x = (e1 = std::move(e2)); 255e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>); 256e356f681SHui Xie assert(&x == &e1); 257e356f681SHui Xie 258e356f681SHui Xie assert(!e1.has_value()); 259e356f681SHui Xie assert(e1.error().data_ == 10); 260e356f681SHui Xie assert(oldState.moveAssignCalled); 261e356f681SHui Xie } 2624f469053SJan Kokemüller 2634f469053SJan Kokemüller // CheckForInvalidWrites 2644f469053SJan Kokemüller { 2654f469053SJan Kokemüller { 2664f469053SJan Kokemüller CheckForInvalidWrites<true> e1(std::unexpect); 2674f469053SJan Kokemüller CheckForInvalidWrites<true> e2; 2684f469053SJan Kokemüller 2694f469053SJan Kokemüller e1 = std::move(e2); 2704f469053SJan Kokemüller 2714f469053SJan Kokemüller assert(e1.check()); 2724f469053SJan Kokemüller assert(e2.check()); 2734f469053SJan Kokemüller } 2744f469053SJan Kokemüller { 2754f469053SJan Kokemüller CheckForInvalidWrites<false> e1(std::unexpect); 2764f469053SJan Kokemüller CheckForInvalidWrites<false> e2; 2774f469053SJan Kokemüller 2784f469053SJan Kokemüller e1 = std::move(e2); 2794f469053SJan Kokemüller 2804f469053SJan Kokemüller assert(e1.check()); 2814f469053SJan Kokemüller assert(e2.check()); 2824f469053SJan Kokemüller } 2834f469053SJan Kokemüller } 2844f469053SJan Kokemüller 285e356f681SHui Xie return true; 286e356f681SHui Xie } 287e356f681SHui Xie 288e356f681SHui Xie void testException() { 289e356f681SHui Xie #ifndef TEST_HAS_NO_EXCEPTIONS 290e356f681SHui Xie // assign value throw on move 291e356f681SHui Xie { 292e356f681SHui Xie std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5); 293e356f681SHui Xie std::expected<ThrowOnMoveConstruct, int> e2(std::in_place); 294e356f681SHui Xie try { 295e356f681SHui Xie e1 = std::move(e2); 296e356f681SHui Xie assert(false); 297e356f681SHui Xie } catch (Except) { 298e356f681SHui Xie assert(!e1.has_value()); 299e356f681SHui Xie assert(e1.error() == 5); 300e356f681SHui Xie } 301e356f681SHui Xie } 302e356f681SHui Xie 303e356f681SHui Xie // assign error throw on move 304e356f681SHui Xie { 305e356f681SHui Xie std::expected<int, ThrowOnMoveConstruct> e1(5); 306e356f681SHui Xie std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect); 307e356f681SHui Xie try { 308e356f681SHui Xie e1 = std::move(e2); 309e356f681SHui Xie assert(false); 310e356f681SHui Xie } catch (Except) { 311e356f681SHui Xie assert(e1.has_value()); 312e356f681SHui Xie assert(*e1 == 5); 313e356f681SHui Xie } 314e356f681SHui Xie } 315e356f681SHui Xie #endif // TEST_HAS_NO_EXCEPTIONS 316e356f681SHui Xie } 317e356f681SHui Xie 318e356f681SHui Xie int main(int, char**) { 319e356f681SHui Xie test(); 320e356f681SHui Xie static_assert(test()); 321e356f681SHui Xie testException(); 322e356f681SHui Xie return 0; 323e356f681SHui Xie } 324