276 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++2010 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=9899513// XFAIL: gcc-14, gcc-1514 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 off104// [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 & overload115 {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& overload133 {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 && overload151 {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&& overload168 {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 on186 187constexpr void test_take_val_return_void() {188 std::expected<int, int> e(1);189 int val = 0;190 (void)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 (void)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 (void)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 (void)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 resolution227constexpr void test_sfinae() {228 std::expected<NonConst, int> e(std::unexpected<int>(2));229 auto l = [](auto&& x) { return x.non_const(); };230 (void)e.transform(l);231 (void)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 (void)e1.transform(never_called);241 (void)std::move(e1).transform(never_called);242 (void)ce1.transform(never_called);243 (void)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 (void)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 (void)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