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 G>
11// constexpr expected& operator=(unexpected<G>&& e);
12//
13// Let GF be G
14// Constraints:
15// - is_constructible_v<E, GF> is true; and
16// - is_assignable_v<E&, GF> is true; and
17// - is_nothrow_constructible_v<E, GF> || is_nothrow_move_constructible_v<T> ||
18// is_nothrow_move_constructible_v<E> is true.
19//
20// Effects:
21// - If has_value() is true, equivalent to:
22// reinit-expected(unex, val, std::forward<GF>(e.error()));
23// has_val = false;
24// - Otherwise, equivalent to: unex = std::forward<GF>(e.error());
25// Returns: *this.
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
46struct MoveMayThrow {
47 MoveMayThrow(MoveMayThrow const&) = default;
48 MoveMayThrow& operator=(const MoveMayThrow&) = default;
49 MoveMayThrow(MoveMayThrow&&) noexcept(false) {}
50 MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; }
51};
52
53// Test constraints
54static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>&&>);
55
56// !is_constructible_v<E, GF>
57static_assert(
58 !std::is_assignable_v<std::expected<int, NotMoveConstructible>&, std::unexpected<NotMoveConstructible>&&>);
59
60// !is_assignable_v<E&, GF>
61static_assert(!std::is_assignable_v<std::expected<int, NotMoveAssignable>&, std::unexpected<NotMoveAssignable>&&>);
62
63template <bool moveNoexcept, bool convertNoexcept>
64struct MaybeNoexcept {
65 explicit MaybeNoexcept(int) noexcept(convertNoexcept);
66 MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept);
67 MaybeNoexcept& operator=(MaybeNoexcept&&) = default;
68 MaybeNoexcept& operator=(int);
69};
70
71// !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> &&
72// is_nothrow_move_constructible_v<E>
73static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<true, false>>&,
74 std::unexpected<int>&&>);
75
76// is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> &&
77// !is_nothrow_move_constructible_v<E>
78static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, true>>&,
79 std::unexpected<int>&&>);
80
81// !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<T> &&
82// !is_nothrow_move_constructible_v<E>
83static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, true>, MaybeNoexcept<false, false>>&,
84 std::unexpected<int>&&>);
85
86// !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> &&
87// !is_nothrow_move_constructible_v<E>
88static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&,
89 std::unexpected<int>&&>);
90
91constexpr bool test() {
92 // - If has_value() is true, equivalent to:
93 // reinit-expected(unex, val, std::forward<GF>(e.error()));
94 // is_nothrow_constructible_v<E, GF>
95 //
96 // In this case, it should call the branch
97 // destroy_at(addressof(oldval));
98 // construct_at(addressof(newval), std::forward<Args>(args)...);
99 {
100 BothNoexcept::state oldState{};
101 std::expected<BothNoexcept, BothNoexcept> e(std::in_place, oldState, 5);
102 std::unexpected<int> un(10);
103 decltype(auto) x = (e = std::move(un));
104 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothNoexcept>&>);
105 assert(&x == &e);
106
107 assert(!oldState.moveCtorCalled);
108 assert(oldState.dtorCalled);
109 assert(e.error().movedFromInt);
110 }
111
112 // - If has_value() is true, equivalent to:
113 // reinit-expected(unex, val, std::forward<GF>(e.error()));
114 // !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<E>
115 //
116 // In this case, it should call the branch
117 // T tmp(std::forward<Args>(args)...);
118 // destroy_at(addressof(oldval));
119 // construct_at(addressof(newval), std::move(tmp));
120 {
121 BothNoexcept::state oldState{};
122 std::expected<BothNoexcept, MoveNoexceptConvThrow> e(std::in_place, oldState, 5);
123 std::unexpected<int> un(10);
124 decltype(auto) x = (e = std::move(un));
125 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, MoveNoexceptConvThrow>&>);
126 assert(&x == &e);
127
128 assert(!oldState.moveCtorCalled);
129 assert(oldState.dtorCalled);
130 assert(!e.error().movedFromInt);
131 assert(e.error().movedFromTmp);
132 }
133
134 // - If has_value() is true, equivalent to:
135 // reinit-expected(unex, val, std::forward<GF>(e.error()));
136 // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<E>
137 // is_nothrow_move_constructible_v<T>
138 //
139 // In this case, it should call the branch
140 // U tmp(std::move(oldval));
141 // destroy_at(addressof(oldval));
142 // try {
143 // construct_at(addressof(newval), std::forward<Args>(args)...);
144 // } catch (...) {
145 // construct_at(addressof(oldval), std::move(tmp));
146 // throw;
147 // }
148 {
149 BothNoexcept::state oldState{};
150 std::expected<BothNoexcept, BothMayThrow> e(std::in_place, oldState, 5);
151 std::unexpected<int> un(10);
152 decltype(auto) x = (e = std::move(un));
153 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothMayThrow>&>);
154 assert(&x == &e);
155
156 assert(oldState.moveCtorCalled);
157 assert(oldState.dtorCalled);
158 assert(e.error().movedFromInt);
159 }
160
161 // Otherwise, equivalent to: unex = std::forward<GF>(e.error());
162 {
163 Traced::state oldState{};
164 Traced::state newState{};
165 std::expected<int, Traced> e1(std::unexpect, oldState, 5);
166 std::unexpected<Traced> e(std::in_place, newState, 10);
167 decltype(auto) x = (e1 = std::move(e));
168 static_assert(std::same_as<decltype(x), std::expected<int, Traced >&>);
169 assert(&x == &e1);
170
171 assert(!e1.has_value());
172 assert(e1.error().data_ == 10);
173 assert(oldState.moveAssignCalled);
174 }
175
176 // CheckForInvalidWrites
177 {
178 {
179 CheckForInvalidWrites<true, true> e;
180 std::unexpected<int> un(std::in_place, 42);
181 e = std::move(un);
182 assert(e.check());
183 }
184 {
185 CheckForInvalidWrites<false, true> e;
186 std::unexpected<bool> un(std::in_place, true);
187 e = std::move(un);
188 assert(e.check());
189 }
190 }
191
192 return true;
193}
194
195void testException() {
196#ifndef TEST_HAS_NO_EXCEPTIONS
197 std::expected<void, ThrowOnMoveConstruct> e1(std::in_place);
198 std::unexpected<ThrowOnMoveConstruct> un(std::in_place);
199 try {
200 e1 = std::move(un);
201 assert(false);
202 } catch (Except) {
203 assert(e1.has_value());
204 }
205#endif // TEST_HAS_NO_EXCEPTIONS
206}
207
208int main(int, char**) {
209 test();
210 static_assert(test());
211 testException();
212 return 0;
213}
214

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