xref: /llvm-project/libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
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 // constexpr expected& operator=(expected&& rhs) noexcept(see below);
12 //
13 // Effects:
14 // - If this->has_value() && rhs.has_value() is true, no effects.
15 // - Otherwise, if this->has_value() is true, equivalent to:
16 //   construct_at(addressof(unex), std::move(rhs.unex));
17 //   has_val = false;
18 // - Otherwise, if rhs.has_value() is true, destroys unex and sets has_val to true.
19 // - Otherwise, equivalent to unex = std::move(rhs.error()).
20 //
21 // Returns: *this.
22 //
23 // Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<E> && is_nothrow_move_assignable_v<E>.
24 //
25 // This operator is defined as deleted unless is_move_constructible_v<E> is true and is_move_assignable_v<E> is true.
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 NotMoveConstructible {
37   NotMoveConstructible(NotMoveConstructible&&)            = delete;
38   NotMoveConstructible& operator=(NotMoveConstructible&&) = default;
39 };
40 
41 struct NotMoveAssignable {
42   NotMoveAssignable(NotMoveAssignable&&)            = default;
43   NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;
44 };
45 
46 // Test constraints
47 static_assert(std::is_move_assignable_v<std::expected<void, int>>);
48 
49 // !is_move_assignable_v<E>
50 static_assert(!std::is_move_assignable_v<std::expected<void, NotMoveAssignable>>);
51 
52 // !is_move_constructible_v<E>
53 static_assert(!std::is_move_assignable_v<std::expected<void, NotMoveConstructible>>);
54 
55 // Test noexcept
56 struct MoveCtorMayThrow {
57   MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {}
58   MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default;
59 };
60 
61 struct MoveAssignMayThrow {
62   MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default;
63   MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; }
64 };
65 
66 // Test noexcept
67 static_assert(std::is_nothrow_move_assignable_v<std::expected<void, int>>);
68 
69 // !is_nothrow_move_assignable_v<E>
70 static_assert(!std::is_nothrow_move_assignable_v<std::expected<void, MoveAssignMayThrow>>);
71 
72 // !is_nothrow_move_constructible_v<E>
73 static_assert(!std::is_nothrow_move_assignable_v<std::expected<void, MoveCtorMayThrow>>);
74 
75 constexpr bool test() {
76   // If this->has_value() && rhs.has_value() is true, no effects.
77   {
78     std::expected<void, int> e1;
79     std::expected<void, int> e2;
80     decltype(auto) x = (e1 = std::move(e2));
81     static_assert(std::same_as<decltype(x), std::expected<void, int>&>);
82     assert(&x == &e1);
83     assert(e1.has_value());
84   }
85 
86   // Otherwise, if this->has_value() is true, equivalent to:
87   // construct_at(addressof(unex), std::move(rhs.unex));
88   // has_val = false;
89   {
90     Traced::state state{};
91     std::expected<void, Traced> e1;
92     std::expected<void, Traced> e2(std::unexpect, state, 5);
93     decltype(auto) x = (e1 = std::move(e2));
94     static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>);
95     assert(&x == &e1);
96     assert(!e1.has_value());
97     assert(e1.error().data_ == 5);
98 
99     assert(state.moveCtorCalled);
100   }
101 
102   // Otherwise, if rhs.has_value() is true, destroys unex and sets has_val to true.
103   {
104     Traced::state state{};
105     std::expected<void, Traced> e1(std::unexpect, state, 5);
106     std::expected<void, Traced> e2;
107     decltype(auto) x = (e1 = std::move(e2));
108     static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>);
109     assert(&x == &e1);
110     assert(e1.has_value());
111 
112     assert(state.dtorCalled);
113   }
114 
115   // Otherwise, equivalent to unex = rhs.error().
116   {
117     Traced::state state{};
118     std::expected<void, Traced> e1(std::unexpect, state, 5);
119     std::expected<void, Traced> e2(std::unexpect, state, 10);
120     decltype(auto) x = (e1 = std::move(e2));
121     static_assert(std::same_as<decltype(x), std::expected<void, Traced>&>);
122     assert(&x == &e1);
123     assert(!e1.has_value());
124     assert(e1.error().data_ == 10);
125 
126     assert(state.moveAssignCalled);
127   }
128 
129   // CheckForInvalidWrites
130   {
131     {
132       CheckForInvalidWrites<true, true> e1;
133       CheckForInvalidWrites<true, true> e2(std::unexpect);
134 
135       e1 = std::move(e2);
136 
137       assert(e1.check());
138       assert(e2.check());
139     }
140     {
141       CheckForInvalidWrites<false, true> e1;
142       CheckForInvalidWrites<false, true> e2(std::unexpect);
143 
144       e1 = std::move(e2);
145 
146       assert(e1.check());
147       assert(e2.check());
148     }
149   }
150 
151   return true;
152 }
153 
154 void testException() {
155 #ifndef TEST_HAS_NO_EXCEPTIONS
156   std::expected<void, ThrowOnMoveConstruct> e1(std::in_place);
157   std::expected<void, ThrowOnMoveConstruct> e2(std::unexpect);
158   try {
159     e1 = std::move(e2);
160     assert(false);
161   } catch (Except) {
162     assert(e1.has_value());
163   }
164 #endif // TEST_HAS_NO_EXCEPTIONS
165 }
166 
167 int main(int, char**) {
168   test();
169   static_assert(test());
170   testException();
171   return 0;
172 }
173