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