xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp (revision ebc111b08bddca55d5f7e560a20bdb2c913d80cb)
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, but we have a workaround to
13 // avoid this issue.
14 
15 // <expected>
16 
17 // template<class F> constexpr auto transform(F&& f) &;
18 // template<class F> constexpr auto transform(F&& f) const &;
19 // template<class F> constexpr auto transform(F&& f) &&;
20 // template<class F> constexpr auto transform(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 struct LVal {
30   constexpr int operator()(int&) { return 1; }
31   int operator()(const int&)  = delete;
32   int operator()(int&&)       = delete;
33   int operator()(const int&&) = delete;
34 };
35 
36 struct CLVal {
37   int operator()(int&) = delete;
38   constexpr int operator()(const int&) { return 1; }
39   int operator()(int&&)       = delete;
40   int operator()(const int&&) = delete;
41 };
42 
43 struct RVal {
44   int operator()(int&)       = delete;
45   int operator()(const int&) = delete;
46   constexpr int operator()(int&&) { return 1; }
47   int operator()(const int&&) = delete;
48 };
49 
50 struct CRVal {
51   int operator()(int&)       = delete;
52   int operator()(const int&) = delete;
53   int operator()(int&&)      = delete;
54   constexpr int operator()(const int&&) { return 1; }
55 };
56 
57 struct RefQual {
58   constexpr int operator()(int) & { return 1; }
59   int operator()(int) const&  = delete;
60   int operator()(int) &&      = delete;
61   int operator()(int) const&& = delete;
62 };
63 
64 struct CRefQual {
65   int operator()(int) & = delete;
66   constexpr int operator()(int) const& { return 1; }
67   int operator()(int) &&      = delete;
68   int operator()(int) const&& = delete;
69 };
70 
71 struct RVRefQual {
72   int operator()(int) &      = delete;
73   int operator()(int) const& = delete;
74   constexpr int operator()(int) && { return 1; }
75   int operator()(int) const&& = delete;
76 };
77 
78 struct RVCRefQual {
79   int operator()(int) &      = delete;
80   int operator()(int) const& = delete;
81   int operator()(int) &&     = delete;
82   constexpr int operator()(int) const&& { return 1; }
83 };
84 
85 struct NonCopy {
86   int value;
87   constexpr explicit NonCopy(int val) : value(val) {}
88   NonCopy(const NonCopy&) = delete;
89 };
90 
91 struct NonConst {
92   int non_const() { return 1; }
93 };
94 
95 template <class E, class F>
96 concept has_transform =
97     requires(E&& e, F&& f) {
98       { std::forward<E>(e).transform(std::forward<F>(f)) };
99     };
100 
101 // [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body.
102 static_assert(!has_transform<const std::expected<int, std::unique_ptr<int>>&, int()>);
103 static_assert(!has_transform<const std::expected<int, std::unique_ptr<int>>&&, int()>);
104 
105 // clang-format off
106 constexpr void test_val_types() {
107   // Test & overload
108   {
109     // Without &qualifier on F'soperator()
110     {
111       std::expected<int, int> e(0);
112       std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(LVal{});
113       assert(val == 1);
114     }
115 
116     // With & qualifier on F's operator()
117     {
118       std::expected<int, int> e(0);
119       RefQual l{};
120       std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(l);
121       assert(val == 1);
122     }
123   }
124 
125   // Test const& overload
126   {
127     // Without & qualifier on F's operator()
128     {
129       const std::expected<int, int> e(0);
130       std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(CLVal{});
131       assert(val == 1);
132     }
133 
134     // With & qualifier on F's operator()
135     {
136       const std::expected<int, int> e(0);
137       const CRefQual l{};
138       std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(l);
139       assert(val == 1);
140     }
141   }
142 
143   // Test && overload
144   {
145     // Without & qualifier on F's operator()
146     {
147       std::expected<int, int> e(0);
148       std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform(RVal{});
149       assert(val == 1);
150     }
151 
152     // With & qualifier on F's operator()
153     {
154       std::expected<int, int> e(0);
155       std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform(RVRefQual{});
156       assert(val == 1);
157     }
158   }
159 
160   // Test const&& overload
161   {
162     // Without & qualifier on F's operator()
163     {
164       const std::expected<int, int> e(0);
165       std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform(CRVal{});
166       assert(val == 1);
167     }
168 
169     // With & qualifier on F's operator()
170     {
171       const std::expected<int, int> e(0);
172       const RVCRefQual l{};
173       std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(std::move(l));
174       assert(val == 1);
175     }
176   }
177 }
178 // clang-format on
179 
180 constexpr void test_take_val_return_void() {
181   std::expected<int, int> e(1);
182   int val = 0;
183   e.transform([&val]<typename T>(T&&) -> void {
184     static_assert(std::is_same_v<T, int&>);
185     assert(val == 0);
186     val = 1;
187   });
188   assert(val == 1);
189   std::move(e).transform([&val]<typename T>(T&&) -> void {
190     static_assert(std::is_same_v<T, int>);
191     assert(val == 1);
192     val = 2;
193   });
194 
195   const auto& ce = e;
196   assert(val == 2);
197   ce.transform([&val]<typename T>(T&&) -> void {
198     static_assert(std::is_same_v<T, const int&>);
199     assert(val == 2);
200     val = 3;
201   });
202   assert(val == 3);
203   std::move(ce).transform([&val]<typename T>(T&&) -> void {
204     static_assert(std::is_same_v<T, const int>);
205     assert(val == 3);
206     val = 4;
207   });
208   assert(val == 4);
209 }
210 
211 // check val member is direct-non-list-initialized with invoke(std::forward<F>(f), value())
212 constexpr void test_direct_non_list_init() {
213   auto xform = [](int i) { return NonCopy(i); };
214   std::expected<int, int> e(2);
215   std::expected<NonCopy, int> n = e.transform(xform);
216   assert(n.value().value == 2);
217 }
218 
219 // check that the lambda body is not instantiated during overload resolution
220 constexpr void test_sfinae() {
221   std::expected<NonConst, int> e(std::unexpected<int>(2));
222   auto l = [](auto&& x) { return x.non_const(); };
223   e.transform(l);
224   std::move(e).transform(l);
225 
226   std::expected<int, int> e1(std::unexpected<int>(1));
227   const auto& ce1         = e1;
228   const auto never_called = [](int) {
229     assert(false);
230     return std::expected<int, int>();
231   };
232 
233   e1.transform(never_called);
234   std::move(e1).transform(never_called);
235   ce1.and_then(never_called);
236   std::move(ce1).transform(never_called);
237 }
238 
239 constexpr bool test() {
240   test_sfinae();
241   test_val_types();
242   test_direct_non_list_init();
243   return true;
244 }
245 
246 int main(int, char**) {
247   test();
248   static_assert(test());
249 
250   return 0;
251 }
252