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 | // GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`, |
12 | // please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333. |
13 | // XFAIL: gcc-13 |
14 | |
15 | // <expected> |
16 | |
17 | // template<class F> constexpr auto transform_error(F&& f) &; |
18 | // template<class F> constexpr auto transform_error(F&& f) const &; |
19 | // template<class F> constexpr auto transform_error(F&& f) &&; |
20 | // template<class F> constexpr auto transform_error(F&& f) const &&; |
21 | |
22 | #include <expected> |
23 | #include <concepts> |
24 | #include <cassert> |
25 | #include <memory> |
26 | #include <type_traits> |
27 | #include <utility> |
28 | |
29 | #include "../../types.h" |
30 | |
31 | struct LVal { |
32 | constexpr int operator()(int&) { return 1; } |
33 | int operator()(const int&) = delete; |
34 | int operator()(int&&) = delete; |
35 | int operator()(const int&&) = delete; |
36 | }; |
37 | |
38 | struct CLVal { |
39 | int operator()(int&) = delete; |
40 | constexpr int operator()(const int&) { return 1; } |
41 | int operator()(int&&) = delete; |
42 | int operator()(const int&&) = delete; |
43 | }; |
44 | |
45 | struct RVal { |
46 | int operator()(int&) = delete; |
47 | int operator()(const int&) = delete; |
48 | constexpr int operator()(int&&) { return 1; } |
49 | int operator()(const int&&) = delete; |
50 | }; |
51 | |
52 | struct CRVal { |
53 | int operator()(int&) = delete; |
54 | int operator()(const int&) = delete; |
55 | int operator()(int&&) = delete; |
56 | constexpr int operator()(const int&&) { return 1; } |
57 | }; |
58 | |
59 | struct RefQual { |
60 | constexpr int operator()(int) & { return 1; } |
61 | int operator()(int) const& = delete; |
62 | int operator()(int) && = delete; |
63 | int operator()(int) const&& = delete; |
64 | }; |
65 | |
66 | struct CRefQual { |
67 | int operator()(int) & = delete; |
68 | constexpr int operator()(int) const& { return 1; } |
69 | int operator()(int) && = delete; |
70 | int operator()(int) const&& = delete; |
71 | }; |
72 | |
73 | struct RVRefQual { |
74 | int operator()(int) & = delete; |
75 | int operator()(int) const& = delete; |
76 | constexpr int operator()(int) && { return 1; } |
77 | int operator()(int) const&& = delete; |
78 | }; |
79 | |
80 | struct RVCRefQual { |
81 | int operator()(int) & = delete; |
82 | int operator()(int) const& = delete; |
83 | int operator()(int) && = delete; |
84 | constexpr int operator()(int) const&& { return 1; } |
85 | }; |
86 | |
87 | struct NonCopy { |
88 | int value; |
89 | constexpr explicit NonCopy(int val) : value(val) {} |
90 | NonCopy(const NonCopy&) = delete; |
91 | }; |
92 | |
93 | struct NonConst { |
94 | int non_const() { return 1; } |
95 | }; |
96 | |
97 | template <class E, class F> |
98 | concept has_transform_error = |
99 | requires(E&& e, F&& f) { |
100 | { std::forward<E>(e).transform_error(std::forward<F>(f)) }; |
101 | }; |
102 | |
103 | // clang-format off |
104 | // [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. |
105 | static_assert(!has_transform_error<const std::expected<std::unique_ptr<int>, int>&, int()>); |
106 | static_assert(!has_transform_error<const std::expected<std::unique_ptr<int>, int>&&, int()>); |
107 | |
108 | // [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type. |
109 | static_assert(has_transform_error<std::expected<int, MoveOnlyErrorType>&, int(MoveOnlyErrorType &)>); |
110 | static_assert(has_transform_error<const std::expected<int, MoveOnlyErrorType>&, int(const MoveOnlyErrorType &)>); |
111 | static_assert(has_transform_error<std::expected<int, MoveOnlyErrorType>&&, int(MoveOnlyErrorType&&)>); |
112 | static_assert(has_transform_error<const std::expected<int, MoveOnlyErrorType>&&, int(const MoveOnlyErrorType&&)>); |
113 | |
114 | constexpr void test_val_types() { |
115 | // Test & overload |
116 | { |
117 | // Without & qualifier on F's operator() |
118 | { |
119 | std::expected<int, int> e(std::unexpected<int>(0)); |
120 | std::same_as<std::expected<int, int>> decltype(auto) val = e.transform_error(LVal{}); |
121 | assert(val.error() == 1); |
122 | } |
123 | |
124 | // With & qualifier on F's operator() |
125 | { |
126 | std::expected<int, int> e(std::unexpected<int>(0)); |
127 | RefQual l{}; |
128 | std::same_as<std::expected<int, int>> decltype(auto) val = e.transform_error(l); |
129 | assert(val.error() == 1); |
130 | } |
131 | } |
132 | |
133 | // Test const& overload |
134 | { |
135 | // Without const& qualifier on F's operator() |
136 | { |
137 | const std::expected<int, int> e(std::unexpected<int>(0)); |
138 | std::same_as<std::expected<int, int>> decltype(auto) val = e.transform_error(CLVal{}); |
139 | assert(val.error() == 1); |
140 | } |
141 | |
142 | // With const& qualifier on F's operator() |
143 | { |
144 | const std::expected<int, int> e(std::unexpected<int>(0)); |
145 | const CRefQual l{}; |
146 | std::same_as<std::expected<int, int>> decltype(auto) val = e.transform_error(l); |
147 | assert(val.error() == 1); |
148 | } |
149 | } |
150 | |
151 | // Test && overload |
152 | { |
153 | // Without && qualifier on F's operator() |
154 | { |
155 | std::expected<int, int> e(std::unexpected<int>(0)); |
156 | std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform_error(RVal{}); |
157 | assert(val.error() == 1); |
158 | } |
159 | |
160 | // With && qualifier on F's operator() |
161 | { |
162 | std::expected<int, int> e(std::unexpected<int>(0)); |
163 | std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform_error(RVRefQual{}); |
164 | assert(val.error() == 1); |
165 | } |
166 | } |
167 | |
168 | // Test const&& overload |
169 | { |
170 | // Without const&& qualifier on F's operator() |
171 | { |
172 | const std::expected<int, int> e(std::unexpected<int>(0)); |
173 | std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform_error(CRVal{}); |
174 | assert(val.error() == 1); |
175 | } |
176 | |
177 | // With const&& qualifier on F's operator() |
178 | { |
179 | const std::expected<int, int> e(std::unexpected<int>(0)); |
180 | const RVCRefQual l{}; |
181 | std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform_error(std::move(l)); |
182 | assert(val.error() == 1); |
183 | } |
184 | } |
185 | } |
186 | // clang-format on |
187 | |
188 | // check unex member is direct-non-list-initialized with invoke(std::forward<F>(f), error()) |
189 | constexpr void test_direct_non_list_init() { |
190 | auto xform = [](int i) { return NonCopy(i); }; |
191 | std::expected<int, int> e(std::unexpected<int>(2)); |
192 | std::expected<int, NonCopy> n = e.transform_error(xform); |
193 | assert(n.error().value == 2); |
194 | } |
195 | |
196 | // check that the lambda body is not instantiated during overload resolution |
197 | constexpr void test_sfinae() { |
198 | std::expected<int, NonConst> e(2); |
199 | auto l = [](auto&& x) { return x.non_const(); }; |
200 | e.transform_error(l); |
201 | std::move(e).transform_error(l); |
202 | |
203 | std::expected<int, int> e1; |
204 | const auto& ce1 = e1; |
205 | |
206 | const auto never_called = [](int) { |
207 | assert(false); |
208 | return 0; |
209 | }; |
210 | |
211 | e1.transform_error(never_called); |
212 | std::move(e1).transform_error(never_called); |
213 | ce1.transform_error(never_called); |
214 | std::move(ce1).transform_error(never_called); |
215 | } |
216 | |
217 | constexpr void test_move_only_error_type() { |
218 | // Test & |
219 | { |
220 | std::expected<int, MoveOnlyErrorType> e; |
221 | auto l = [](MoveOnlyErrorType&) { return 0; }; |
222 | e.transform_error(l); |
223 | } |
224 | |
225 | // Test const& |
226 | { |
227 | const std::expected<int, MoveOnlyErrorType> e; |
228 | auto l = [](const MoveOnlyErrorType&) { return 0; }; |
229 | e.transform_error(l); |
230 | } |
231 | |
232 | // Test && |
233 | { |
234 | std::expected<int, MoveOnlyErrorType> e; |
235 | auto l = [](MoveOnlyErrorType&&) { return 0; }; |
236 | std::move(e).transform_error(l); |
237 | } |
238 | |
239 | // Test const&& |
240 | { |
241 | const std::expected<int, MoveOnlyErrorType> e; |
242 | auto l = [](const MoveOnlyErrorType&&) { return 0; }; |
243 | std::move(e).transform_error(l); |
244 | } |
245 | } |
246 | |
247 | constexpr bool test() { |
248 | test_sfinae(); |
249 | test_val_types(); |
250 | test_direct_non_list_init(); |
251 | test_move_only_error_type(); |
252 | |
253 | return true; |
254 | } |
255 | |
256 | int main(int, char**) { |
257 | test(); |
258 | static_assert(test()); |
259 | |
260 | return 0; |
261 | } |
262 | |