//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`, // please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333 // XFAIL: gcc-14 // // template constexpr auto transform(F&& f) &; // template constexpr auto transform(F&& f) const &; // template constexpr auto transform(F&& f) &&; // template constexpr auto transform(F&& f) const &&; #include #include #include #include #include #include #include "../../types.h" struct LVal { constexpr int operator()(int&) { return 1; } int operator()(const int&) = delete; int operator()(int&&) = delete; int operator()(const int&&) = delete; }; struct CLVal { int operator()(int&) = delete; constexpr int operator()(const int&) { return 1; } int operator()(int&&) = delete; int operator()(const int&&) = delete; }; struct RVal { int operator()(int&) = delete; int operator()(const int&) = delete; constexpr int operator()(int&&) { return 1; } int operator()(const int&&) = delete; }; struct CRVal { int operator()(int&) = delete; int operator()(const int&) = delete; int operator()(int&&) = delete; constexpr int operator()(const int&&) { return 1; } }; struct RefQual { constexpr int operator()(int) & { return 1; } int operator()(int) const& = delete; int operator()(int) && = delete; int operator()(int) const&& = delete; }; struct CRefQual { int operator()(int) & = delete; constexpr int operator()(int) const& { return 1; } int operator()(int) && = delete; int operator()(int) const&& = delete; }; struct RVRefQual { int operator()(int) & = delete; int operator()(int) const& = delete; constexpr int operator()(int) && { return 1; } int operator()(int) const&& = delete; }; struct RVCRefQual { int operator()(int) & = delete; int operator()(int) const& = delete; int operator()(int) && = delete; constexpr int operator()(int) const&& { return 1; } }; struct NonCopy { int value; constexpr explicit NonCopy(int val) : value(val) {} NonCopy(const NonCopy&) = delete; }; struct NonConst { int non_const() { return 1; } }; template concept has_transform = requires(E&& e, F&& f) { { std::forward(e).transform(std::forward(f)) }; }; // clang-format off // [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. static_assert(!has_transform>&, int()>); static_assert(!has_transform>&&, int()>); // [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type. // There are no effects for `&` and `const &` overload, because the constraints requires is_constructible_v is true. static_assert(has_transform&&, int(int)>); static_assert(has_transform&&, int(const int)>); constexpr void test_val_types() { // Test & overload { // Without & qualifier on F's operator() { std::expected e(0); std::same_as> decltype(auto) val = e.transform(LVal{}); assert(val == 1); } // With & qualifier on F's operator() { std::expected e(0); RefQual l{}; std::same_as> decltype(auto) val = e.transform(l); assert(val == 1); } } // Test const& overload { // Without & qualifier on F's operator() { const std::expected e(0); std::same_as> decltype(auto) val = e.transform(CLVal{}); assert(val == 1); } // With & qualifier on F's operator() { const std::expected e(0); const CRefQual l{}; std::same_as> decltype(auto) val = e.transform(l); assert(val == 1); } } // Test && overload { // Without & qualifier on F's operator() { std::expected e(0); std::same_as> decltype(auto) val = std::move(e).transform(RVal{}); assert(val == 1); } // With & qualifier on F's operator() { std::expected e(0); std::same_as> decltype(auto) val = std::move(e).transform(RVRefQual{}); assert(val == 1); } } // Test const&& overload { // Without & qualifier on F's operator() { const std::expected e(0); std::same_as> decltype(auto) val = std::move(e).transform(CRVal{}); assert(val == 1); } // With & qualifier on F's operator() { const std::expected e(0); const RVCRefQual l{}; std::same_as> decltype(auto) val = e.transform(std::move(l)); assert(val == 1); } } } // clang-format on constexpr void test_take_val_return_void() { std::expected e(1); int val = 0; (void)e.transform([&val](T&&) -> void { static_assert(std::is_same_v); assert(val == 0); val = 1; }); assert(val == 1); (void)std::move(e).transform([&val](T&&) -> void { static_assert(std::is_same_v); assert(val == 1); val = 2; }); const auto& ce = e; assert(val == 2); (void)ce.transform([&val](T&&) -> void { static_assert(std::is_same_v); assert(val == 2); val = 3; }); assert(val == 3); (void)std::move(ce).transform([&val](T&&) -> void { static_assert(std::is_same_v); assert(val == 3); val = 4; }); assert(val == 4); } // check val member is direct-non-list-initialized with invoke(std::forward(f), value()) constexpr void test_direct_non_list_init() { auto xform = [](int i) { return NonCopy(i); }; std::expected e(2); std::expected n = e.transform(xform); assert(n.value().value == 2); } // check that the lambda body is not instantiated during overload resolution constexpr void test_sfinae() { std::expected e(std::unexpected(2)); auto l = [](auto&& x) { return x.non_const(); }; (void)e.transform(l); (void)std::move(e).transform(l); std::expected e1(std::unexpected(1)); const auto& ce1 = e1; const auto never_called = [](int) { assert(false); return std::expected(); }; (void)e1.transform(never_called); (void)std::move(e1).transform(never_called); (void)ce1.transform(never_called); (void)std::move(ce1).transform(never_called); } constexpr void test_move_only_error_type() { // Test && { std::expected e; auto l = [](int) { return 0; }; (void)std::move(e).transform(l); } // Test const&& { const std::expected e; auto l = [](const int) { return 0; }; (void)std::move(e).transform(l); } } constexpr bool test() { test_sfinae(); test_val_types(); test_direct_non_list_init(); test_move_only_error_type(); return true; } int main(int, char**) { test(); static_assert(test()); return 0; }