173 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++2010 11// constexpr expected& operator=(expected&& rhs) noexcept(see below);12//13// Effects:14// - If this->has_value() && rhs.has_value() is true, no effects.15// - Otherwise, if this->has_value() is true, equivalent to:16// construct_at(addressof(unex), std::move(rhs.unex));17// has_val = false;18// - Otherwise, if rhs.has_value() is true, destroys unex and sets has_val to true.19// - Otherwise, equivalent to unex = std::move(rhs.error()).20//21// Returns: *this.22//23// Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<E> && is_nothrow_move_assignable_v<E>.24//25// This operator is defined as deleted unless is_move_constructible_v<E> is true and is_move_assignable_v<E> is true.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 36struct NotMoveConstructible {37 NotMoveConstructible(NotMoveConstructible&&) = delete;38 NotMoveConstructible& operator=(NotMoveConstructible&&) = default;39};40 41struct NotMoveAssignable {42 NotMoveAssignable(NotMoveAssignable&&) = default;43 NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;44};45 46// Test constraints47static_assert(std::is_move_assignable_v<std::expected<void, int>>);48 49// !is_move_assignable_v<E>50static_assert(!std::is_move_assignable_v<std::expected<void, NotMoveAssignable>>);51 52// !is_move_constructible_v<E>53static_assert(!std::is_move_assignable_v<std::expected<void, NotMoveConstructible>>);54 55// Test noexcept56struct MoveCtorMayThrow {57 MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {}58 MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default;59};60 61struct MoveAssignMayThrow {62 MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default;63 MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; }64};65 66// Test noexcept67static_assert(std::is_nothrow_move_assignable_v<std::expected<void, int>>);68 69// !is_nothrow_move_assignable_v<E>70static_assert(!std::is_nothrow_move_assignable_v<std::expected<void, MoveAssignMayThrow>>);71 72// !is_nothrow_move_constructible_v<E>73static_assert(!std::is_nothrow_move_assignable_v<std::expected<void, MoveCtorMayThrow>>);74 75constexpr bool test() {76 // If this->has_value() && rhs.has_value() is true, no effects.77 {78 std::expected<void, int> e1;79 std::expected<void, int> e2;80 decltype(auto) x = (e1 = std::move(e2));81 static_assert(std::same_as<decltype(x), std::expected<void, int>&>);82 assert(&x == &e1);83 assert(e1.has_value());84 }85 86 // Otherwise, if this->has_value() is true, equivalent to:87 // construct_at(addressof(unex), std::move(rhs.unex));88 // has_val = false;89 {90 Traced::state state{};91 std::expected<void, Traced> e1;92 std::expected<void, Traced> e2(std::unexpect, state, 5);93 decltype(auto) x = (e1 = std::move(e2));94 static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>);95 assert(&x == &e1);96 assert(!e1.has_value());97 assert(e1.error().data_ == 5);98 99 assert(state.moveCtorCalled);100 }101 102 // Otherwise, if rhs.has_value() is true, destroys unex and sets has_val to true.103 {104 Traced::state state{};105 std::expected<void, Traced> e1(std::unexpect, state, 5);106 std::expected<void, Traced> e2;107 decltype(auto) x = (e1 = std::move(e2));108 static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>);109 assert(&x == &e1);110 assert(e1.has_value());111 112 assert(state.dtorCalled);113 }114 115 // Otherwise, equivalent to unex = rhs.error().116 {117 Traced::state state{};118 std::expected<void, Traced> e1(std::unexpect, state, 5);119 std::expected<void, Traced> e2(std::unexpect, state, 10);120 decltype(auto) x = (e1 = std::move(e2));121 static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>);122 assert(&x == &e1);123 assert(!e1.has_value());124 assert(e1.error().data_ == 10);125 126 assert(state.moveAssignCalled);127 }128 129 // CheckForInvalidWrites130 {131 {132 CheckForInvalidWrites<true, true> e1;133 CheckForInvalidWrites<true, true> e2(std::unexpect);134 135 e1 = std::move(e2);136 137 assert(e1.check());138 assert(e2.check());139 }140 {141 CheckForInvalidWrites<false, true> e1;142 CheckForInvalidWrites<false, true> e2(std::unexpect);143 144 e1 = std::move(e2);145 146 assert(e1.check());147 assert(e2.check());148 }149 }150 151 return true;152}153 154void testException() {155#ifndef TEST_HAS_NO_EXCEPTIONS156 std::expected<void, ThrowOnMoveConstruct> e1(std::in_place);157 std::expected<void, ThrowOnMoveConstruct> e2(std::unexpect);158 try {159 e1 = std::move(e2);160 assert(false);161 } catch (Except) {162 assert(e1.has_value());163 }164#endif // TEST_HAS_NO_EXCEPTIONS165}166 167int main(int, char**) {168 test();169 static_assert(test());170 testException();171 return 0;172}173