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