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