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