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 U = T> 11 // constexpr expected& operator=(U&& v); 12 // 13 // Constraints: 14 // - is_same_v<expected, remove_cvref_t<U>> is false; and 15 // - remove_cvref_t<U> is not a specialization of unexpected; and 16 // - is_constructible_v<T, U> is true; and 17 // - is_assignable_v<T&, U> is true; and 18 // - is_nothrow_constructible_v<T, U> || 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: val = std::forward<U>(v); 23 // - Otherwise, equivalent to: 24 // reinit-expected(val, unex, std::forward<U>(v)); 25 // has_val = true; 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 NotCopyConstructible { 38 NotCopyConstructible(const NotCopyConstructible&) = delete; 39 NotCopyConstructible& operator=(const NotCopyConstructible&) = default; 40 }; 41 42 struct NotCopyAssignable { 43 NotCopyAssignable(const NotCopyAssignable&) = default; 44 NotCopyAssignable& operator=(const NotCopyAssignable&) = delete; 45 }; 46 47 // Test constraints 48 static_assert(std::is_assignable_v<std::expected<int, int>&, int>); 49 50 // is_same_v<expected, remove_cvref_t<U>> 51 // it is true because it covered by the copy assignment 52 static_assert(std::is_assignable_v<std::expected<int, int>&, std::expected<int, int>>); 53 54 // remove_cvref_t<U> is a specialization of unexpected 55 // it is true because it covered the unexpected overload 56 static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>>); 57 58 // !is_constructible_v<T, U> 59 struct NoCtorFromInt { 60 NoCtorFromInt(int) = delete; 61 NoCtorFromInt& operator=(int); 62 }; 63 static_assert(!std::is_assignable_v<std::expected<NoCtorFromInt, int>&, int>); 64 65 // !is_assignable_v<T&, U> 66 struct NoAssignFromInt { 67 explicit NoAssignFromInt(int); 68 NoAssignFromInt& operator=(int) = delete; 69 }; 70 static_assert(!std::is_assignable_v<std::expected<NoAssignFromInt, int>&, int>); 71 72 template <bool moveNoexcept, bool convertNoexcept> 73 struct MaybeNoexcept { 74 explicit MaybeNoexcept(int) noexcept(convertNoexcept); 75 MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept); 76 MaybeNoexcept& operator=(MaybeNoexcept&&) = default; 77 MaybeNoexcept& operator=(int); 78 }; 79 80 // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 81 // is_nothrow_move_constructible_v<E> 82 static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, int>&, int>); 83 84 // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 85 // !is_nothrow_move_constructible_v<E> 86 static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, true>, MaybeNoexcept<false, false>>&, int>); 87 88 // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> && 89 // !is_nothrow_move_constructible_v<E> 90 static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, false>, MaybeNoexcept<false, false>>&, int>); 91 92 // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 93 // !is_nothrow_move_constructible_v<E> 94 static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, int>); 95 96 constexpr bool test() { 97 // If has_value() is true, equivalent to: val = std::forward<U>(v); 98 // Copy 99 { 100 Traced::state oldState{}; 101 Traced::state newState{}; 102 std::expected<Traced, int> e1(std::in_place, oldState, 5); 103 Traced u(newState, 10); 104 decltype(auto) x = (e1 = u); 105 static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>); 106 assert(&x == &e1); 107 108 assert(e1.has_value()); 109 assert(e1.value().data_ == 10); 110 assert(oldState.copyAssignCalled); 111 } 112 113 // If has_value() is true, equivalent to: val = std::forward<U>(v); 114 // Move 115 { 116 Traced::state oldState{}; 117 Traced::state newState{}; 118 std::expected<Traced, int> e1(std::in_place, oldState, 5); 119 Traced u(newState, 10); 120 decltype(auto) x = (e1 = std::move(u)); 121 static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>); 122 assert(&x == &e1); 123 124 assert(e1.has_value()); 125 assert(e1.value().data_ == 10); 126 assert(oldState.moveAssignCalled); 127 } 128 129 // Otherwise, equivalent to: 130 // reinit-expected(val, unex, std::forward<U>(v)); 131 // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 132 // !is_nothrow_move_constructible_v<E> 133 // copy 134 // 135 // In this case, it should call the branch 136 // destroy_at(addressof(oldval)); 137 // construct_at(addressof(newval), std::forward<Args>(args)...); 138 { 139 BothMayThrow::state oldState{}; 140 std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5); 141 const int i = 10; 142 decltype(auto) x = (e1 = i); 143 static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>); 144 assert(&x == &e1); 145 146 assert(e1.has_value()); 147 assert(e1.value().data_ == 10); 148 149 assert(!oldState.copyCtorCalled); 150 assert(!oldState.moveCtorCalled); 151 assert(oldState.dtorCalled); 152 assert(e1.value().copiedFromInt); 153 } 154 155 // Otherwise, equivalent to: 156 // reinit-expected(val, unex, std::forward<U>(v)); 157 // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 158 // !is_nothrow_move_constructible_v<E> 159 // move 160 // 161 // In this case, it should call the branch 162 // destroy_at(addressof(oldval)); 163 // construct_at(addressof(newval), std::forward<Args>(args)...); 164 { 165 BothMayThrow::state oldState{}; 166 std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5); 167 decltype(auto) x = (e1 = 10); 168 static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>); 169 assert(&x == &e1); 170 171 assert(e1.has_value()); 172 assert(e1.value().data_ == 10); 173 174 assert(!oldState.copyCtorCalled); 175 assert(!oldState.moveCtorCalled); 176 assert(oldState.dtorCalled); 177 assert(e1.value().movedFromInt); 178 } 179 180 // Otherwise, equivalent to: 181 // reinit-expected(val, unex, std::forward<U>(v)); 182 // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> && 183 // !is_nothrow_move_constructible_v<E> 184 // copy 185 // 186 // In this case, it should call the branch 187 // T tmp(std::forward<Args>(args)...); 188 // destroy_at(addressof(oldval)); 189 // construct_at(addressof(newval), std::move(tmp)); 190 { 191 BothMayThrow::state oldState{}; 192 std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5); 193 const int i = 10; 194 decltype(auto) x = (e1 = i); 195 static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>); 196 assert(&x == &e1); 197 198 assert(e1.has_value()); 199 assert(e1.value().data_ == 10); 200 201 assert(!oldState.copyCtorCalled); 202 assert(!oldState.moveCtorCalled); 203 assert(oldState.dtorCalled); 204 assert(!e1.value().copiedFromInt); 205 assert(e1.value().movedFromTmp); 206 } 207 208 // Otherwise, equivalent to: 209 // reinit-expected(val, unex, std::forward<U>(v)); 210 // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> && 211 // !is_nothrow_move_constructible_v<E> 212 // move 213 // 214 // In this case, it should call the branch 215 // T tmp(std::forward<Args>(args)...); 216 // destroy_at(addressof(oldval)); 217 // construct_at(addressof(newval), std::move(tmp)); 218 { 219 BothMayThrow::state oldState{}; 220 std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5); 221 decltype(auto) x = (e1 = 10); 222 static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>); 223 assert(&x == &e1); 224 225 assert(e1.has_value()); 226 assert(e1.value().data_ == 10); 227 228 assert(!oldState.copyCtorCalled); 229 assert(!oldState.moveCtorCalled); 230 assert(oldState.dtorCalled); 231 assert(!e1.value().copiedFromInt); 232 assert(e1.value().movedFromTmp); 233 } 234 235 // Otherwise, equivalent to: 236 // reinit-expected(val, unex, std::forward<U>(v)); 237 // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 238 // is_nothrow_move_constructible_v<E> 239 // copy 240 // 241 // In this case, it should call the branch 242 // U tmp(std::move(oldval)); 243 // destroy_at(addressof(oldval)); 244 // try { 245 // construct_at(addressof(newval), std::forward<Args>(args)...); 246 // } catch (...) { 247 // construct_at(addressof(oldval), std::move(tmp)); 248 // throw; 249 // } 250 { 251 TracedNoexcept::state oldState{}; 252 std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5); 253 const int i = 10; 254 decltype(auto) x = (e1 = i); 255 static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>); 256 assert(&x == &e1); 257 258 assert(e1.has_value()); 259 assert(e1.value().data_ == 10); 260 261 assert(!oldState.copyCtorCalled); 262 assert(oldState.moveCtorCalled); 263 assert(oldState.dtorCalled); 264 assert(e1.value().copiedFromInt); 265 } 266 267 // Otherwise, equivalent to: 268 // reinit-expected(val, unex, std::forward<U>(v)); 269 // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> && 270 // is_nothrow_move_constructible_v<E> 271 // move 272 // 273 // In this case, it should call the branch 274 // U tmp(std::move(oldval)); 275 // destroy_at(addressof(oldval)); 276 // try { 277 // construct_at(addressof(newval), std::forward<Args>(args)...); 278 // } catch (...) { 279 // construct_at(addressof(oldval), std::move(tmp)); 280 // throw; 281 // } 282 { 283 TracedNoexcept::state oldState{}; 284 std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5); 285 decltype(auto) x = (e1 = 10); 286 static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>); 287 assert(&x == &e1); 288 289 assert(e1.has_value()); 290 assert(e1.value().data_ == 10); 291 292 assert(!oldState.copyCtorCalled); 293 assert(oldState.moveCtorCalled); 294 assert(oldState.dtorCalled); 295 assert(e1.value().movedFromInt); 296 } 297 298 // Test default template argument. 299 // Without it, the template parameter cannot be deduced from an initializer list 300 { 301 struct Bar { 302 int i; 303 int j; 304 constexpr Bar(int ii, int jj) : i(ii), j(jj) {} 305 }; 306 307 std::expected<Bar, int> e({5, 6}); 308 e = {7, 8}; 309 assert(e.value().i == 7); 310 assert(e.value().j == 8); 311 } 312 313 return true; 314 } 315 316 void testException() { 317 #ifndef TEST_HAS_NO_EXCEPTIONS 318 std::expected<ThrowOnConvert, int> e1(std::unexpect, 5); 319 try { 320 e1 = 10; 321 assert(false); 322 } catch (Except) { 323 assert(!e1.has_value()); 324 assert(e1.error() == 5); 325 } 326 327 #endif // TEST_HAS_NO_EXCEPTIONS 328 } 329 330 int main(int, char**) { 331 test(); 332 static_assert(test()); 333 testException(); 334 return 0; 335 } 336