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 // template<class G> 12 // constexpr expected& operator=(unexpected<G>&& e); 13 // 14 // Let GF be G 15 // Constraints: 16 // - is_constructible_v<E, GF> is true; and 17 // - is_assignable_v<E&, GF> is true; and 18 // - is_nothrow_constructible_v<E, GF> || is_nothrow_move_constructible_v<T> || 19 // is_nothrow_move_constructible_v<E> is true. 20 // 21 // Effects: 22 // - If has_value() is true, equivalent to: 23 // reinit-expected(unex, val, std::forward<GF>(e.error())); 24 // has_val = false; 25 // - Otherwise, equivalent to: unex = std::forward<GF>(e.error()); 26 // Returns: *this. 27 28 #include <cassert> 29 #include <concepts> 30 #include <expected> 31 #include <type_traits> 32 #include <utility> 33 34 #include "../../types.h" 35 #include "test_macros.h" 36 37 struct NotMoveConstructible { 38 NotMoveConstructible(NotMoveConstructible&&) = delete; 39 NotMoveConstructible& operator=(NotMoveConstructible&&) = default; 40 }; 41 42 struct NotMoveAssignable { 43 NotMoveAssignable(NotMoveAssignable&&) = default; 44 NotMoveAssignable& operator=(NotMoveAssignable&&) = delete; 45 }; 46 47 struct MoveMayThrow { 48 MoveMayThrow(MoveMayThrow const&) = default; 49 MoveMayThrow& operator=(const MoveMayThrow&) = default; 50 MoveMayThrow(MoveMayThrow&&) noexcept(false) {} 51 MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; } 52 }; 53 54 // Test constraints 55 static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>&&>); 56 57 // !is_constructible_v<E, GF> 58 static_assert( 59 !std::is_assignable_v<std::expected<int, NotMoveConstructible>&, std::unexpected<NotMoveConstructible>&&>); 60 61 // !is_assignable_v<E&, GF> 62 static_assert(!std::is_assignable_v<std::expected<int, NotMoveAssignable>&, std::unexpected<NotMoveAssignable>&&>); 63 64 template <bool moveNoexcept, bool convertNoexcept> 65 struct MaybeNoexcept { 66 explicit MaybeNoexcept(int) noexcept(convertNoexcept); 67 MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept); 68 MaybeNoexcept& operator=(MaybeNoexcept&&) = default; 69 MaybeNoexcept& operator=(int); 70 }; 71 72 // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> && 73 // is_nothrow_move_constructible_v<E> 74 static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<true, false>>&, 75 std::unexpected<int>&&>); 76 77 // is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> && 78 // !is_nothrow_move_constructible_v<E> 79 static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, true>>&, 80 std::unexpected<int>&&>); 81 82 // !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<T> && 83 // !is_nothrow_move_constructible_v<E> 84 static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, true>, MaybeNoexcept<false, false>>&, 85 std::unexpected<int>&&>); 86 87 // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> && 88 // !is_nothrow_move_constructible_v<E> 89 static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, 90 std::unexpected<int>&&>); 91 92 constexpr bool test() { 93 // - If has_value() is true, equivalent to: 94 // reinit-expected(unex, val, std::forward<GF>(e.error())); 95 // is_nothrow_constructible_v<E, GF> 96 // 97 // In this case, it should call the branch 98 // destroy_at(addressof(oldval)); 99 // construct_at(addressof(newval), std::forward<Args>(args)...); 100 { 101 BothNoexcept::state oldState{}; 102 std::expected<BothNoexcept, BothNoexcept> e(std::in_place, oldState, 5); 103 std::unexpected<int> un(10); 104 decltype(auto) x = (e = std::move(un)); 105 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothNoexcept>&>); 106 assert(&x == &e); 107 108 assert(!oldState.moveCtorCalled); 109 assert(oldState.dtorCalled); 110 assert(e.error().movedFromInt); 111 } 112 113 // - If has_value() is true, equivalent to: 114 // reinit-expected(unex, val, std::forward<GF>(e.error())); 115 // !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<E> 116 // 117 // In this case, it should call the branch 118 // T tmp(std::forward<Args>(args)...); 119 // destroy_at(addressof(oldval)); 120 // construct_at(addressof(newval), std::move(tmp)); 121 { 122 BothNoexcept::state oldState{}; 123 std::expected<BothNoexcept, MoveNoexceptConvThrow> e(std::in_place, oldState, 5); 124 std::unexpected<int> un(10); 125 decltype(auto) x = (e = std::move(un)); 126 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, MoveNoexceptConvThrow>&>); 127 assert(&x == &e); 128 129 assert(!oldState.moveCtorCalled); 130 assert(oldState.dtorCalled); 131 assert(!e.error().movedFromInt); 132 assert(e.error().movedFromTmp); 133 } 134 135 // - If has_value() is true, equivalent to: 136 // reinit-expected(unex, val, std::forward<GF>(e.error())); 137 // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<E> 138 // is_nothrow_move_constructible_v<T> 139 // 140 // In this case, it should call the branch 141 // U tmp(std::move(oldval)); 142 // destroy_at(addressof(oldval)); 143 // try { 144 // construct_at(addressof(newval), std::forward<Args>(args)...); 145 // } catch (...) { 146 // construct_at(addressof(oldval), std::move(tmp)); 147 // throw; 148 // } 149 { 150 BothNoexcept::state oldState{}; 151 std::expected<BothNoexcept, BothMayThrow> e(std::in_place, oldState, 5); 152 std::unexpected<int> un(10); 153 decltype(auto) x = (e = std::move(un)); 154 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothMayThrow>&>); 155 assert(&x == &e); 156 157 assert(oldState.moveCtorCalled); 158 assert(oldState.dtorCalled); 159 assert(e.error().movedFromInt); 160 } 161 162 // Otherwise, equivalent to: unex = std::forward<GF>(e.error()); 163 { 164 Traced::state oldState{}; 165 Traced::state newState{}; 166 std::expected<int, Traced> e1(std::unexpect, oldState, 5); 167 std::unexpected<Traced> e(std::in_place, newState, 10); 168 decltype(auto) x = (e1 = std::move(e)); 169 static_assert(std::same_as<decltype(x), std::expected<int, Traced >&>); 170 assert(&x == &e1); 171 172 assert(!e1.has_value()); 173 assert(e1.error().data_ == 10); 174 assert(oldState.moveAssignCalled); 175 } 176 return true; 177 } 178 179 void testException() { 180 #ifndef TEST_HAS_NO_EXCEPTIONS 181 std::expected<int, ThrowOnConvert> e1(std::in_place, 5); 182 std::unexpected<int> un(10); 183 try { 184 e1 = std::move(un); 185 assert(false); 186 } catch (Except) { 187 assert(e1.has_value()); 188 assert(*e1 == 5); 189 } 190 #endif // TEST_HAS_NO_EXCEPTIONS 191 } 192 193 int main(int, char**) { 194 test(); 195 static_assert(test()); 196 testException(); 197 return 0; 198 } 199