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