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=(const unexpected<G>& e);
12//
13// Let GF be const 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 NotCopyConstructible {
37 NotCopyConstructible(const NotCopyConstructible&) = delete;
38 NotCopyConstructible& operator=(const NotCopyConstructible&) = default;
39};
40
41struct NotCopyAssignable {
42 NotCopyAssignable(const NotCopyAssignable&) = default;
43 NotCopyAssignable& operator=(const NotCopyAssignable&) = 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>&, const std::unexpected<int>&>);
55
56// !is_constructible_v<E, GF>
57static_assert(
58 !std::is_assignable_v<std::expected<int, NotCopyConstructible>&, const std::unexpected<NotCopyConstructible>&>);
59
60// !is_assignable_v<E&, GF>
61static_assert(!std::is_assignable_v<std::expected<int, NotCopyAssignable>&, const std::unexpected<NotCopyAssignable>&>);
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 const 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 const 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 const 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 const 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 const std::unexpected<int> un(10);
103 decltype(auto) x = (e = 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().copiedFromInt);
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 const std::unexpected<int> un(10);
124 decltype(auto) x = (e = 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().copiedFromInt);
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 const std::unexpected<int> un(10);
152 decltype(auto) x = (e = 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().copiedFromInt);
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 const std::unexpected<Traced> e(std::in_place, newState, 10);
167 decltype(auto) x = (e1 = 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.copyAssignCalled);
174 }
175 return true;
176}
177
178void testException() {
179#ifndef TEST_HAS_NO_EXCEPTIONS
180 std::expected<int, ThrowOnConvert> e1(std::in_place, 5);
181 const std::unexpected<int> un(10);
182 try {
183 e1 = un;
184 assert(false);
185 } catch (Except) {
186 assert(e1.has_value());
187 assert(*e1 == 5);
188 }
189#endif // TEST_HAS_NO_EXCEPTIONS
190}
191
192int main(int, char**) {
193 test();
194 static_assert(test());
195 testException();
196 return 0;
197}
198

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