xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp (revision cb4433b677a06ecbb3112f39d24b28f19b0d2626)
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-14
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   (void)e.transform_error(l);
201   (void)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   (void)e1.transform_error(never_called);
212   (void)std::move(e1).transform_error(never_called);
213   (void)ce1.transform_error(never_called);
214   (void)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       (void)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       (void)e.transform_error(l);
230   }
231 
232   // Test &&
233   {
234       std::expected<int, MoveOnlyErrorType> e;
235       auto l = [](MoveOnlyErrorType&&) { return 0; };
236       (void)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       (void)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