1e356f681SHui Xie //===----------------------------------------------------------------------===// 2*6a54dfbfSLouis Dionne // 3e356f681SHui Xie // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e356f681SHui Xie // See https://llvm.org/LICENSE.txt for license information. 5e356f681SHui Xie // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e356f681SHui Xie // 7e356f681SHui Xie //===----------------------------------------------------------------------===// 8e356f681SHui Xie 9e356f681SHui Xie // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 10e356f681SHui Xie 11e356f681SHui Xie // constexpr expected& operator=(const expected& rhs); 12e356f681SHui Xie // 13e356f681SHui Xie // Effects: 14e356f681SHui Xie // - If this->has_value() && rhs.has_value() is true, equivalent to val = *rhs. 15e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to: 16e356f681SHui Xie // reinit-expected(unex, val, rhs.error()) 17e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to: 18e356f681SHui Xie // reinit-expected(val, unex, *rhs) 19e356f681SHui Xie // - Otherwise, equivalent to unex = rhs.error(). 20e356f681SHui Xie // 21e356f681SHui Xie // - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this; 22e356f681SHui Xie // 23e356f681SHui Xie // Returns: *this. 24e356f681SHui Xie // 25e356f681SHui Xie // Remarks: This operator is defined as deleted unless: 26e356f681SHui Xie // - is_copy_assignable_v<T> is true and 27e356f681SHui Xie // - is_copy_constructible_v<T> is true and 28e356f681SHui Xie // - is_copy_assignable_v<E> is true and 29e356f681SHui Xie // - is_copy_constructible_v<E> is true and 30e356f681SHui Xie // - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true. 31e356f681SHui Xie 32e356f681SHui Xie #include <cassert> 33e356f681SHui Xie #include <concepts> 34e356f681SHui Xie #include <expected> 35e356f681SHui Xie #include <type_traits> 36e356f681SHui Xie #include <utility> 37e356f681SHui Xie 38e356f681SHui Xie #include "../../types.h" 39e356f681SHui Xie #include "test_macros.h" 40e356f681SHui Xie 41e356f681SHui Xie struct NotCopyConstructible { 42e356f681SHui Xie NotCopyConstructible(const NotCopyConstructible&) = delete; 43e356f681SHui Xie NotCopyConstructible& operator=(const NotCopyConstructible&) = default; 44e356f681SHui Xie }; 45e356f681SHui Xie 46e356f681SHui Xie struct NotCopyAssignable { 47e356f681SHui Xie NotCopyAssignable(const NotCopyAssignable&) = default; 48e356f681SHui Xie NotCopyAssignable& operator=(const NotCopyAssignable&) = delete; 49e356f681SHui Xie }; 50e356f681SHui Xie 51e356f681SHui Xie struct MoveMayThrow { 52e356f681SHui Xie MoveMayThrow(MoveMayThrow const&) = default; 53e356f681SHui Xie MoveMayThrow& operator=(const MoveMayThrow&) = default; 54e356f681SHui Xie MoveMayThrow(MoveMayThrow&&) noexcept(false) {} 55e356f681SHui Xie MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; } 56e356f681SHui Xie }; 57e356f681SHui Xie 58e356f681SHui Xie // Test constraints 59e356f681SHui Xie static_assert(std::is_copy_assignable_v<std::expected<int, int>>); 60e356f681SHui Xie 61e356f681SHui Xie // !is_copy_assignable_v<T> 62e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<NotCopyAssignable, int>>); 63e356f681SHui Xie 64e356f681SHui Xie // !is_copy_constructible_v<T> 65e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<NotCopyConstructible, int>>); 66e356f681SHui Xie 67e356f681SHui Xie // !is_copy_assignable_v<E> 68e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<int, NotCopyAssignable>>); 69e356f681SHui Xie 70e356f681SHui Xie // !is_copy_constructible_v<E> 71e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<int, NotCopyConstructible>>); 72e356f681SHui Xie 73e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E> 74e356f681SHui Xie static_assert(std::is_copy_assignable_v<std::expected<MoveMayThrow, int>>); 75e356f681SHui Xie 76e356f681SHui Xie // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 77e356f681SHui Xie static_assert(std::is_copy_assignable_v<std::expected<int, MoveMayThrow>>); 78e356f681SHui Xie 79e356f681SHui Xie // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 80e356f681SHui Xie static_assert(!std::is_copy_assignable_v<std::expected<MoveMayThrow, MoveMayThrow>>); 81e356f681SHui Xie 82e356f681SHui Xie constexpr bool test() { 83e356f681SHui Xie // If this->has_value() && rhs.has_value() is true, equivalent to val = *rhs. 84e356f681SHui Xie { 85e356f681SHui Xie Traced::state oldState{}; 86e356f681SHui Xie Traced::state newState{}; 87e356f681SHui Xie std::expected<Traced, int> e1(std::in_place, oldState, 5); 88e356f681SHui Xie const std::expected<Traced, int> e2(std::in_place, newState, 10); 89e356f681SHui Xie decltype(auto) x = (e1 = e2); 90e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>); 91e356f681SHui Xie assert(&x == &e1); 92e356f681SHui Xie 93e356f681SHui Xie assert(e1.has_value()); 94e356f681SHui Xie assert(e1.value().data_ == 10); 95e356f681SHui Xie assert(oldState.copyAssignCalled); 96e356f681SHui Xie } 97e356f681SHui Xie 98e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to: 99e356f681SHui Xie // reinit-expected(unex, val, rhs.error()) 100e356f681SHui Xie // E move is not noexcept 101e356f681SHui Xie // In this case, it should call the branch 102e356f681SHui Xie // 103e356f681SHui Xie // U tmp(std::move(oldval)); 104e356f681SHui Xie // destroy_at(addressof(oldval)); 105e356f681SHui Xie // try { 106e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 107e356f681SHui Xie // } catch (...) { 108e356f681SHui Xie // construct_at(addressof(oldval), std::move(tmp)); 109e356f681SHui Xie // throw; 110e356f681SHui Xie // } 111e356f681SHui Xie // 112e356f681SHui Xie { 113e356f681SHui Xie TracedNoexcept::state oldState{}; 114e356f681SHui Xie Traced::state newState{}; 115e356f681SHui Xie std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5); 116e356f681SHui Xie const std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10); 117e356f681SHui Xie 118e356f681SHui Xie decltype(auto) x = (e1 = e2); 119e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>); 120e356f681SHui Xie assert(&x == &e1); 121e356f681SHui Xie 122e356f681SHui Xie assert(!e1.has_value()); 123e356f681SHui Xie assert(e1.error().data_ == 10); 124e356f681SHui Xie 125e356f681SHui Xie assert(!oldState.copyAssignCalled); 126e356f681SHui Xie assert(oldState.moveCtorCalled); 127e356f681SHui Xie assert(oldState.dtorCalled); 128e356f681SHui Xie assert(!oldState.copyCtorCalled); 129e356f681SHui Xie assert(newState.copyCtorCalled); 130e356f681SHui Xie assert(!newState.moveCtorCalled); 131e356f681SHui Xie assert(!newState.dtorCalled); 132e356f681SHui Xie } 133e356f681SHui Xie 134e356f681SHui Xie // - Otherwise, if this->has_value() is true, equivalent to: 135e356f681SHui Xie // reinit-expected(unex, val, rhs.error()) 136e356f681SHui Xie // E move is noexcept 137e356f681SHui Xie // In this case, it should call the branch 138e356f681SHui Xie // 139e356f681SHui Xie // destroy_at(addressof(oldval)); 140e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 141e356f681SHui Xie // 142e356f681SHui Xie { 143e356f681SHui Xie Traced::state oldState{}; 144e356f681SHui Xie TracedNoexcept::state newState{}; 145e356f681SHui Xie std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5); 146e356f681SHui Xie const std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10); 147e356f681SHui Xie 148e356f681SHui Xie decltype(auto) x = (e1 = e2); 149e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>); 150e356f681SHui Xie assert(&x == &e1); 151e356f681SHui Xie 152e356f681SHui Xie assert(!e1.has_value()); 153e356f681SHui Xie assert(e1.error().data_ == 10); 154e356f681SHui Xie 155e356f681SHui Xie assert(!oldState.copyAssignCalled); 156e356f681SHui Xie assert(!oldState.moveCtorCalled); 157e356f681SHui Xie assert(oldState.dtorCalled); 158e356f681SHui Xie assert(!oldState.copyCtorCalled); 159e356f681SHui Xie assert(newState.copyCtorCalled); 160e356f681SHui Xie assert(!newState.moveCtorCalled); 161e356f681SHui Xie assert(!newState.dtorCalled); 162e356f681SHui Xie } 163e356f681SHui Xie 164e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to: 165e356f681SHui Xie // reinit-expected(val, unex, *rhs) 166e356f681SHui Xie // T move is not noexcept 167e356f681SHui Xie // In this case, it should call the branch 168e356f681SHui Xie // 169e356f681SHui Xie // U tmp(std::move(oldval)); 170e356f681SHui Xie // destroy_at(addressof(oldval)); 171e356f681SHui Xie // try { 172e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 173e356f681SHui Xie // } catch (...) { 174e356f681SHui Xie // construct_at(addressof(oldval), std::move(tmp)); 175e356f681SHui Xie // throw; 176e356f681SHui Xie // } 177e356f681SHui Xie // 178e356f681SHui Xie { 179e356f681SHui Xie TracedNoexcept::state oldState{}; 180e356f681SHui Xie Traced::state newState{}; 181e356f681SHui Xie std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5); 182e356f681SHui Xie const std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10); 183e356f681SHui Xie 184e356f681SHui Xie decltype(auto) x = (e1 = e2); 185e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>); 186e356f681SHui Xie assert(&x == &e1); 187e356f681SHui Xie 188e356f681SHui Xie assert(e1.has_value()); 189e356f681SHui Xie assert(e1.value().data_ == 10); 190e356f681SHui Xie 191e356f681SHui Xie assert(!oldState.copyAssignCalled); 192e356f681SHui Xie assert(oldState.moveCtorCalled); 193e356f681SHui Xie assert(oldState.dtorCalled); 194e356f681SHui Xie assert(!oldState.copyCtorCalled); 195e356f681SHui Xie assert(newState.copyCtorCalled); 196e356f681SHui Xie assert(!newState.moveCtorCalled); 197e356f681SHui Xie assert(!newState.dtorCalled); 198e356f681SHui Xie } 199e356f681SHui Xie 200e356f681SHui Xie // - Otherwise, if rhs.has_value() is true, equivalent to: 201e356f681SHui Xie // reinit-expected(val, unex, *rhs) 202e356f681SHui Xie // T move is noexcept 203e356f681SHui Xie // In this case, it should call the branch 204e356f681SHui Xie // 205e356f681SHui Xie // destroy_at(addressof(oldval)); 206e356f681SHui Xie // construct_at(addressof(newval), std::forward<Args>(args)...); 207e356f681SHui Xie // 208e356f681SHui Xie { 209e356f681SHui Xie Traced::state oldState{}; 210e356f681SHui Xie TracedNoexcept::state newState{}; 211e356f681SHui Xie std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5); 212e356f681SHui Xie const std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10); 213e356f681SHui Xie 214e356f681SHui Xie decltype(auto) x = (e1 = e2); 215e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>); 216e356f681SHui Xie assert(&x == &e1); 217e356f681SHui Xie 218e356f681SHui Xie assert(e1.has_value()); 219e356f681SHui Xie assert(e1.value().data_ == 10); 220e356f681SHui Xie 221e356f681SHui Xie assert(!oldState.copyAssignCalled); 222e356f681SHui Xie assert(!oldState.moveCtorCalled); 223e356f681SHui Xie assert(oldState.dtorCalled); 224e356f681SHui Xie assert(!oldState.copyCtorCalled); 225e356f681SHui Xie assert(newState.copyCtorCalled); 226e356f681SHui Xie assert(!newState.moveCtorCalled); 227e356f681SHui Xie assert(!newState.dtorCalled); 228e356f681SHui Xie } 229e356f681SHui Xie 230e356f681SHui Xie // Otherwise, equivalent to unex = rhs.error(). 231e356f681SHui Xie { 232e356f681SHui Xie Traced::state oldState{}; 233e356f681SHui Xie Traced::state newState{}; 234e356f681SHui Xie std::expected<int, Traced> e1(std::unexpect, oldState, 5); 235e356f681SHui Xie const std::expected<int, Traced> e2(std::unexpect, newState, 10); 236e356f681SHui Xie decltype(auto) x = (e1 = e2); 237e356f681SHui Xie static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>); 238e356f681SHui Xie assert(&x == &e1); 239e356f681SHui Xie 240e356f681SHui Xie assert(!e1.has_value()); 241e356f681SHui Xie assert(e1.error().data_ == 10); 242e356f681SHui Xie assert(oldState.copyAssignCalled); 243e356f681SHui Xie } 2444f469053SJan Kokemüller 2454f469053SJan Kokemüller // CheckForInvalidWrites 2464f469053SJan Kokemüller { 2474f469053SJan Kokemüller { 2484f469053SJan Kokemüller CheckForInvalidWrites<true> e1(std::unexpect); 2494f469053SJan Kokemüller CheckForInvalidWrites<true> e2; 2504f469053SJan Kokemüller 2514f469053SJan Kokemüller e1 = e2; 2524f469053SJan Kokemüller 2534f469053SJan Kokemüller assert(e1.check()); 2544f469053SJan Kokemüller assert(e2.check()); 2554f469053SJan Kokemüller } 2564f469053SJan Kokemüller { 2574f469053SJan Kokemüller CheckForInvalidWrites<false> e1(std::unexpect); 2584f469053SJan Kokemüller CheckForInvalidWrites<false> e2; 2594f469053SJan Kokemüller 2604f469053SJan Kokemüller e1 = e2; 2614f469053SJan Kokemüller 2624f469053SJan Kokemüller assert(e1.check()); 2634f469053SJan Kokemüller assert(e2.check()); 2644f469053SJan Kokemüller } 2654f469053SJan Kokemüller } 2664f469053SJan Kokemüller 267e356f681SHui Xie return true; 268e356f681SHui Xie } 269e356f681SHui Xie 270e356f681SHui Xie void testException() { 271e356f681SHui Xie #ifndef TEST_HAS_NO_EXCEPTIONS 272e356f681SHui Xie struct ThrowOnCopyMoveMayThrow { 273e356f681SHui Xie ThrowOnCopyMoveMayThrow() = default; 274e356f681SHui Xie ThrowOnCopyMoveMayThrow(const ThrowOnCopyMoveMayThrow&) { throw Except{}; }; 275e356f681SHui Xie ThrowOnCopyMoveMayThrow& operator=(const ThrowOnCopyMoveMayThrow&) = default; 276e356f681SHui Xie ThrowOnCopyMoveMayThrow(ThrowOnCopyMoveMayThrow&&) noexcept(false) {} 277e356f681SHui Xie }; 278e356f681SHui Xie 279e356f681SHui Xie // assign value throw on copy 280e356f681SHui Xie { 281e356f681SHui Xie std::expected<ThrowOnCopyMoveMayThrow, int> e1(std::unexpect, 5); 282e356f681SHui Xie const std::expected<ThrowOnCopyMoveMayThrow, int> e2(std::in_place); 283e356f681SHui Xie try { 284e356f681SHui Xie e1 = e2; 285e356f681SHui Xie assert(false); 286e356f681SHui Xie } catch (Except) { 287e356f681SHui Xie assert(!e1.has_value()); 288e356f681SHui Xie assert(e1.error() == 5); 289e356f681SHui Xie } 290e356f681SHui Xie } 291e356f681SHui Xie 292e356f681SHui Xie // assign error throw on copy 293e356f681SHui Xie { 294e356f681SHui Xie std::expected<int, ThrowOnCopyMoveMayThrow> e1(5); 295e356f681SHui Xie const std::expected<int, ThrowOnCopyMoveMayThrow> e2(std::unexpect); 296e356f681SHui Xie try { 297e356f681SHui Xie e1 = e2; 298e356f681SHui Xie assert(false); 299e356f681SHui Xie } catch (Except) { 300e356f681SHui Xie assert(e1.has_value()); 301e356f681SHui Xie assert(e1.value() == 5); 302e356f681SHui Xie } 303e356f681SHui Xie } 304e356f681SHui Xie #endif // TEST_HAS_NO_EXCEPTIONS 305e356f681SHui Xie } 306e356f681SHui Xie 307e356f681SHui Xie int main(int, char**) { 308e356f681SHui Xie test(); 309e356f681SHui Xie static_assert(test()); 310e356f681SHui Xie testException(); 311e356f681SHui Xie return 0; 312e356f681SHui Xie } 313