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(expected&& rhs) noexcept(see below); |
12 | // |
13 | // Constraints: |
14 | // - is_move_constructible_v<T> is true and |
15 | // - is_move_constructible_v<E> is true. |
16 | // |
17 | // Effects: If rhs.has_value() is true, direct-non-list-initializes val with std::move(*rhs). |
18 | // Otherwise, direct-non-list-initializes unex with std::move(rhs.error()). |
19 | // |
20 | // Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true. |
21 | // |
22 | // Throws: Any exception thrown by the initialization of val or unex. |
23 | // |
24 | // Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>. |
25 | // |
26 | // This constructor is trivial if |
27 | // - is_trivially_move_constructible_v<T> is true and |
28 | // - is_trivially_move_constructible_v<E> is true. |
29 | |
30 | #include <cassert> |
31 | #include <expected> |
32 | #include <type_traits> |
33 | #include <utility> |
34 | |
35 | #include "test_macros.h" |
36 | #include "../../types.h" |
37 | |
38 | struct NonMovable { |
39 | NonMovable(NonMovable&&) = delete; |
40 | }; |
41 | |
42 | struct MovableNonTrivial { |
43 | int i; |
44 | constexpr MovableNonTrivial(int ii) : i(ii) {} |
45 | constexpr MovableNonTrivial(MovableNonTrivial&& o) : i(o.i) { o.i = 0; } |
46 | friend constexpr bool operator==(const MovableNonTrivial&, const MovableNonTrivial&) = default; |
47 | }; |
48 | |
49 | struct MoveMayThrow { |
50 | MoveMayThrow(MoveMayThrow&&) {} |
51 | }; |
52 | |
53 | // Test Constraints: |
54 | // - is_move_constructible_v<T> is true and |
55 | // - is_move_constructible_v<E> is true. |
56 | static_assert(std::is_move_constructible_v<std::expected<int, int>>); |
57 | static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, int>>); |
58 | static_assert(std::is_move_constructible_v<std::expected<int, MovableNonTrivial>>); |
59 | static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>); |
60 | static_assert(!std::is_move_constructible_v<std::expected<NonMovable, int>>); |
61 | static_assert(!std::is_move_constructible_v<std::expected<int, NonMovable>>); |
62 | static_assert(!std::is_move_constructible_v<std::expected<NonMovable, NonMovable>>); |
63 | |
64 | // Test: This constructor is trivial if |
65 | // - is_trivially_move_constructible_v<T> is true and |
66 | // - is_trivially_move_constructible_v<E> is true. |
67 | static_assert(std::is_trivially_move_constructible_v<std::expected<int, int>>); |
68 | static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, int>>); |
69 | static_assert(!std::is_trivially_move_constructible_v<std::expected<int, MovableNonTrivial>>); |
70 | static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>); |
71 | |
72 | // Test: The exception specification is equivalent to |
73 | // is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>. |
74 | static_assert(std::is_nothrow_move_constructible_v<std::expected<int, int>>); |
75 | static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, int>>); |
76 | static_assert(!std::is_nothrow_move_constructible_v<std::expected<int, MoveMayThrow>>); |
77 | static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, MoveMayThrow>>); |
78 | |
79 | constexpr bool test() { |
80 | // move the value non-trivial |
81 | { |
82 | std::expected<MovableNonTrivial, int> e1(5); |
83 | auto e2 = std::move(e1); |
84 | assert(e2.has_value()); |
85 | assert(e2.value().i == 5); |
86 | assert(e1.has_value()); |
87 | assert(e1.value().i == 0); |
88 | } |
89 | |
90 | // move the error non-trivial |
91 | { |
92 | std::expected<int, MovableNonTrivial> e1(std::unexpect, 5); |
93 | auto e2 = std::move(e1); |
94 | assert(!e2.has_value()); |
95 | assert(e2.error().i == 5); |
96 | assert(!e1.has_value()); |
97 | assert(e1.error().i == 0); |
98 | } |
99 | |
100 | // move the value trivial |
101 | { |
102 | std::expected<int, int> e1(5); |
103 | auto e2 = std::move(e1); |
104 | assert(e2.has_value()); |
105 | assert(e2.value() == 5); |
106 | assert(e1.has_value()); |
107 | } |
108 | |
109 | // move the error trivial |
110 | { |
111 | std::expected<int, int> e1(std::unexpect, 5); |
112 | auto e2 = std::move(e1); |
113 | assert(!e2.has_value()); |
114 | assert(e2.error() == 5); |
115 | assert(!e1.has_value()); |
116 | } |
117 | |
118 | // move TailClobbererNonTrivialMove as value |
119 | { |
120 | std::expected<TailClobbererNonTrivialMove<0>, bool> e1; |
121 | auto e2 = std::move(e1); |
122 | assert(e2.has_value()); |
123 | assert(e1.has_value()); |
124 | } |
125 | |
126 | // move TailClobbererNonTrivialMove as error |
127 | { |
128 | std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect); |
129 | auto e2 = std::move(e1); |
130 | assert(!e2.has_value()); |
131 | assert(!e1.has_value()); |
132 | } |
133 | |
134 | return true; |
135 | } |
136 | |
137 | void testException() { |
138 | #ifndef TEST_HAS_NO_EXCEPTIONS |
139 | struct Throwing { |
140 | Throwing() = default; |
141 | Throwing(Throwing&&) { throw Except{}; } |
142 | }; |
143 | |
144 | // throw on moving value |
145 | { |
146 | std::expected<Throwing, int> e1; |
147 | try { |
148 | [[maybe_unused]] auto e2 = std::move(e1); |
149 | assert(false); |
150 | } catch (Except) { |
151 | } |
152 | } |
153 | |
154 | // throw on moving error |
155 | { |
156 | std::expected<int, Throwing> e1(std::unexpect); |
157 | try { |
158 | [[maybe_unused]] auto e2 = std::move(e1); |
159 | assert(false); |
160 | } catch (Except) { |
161 | } |
162 | } |
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 | |