diff --git a/libcxx/include/optional b/libcxx/include/optional --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -132,6 +132,18 @@ template constexpr T value_or(U &&) const &; template constexpr T value_or(U &&) &&; + // [optional.monadic], monadic operations + template constexpr auto and_then(F&& f) &; // since c++23 + template constexpr auto and_then(F&& f) &&; // since c++23 + template constexpr auto and_then(F&& f) const&; // since c++23 + template constexpr auto and_then(F&& f) const&&; // since c++23 + template constexpr auto transform(F&& f) &; // since c++23 + template constexpr auto transform(F&& f) &&; // since c++23 + template constexpr auto transform(F&& f) const&; // since c++23 + template constexpr auto transform(F&& f) const&&; // since c++23 + template constexpr optional or_else(F&& f) &&; // since c++23 + template constexpr optional or_else(F&& f) const&; // since c++23 + // 23.6.3.6, modifiers void reset() noexcept; // constexpr in C++20 @@ -146,7 +158,9 @@ */ +#include "__config" #include <__availability> +#include <__concepts/invocable.h> #include <__config> #include <__debug> #include <__functional_base> @@ -582,6 +596,16 @@ (is_move_constructible<_Tp>::value && is_move_assignable<_Tp>::value) >; +#if _LIBCPP_STD_VER > 20 +template +class optional; +template +struct __is_optional : _VSTD::false_type {}; +template struct __is_optional<_VSTD::optional<_Tp>> : _VSTD::true_type {}; +template +constexpr bool __is_optional_v = __is_optional<_Tp>::value; +#endif + template class optional : private __optional_move_assign_base<_Tp> @@ -993,6 +1017,128 @@ static_cast(_VSTD::forward<_Up>(__v)); } +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) & { + using __result_type = invoke_result_t<_Func, decltype(value())>; + static_assert(__is_optional_v<__result_type>, + "Result of f(value()) must be a specialization of std::optional"); + if (*this) + return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + return _VSTD::remove_cvref_t<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) const& { + using __result_type = invoke_result_t<_Func, decltype(value())>; + static_assert(__is_optional_v<__result_type>, + "Result of f(value()) must be a specialization of std::optional"); + if (*this) + return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + return _VSTD::remove_cvref_t<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) && { + using __result_type = invoke_result_t<_Func, decltype(_VSTD::move(value()))>; + static_assert(__is_optional_v<__result_type>, + "Result of f(std::move(value())) must be a specialization of std::optional"); + if (*this) + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); + return remove_cvref_t<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) const&& { + using __result_type = invoke_result_t<_Func, decltype(_VSTD::move(value()))>; + static_assert(__is_optional_v<__result_type>, + "Result of f(std::move(value())) must be a specialization of std::optional"); + if (*this) + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); + return remove_cvref_t<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) & { + using __result_type = remove_cvref_t>; + static_assert(!is_array_v<__result_type>, "Result of f(value()) should not be an Array"); + static_assert(!is_same_v<__result_type, in_place_t>, + "Result of f(value()) should not be std::in_place_t"); + static_assert(!is_same_v<__result_type, nullopt_t>, + "Result of f(value()) should not be std::nullopt_t"); + if (*this) + return optional<__result_type> {_VSTD::invoke(_VSTD::forward<_Func>(__f), value())}; + return optional<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) const& { + using __result_type = remove_cvref_t>; + static_assert(!is_array_v<__result_type>, "Result of f(value()) should not be an Array"); + static_assert(!is_same_v<__result_type, in_place_t>, + "Result of f(value()) should not be std::in_place_t"); + static_assert(!is_same_v<__result_type, nullopt_t>, + "Result of f(value()) should not be std::nullopt_t"); + if (*this) + return optional<__result_type> {_VSTD::invoke(_VSTD::forward<_Func>(__f), value())}; + return optional<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) && { + using __result_type = remove_cvref_t>; + static_assert(!is_array_v<__result_type>, "Result of f(std::move(value())) should not be an Array"); + static_assert(!is_same_v<__result_type, in_place_t>, + "Result of f(std::move(value())) should not be std::in_place_t"); + static_assert(!is_same_v<__result_type, nullopt_t>, + "Result of f(std::move(value())) should not be std::nullopt_t"); + if (*this) + return optional<__result_type> {_VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value()))}; + return optional<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) const&& { + using __result_type = remove_cvref_t>; + static_assert(!is_array_v<__result_type>, "Result of f(std::move(value())) should not be an Array"); + static_assert(!is_same_v<__result_type, in_place_t>, + "Result of f(std::move(value())) should not be std::in_place_t"); + static_assert(!is_same_v<__result_type, nullopt_t>, + "Result of f(std::move(value())) should not be std::nullopt_t"); + if (*this) + return optional<__result_type> {_VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value()))}; + return optional<__result_type> {}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr optional or_else(_Func&& __f) const& requires is_copy_constructible_v { + static_assert(is_same_v>, optional>, + "Result of f() should be an optional"); + if (*this) + return *this; + return std::forward<_Func>(__f)(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr optional or_else(_Func&& __f) && requires is_move_constructible_v { + static_assert(is_same_v>, optional>, + "Result of f() should be an optional"); + if (*this) + return std::move(*this); + return std::forward<_Func>(__f)(); + } +#endif // _LIBCPP_STD_VER > 20 + using __base::reset; }; diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -363,6 +363,7 @@ // # define __cpp_lib_stdatomic_h 202011L # define __cpp_lib_string_contains 202011L # define __cpp_lib_to_underlying 202102L +#define __cpp_lib_monadic_optional 202110L #endif // clang-format on diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// + +// template constexpr auto and_then(F&&) &; +// template constexpr auto and_then(F&&) &&; +// template constexpr auto and_then(F&&) const&; +// template constexpr auto and_then(F&&) const&&; + +#include +#include + +constexpr bool test() { + std::optional opt{}; + opt.and_then([](int) { + assert(false); + return std::optional{}; + }); + + std::move(opt).and_then([](int) { + assert(false); + return std::optional{}; + }); + + static_cast&>(opt).and_then([](int) { + assert(false); + return std::optional{}; + }); + + static_cast&&>(opt).and_then([](int) { + assert(false); + return std::optional{}; + }); + + opt = 1; + assert(*opt.and_then([](int i) { return std::optional{i + 1}; }) == 2); + assert(*std::move(opt).and_then([](int i) { return std::optional{i + 1}; }) == 2); + assert(*static_cast&>(opt).and_then([](int i) { return std::optional{i + 1}; }) == 2); + assert(*static_cast&&>(opt).and_then([](int i) { return std::optional{i + 1}; }) == 2); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// + +// template constexpr optional or_else(F&&) &&; +// template constexpr optional or_else(F&&) const&; + +#include "MoveOnly.h" + +#include +#include + +constexpr bool test() { + { + std::optional opt; + assert(opt.or_else([] { return std::optional{0}; }) == 0); + opt = 1; + opt.or_else([] { + assert(false); + return std::optional{}; + }); + } + { + std::optional opt; + opt = std::move(opt).or_else([] { return std::optional{MoveOnly{}}; }); + std::move(opt).or_else([] { + assert(false); + return std::optional{}; + }); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// + +// template constexpr auto transform(F&&) &; +// template constexpr auto transform(F&&) &&; +// template constexpr auto transform(F&&) const&; +// template constexpr auto transform(F&&) const&&; + +#include +#include + +constexpr bool test() { + std::optional opt; + + opt.transform([](int) { + assert(false); + return 0; + }); + + std::move(opt).transform([](int) { + assert(false); + return 0; + }); + + static_cast&>(opt).transform([](int) { + assert(false); + return 0; + }); + + static_cast&&>(opt).transform([](int) { + assert(false); + return 0; + }); + + opt = 1; + + assert(opt.transform([](int i) { return i + 1; }) == 2); + assert(std::move(opt).transform([](int i) { return i + 1; }) == 2); + assert(static_cast&>(opt).transform([](int i) { return i + 1; }) == 2); + assert(static_cast&&>(opt).transform([](int i) { return i + 1; }) == 2); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +}