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(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#include "../../types.h"
30
31struct 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
38struct 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
45struct 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
52struct 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
59struct 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
66struct 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
73struct 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
80struct 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
87struct NonCopy {
88 int value;
89 constexpr explicit NonCopy(int val) : value(val) {}
90 NonCopy(const NonCopy&) = delete;
91};
92
93struct NonConst {
94 int non_const() { return 1; }
95};
96
97template <class E, class F>
98concept has_transform =
99 requires(E&& e, F&& f) {
100 { std::forward<E>(e).transform(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.
105static_assert(!has_transform<const std::expected<int, std::unique_ptr<int>>&, int()>);
106static_assert(!has_transform<const std::expected<int, std::unique_ptr<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// There are no effects for `&` and `const &` overload, because the constraints requires is_constructible_v<E, decltype(error())> is true.
110static_assert(has_transform<std::expected<int, MoveOnlyErrorType>&&, int(int)>);
111static_assert(has_transform<const std::expected<int, MoveOnlyErrorType>&&, int(const int)>);
112
113constexpr void test_val_types() {
114 // Test & overload
115 {
116 // Without & qualifier on F's operator()
117 {
118 std::expected<int, int> e(0);
119 std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(LVal{});
120 assert(val == 1);
121 }
122
123 // With & qualifier on F's operator()
124 {
125 std::expected<int, int> e(0);
126 RefQual l{};
127 std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(l);
128 assert(val == 1);
129 }
130 }
131
132 // Test const& overload
133 {
134 // Without & qualifier on F's operator()
135 {
136 const std::expected<int, int> e(0);
137 std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(CLVal{});
138 assert(val == 1);
139 }
140
141 // With & qualifier on F's operator()
142 {
143 const std::expected<int, int> e(0);
144 const CRefQual l{};
145 std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(l);
146 assert(val == 1);
147 }
148 }
149
150 // Test && overload
151 {
152 // Without & 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(RVal{});
156 assert(val == 1);
157 }
158
159 // With & qualifier on F's operator()
160 {
161 std::expected<int, int> e(0);
162 std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform(RVRefQual{});
163 assert(val == 1);
164 }
165 }
166
167 // Test const&& overload
168 {
169 // Without & qualifier on F's operator()
170 {
171 const std::expected<int, int> e(0);
172 std::same_as<std::expected<int, int>> decltype(auto) val = std::move(e).transform(CRVal{});
173 assert(val == 1);
174 }
175
176 // With & qualifier on F's operator()
177 {
178 const std::expected<int, int> e(0);
179 const RVCRefQual l{};
180 std::same_as<std::expected<int, int>> decltype(auto) val = e.transform(std::move(l));
181 assert(val == 1);
182 }
183 }
184}
185// clang-format on
186
187constexpr void test_take_val_return_void() {
188 std::expected<int, int> e(1);
189 int val = 0;
190 e.transform([&val]<typename T>(T&&) -> void {
191 static_assert(std::is_same_v<T, int&>);
192 assert(val == 0);
193 val = 1;
194 });
195 assert(val == 1);
196 std::move(e).transform([&val]<typename T>(T&&) -> void {
197 static_assert(std::is_same_v<T, int>);
198 assert(val == 1);
199 val = 2;
200 });
201
202 const auto& ce = e;
203 assert(val == 2);
204 ce.transform([&val]<typename T>(T&&) -> void {
205 static_assert(std::is_same_v<T, const int&>);
206 assert(val == 2);
207 val = 3;
208 });
209 assert(val == 3);
210 std::move(ce).transform([&val]<typename T>(T&&) -> void {
211 static_assert(std::is_same_v<T, const int>);
212 assert(val == 3);
213 val = 4;
214 });
215 assert(val == 4);
216}
217
218// check val member is direct-non-list-initialized with invoke(std::forward<F>(f), value())
219constexpr void test_direct_non_list_init() {
220 auto xform = [](int i) { return NonCopy(i); };
221 std::expected<int, int> e(2);
222 std::expected<NonCopy, int> n = e.transform(xform);
223 assert(n.value().value == 2);
224}
225
226// check that the lambda body is not instantiated during overload resolution
227constexpr void test_sfinae() {
228 std::expected<NonConst, int> e(std::unexpected<int>(2));
229 auto l = [](auto&& x) { return x.non_const(); };
230 e.transform(l);
231 std::move(e).transform(l);
232
233 std::expected<int, int> e1(std::unexpected<int>(1));
234 const auto& ce1 = e1;
235 const auto never_called = [](int) {
236 assert(false);
237 return std::expected<int, int>();
238 };
239
240 e1.transform(never_called);
241 std::move(e1).transform(never_called);
242 ce1.and_then(never_called);
243 std::move(ce1).transform(never_called);
244}
245
246constexpr void test_move_only_error_type() {
247 // Test &&
248 {
249 std::expected<int, MoveOnlyErrorType> e;
250 auto l = [](int) { return 0; };
251 std::move(e).transform(l);
252 }
253
254 // Test const&&
255 {
256 const std::expected<int, MoveOnlyErrorType> e;
257 auto l = [](const int) { return 0; };
258 std::move(e).transform(l);
259 }
260}
261
262constexpr bool test() {
263 test_sfinae();
264 test_val_types();
265 test_direct_non_list_init();
266 test_move_only_error_type();
267 return true;
268}
269
270int main(int, char**) {
271 test();
272 static_assert(test());
273
274 return 0;
275}
276

source code of libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp