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// 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 constraints
47static_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 noexcept
56struct 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 noexcept
67static_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 // CheckForInvalidWrites
130 {
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_EXCEPTIONS
156 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_EXCEPTIONS
165}
166
167int main(int, char**) {
168 test();
169 static_assert(test());
170 testException();
171 return 0;
172}
173

source code of libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp