1 //===----------------------------------------------------------------------===// 2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 3 // See https://llvm.org/LICENSE.txt for license information. 4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 5 // 6 //===----------------------------------------------------------------------===// 7 8 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 9 10 // constexpr expected& operator=(expected&& rhs) noexcept(see below); 11 // 12 // Effects: 13 // - If this->has_value() && rhs.has_value() is true, no effects. 14 // - Otherwise, if this->has_value() is true, equivalent to: 15 // construct_at(addressof(unex), std::move(rhs.unex)); 16 // has_val = false; 17 // - Otherwise, if rhs.has_value() is true, destroys unex and sets has_val to true. 18 // - Otherwise, equivalent to unex = std::move(rhs.error()). 19 // 20 // Returns: *this. 21 // 22 // Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<E> && is_nothrow_move_assignable_v<E>. 23 // 24 // This operator is defined as deleted unless is_move_constructible_v<E> is true and is_move_assignable_v<E> is true. 25 26 #include <cassert> 27 #include <concepts> 28 #include <expected> 29 #include <type_traits> 30 #include <utility> 31 32 #include "../../types.h" 33 #include "test_macros.h" 34 35 struct NotMoveConstructible { 36 NotMoveConstructible(NotMoveConstructible&&) = delete; 37 NotMoveConstructible& operator=(NotMoveConstructible&&) = default; 38 }; 39 40 struct NotMoveAssignable { 41 NotMoveAssignable(NotMoveAssignable&&) = default; 42 NotMoveAssignable& operator=(NotMoveAssignable&&) = delete; 43 }; 44 45 // Test constraints 46 static_assert(std::is_move_assignable_v<std::expected<void, int>>); 47 48 // !is_move_assignable_v<E> 49 static_assert(!std::is_move_assignable_v<std::expected<void, NotMoveAssignable>>); 50 51 // !is_move_constructible_v<E> 52 static_assert(!std::is_move_assignable_v<std::expected<void, NotMoveConstructible>>); 53 54 // Test noexcept 55 struct MoveCtorMayThrow { 56 MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {} 57 MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default; 58 }; 59 60 struct MoveAssignMayThrow { 61 MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default; 62 MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; } 63 }; 64 65 // Test noexcept 66 static_assert(std::is_nothrow_move_assignable_v<std::expected<void, int>>); 67 68 // !is_nothrow_move_assignable_v<E> 69 static_assert(!std::is_nothrow_move_assignable_v<std::expected<void, MoveAssignMayThrow>>); 70 71 // !is_nothrow_move_constructible_v<E> 72 static_assert(!std::is_nothrow_move_assignable_v<std::expected<void, MoveCtorMayThrow>>); 73 74 constexpr bool test() { 75 // If this->has_value() && rhs.has_value() is true, no effects. 76 { 77 std::expected<void, int> e1; 78 std::expected<void, int> e2; 79 decltype(auto) x = (e1 = std::move(e2)); 80 static_assert(std::same_as<decltype(x), std::expected<void, int>&>); 81 assert(&x == &e1); 82 assert(e1.has_value()); 83 } 84 85 // Otherwise, if this->has_value() is true, equivalent to: 86 // construct_at(addressof(unex), std::move(rhs.unex)); 87 // has_val = false; 88 { 89 Traced::state state{}; 90 std::expected<void, Traced> e1; 91 std::expected<void, Traced> e2(std::unexpect, state, 5); 92 decltype(auto) x = (e1 = std::move(e2)); 93 static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>); 94 assert(&x == &e1); 95 assert(!e1.has_value()); 96 assert(e1.error().data_ == 5); 97 98 assert(state.moveCtorCalled); 99 } 100 101 // Otherwise, if rhs.has_value() is true, destroys unex and sets has_val to true. 102 { 103 Traced::state state{}; 104 std::expected<void, Traced> e1(std::unexpect, state, 5); 105 std::expected<void, Traced> e2; 106 decltype(auto) x = (e1 = std::move(e2)); 107 static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>); 108 assert(&x == &e1); 109 assert(e1.has_value()); 110 111 assert(state.dtorCalled); 112 } 113 114 // Otherwise, equivalent to unex = rhs.error(). 115 { 116 Traced::state state{}; 117 std::expected<void, Traced> e1(std::unexpect, state, 5); 118 std::expected<void, Traced> e2(std::unexpect, state, 10); 119 decltype(auto) x = (e1 = std::move(e2)); 120 static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>); 121 assert(&x == &e1); 122 assert(!e1.has_value()); 123 assert(e1.error().data_ == 10); 124 125 assert(state.moveAssignCalled); 126 } 127 128 return true; 129 } 130 131 void testException() { 132 #ifndef TEST_HAS_NO_EXCEPTIONS 133 std::expected<void, ThrowOnMoveConstruct> e1(std::in_place); 134 std::expected<void, ThrowOnMoveConstruct> e2(std::unexpect); 135 try { 136 e1 = std::move(e2); 137 assert(false); 138 } catch (Except) { 139 assert(e1.has_value()); 140 } 141 #endif // TEST_HAS_NO_EXCEPTIONS 142 } 143 144 int main(int, char**) { 145 test(); 146 static_assert(test()); 147 testException(); 148 return 0; 149 } 150