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 
36 struct NotCopyConstructible {
37   NotCopyConstructible(const NotCopyConstructible&)            = delete;
38   NotCopyConstructible& operator=(const NotCopyConstructible&) = default;
39 };
40 
41 struct NotCopyAssignable {
42   NotCopyAssignable(const NotCopyAssignable&)            = default;
43   NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;
44 };
45 
46 struct 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
54 static_assert(std::is_assignable_v<std::expected<int, int>&, const std::unexpected<int>&>);
55 
56 // !is_constructible_v<E, GF>
57 static_assert(
58     !std::is_assignable_v<std::expected<int, NotCopyConstructible>&, const std::unexpected<NotCopyConstructible>&>);
59 
60 // !is_assignable_v<E&, GF>
61 static_assert(!std::is_assignable_v<std::expected<int, NotCopyAssignable>&, const std::unexpected<NotCopyAssignable>&>);
62 
63 template <bool moveNoexcept, bool convertNoexcept>
64 struct 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>
73 static_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>
78 static_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>
83 static_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>
88 static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&,
89                                     const std::unexpected<int>&>);
90 
91 constexpr 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 
178 void 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 
192 int main(int, char**) {
193   test();
194   static_assert(test());
195   testException();
196   return 0;
197 }
198