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