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
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_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.
105static_assert(!has_transform_error<const std::expected<std::unique_ptr<int>, int>&, int()>);
106static_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.
109static_assert(has_transform_error<std::expected<int, MoveOnlyErrorType>&, int(MoveOnlyErrorType &)>);
110static_assert(has_transform_error<const std::expected<int, MoveOnlyErrorType>&, int(const MoveOnlyErrorType &)>);
111static_assert(has_transform_error<std::expected<int, MoveOnlyErrorType>&&, int(MoveOnlyErrorType&&)>);
112static_assert(has_transform_error<const std::expected<int, MoveOnlyErrorType>&&, int(const MoveOnlyErrorType&&)>);
113
114constexpr 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())
189constexpr 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
197constexpr 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
217constexpr 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
247constexpr 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
256int main(int, char**) {
257 test();
258 static_assert(test());
259
260 return 0;
261}
262

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