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