diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -296,6 +296,8 @@ ------------------------------------------------------------------- ``__cpp_lib_is_scoped_enum`` ``202011L`` ------------------------------------------------- ----------------- + ``__cpp_lib_monadic_optional`` ``202110L`` + ------------------------------------------------- ----------------- ``__cpp_lib_stacktrace`` *unimplemented* ------------------------------------------------- ----------------- ``__cpp_lib_stdatomic_h`` *unimplemented* diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -24,7 +24,7 @@ "`P2166R1 `__","LWG","A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr","June 2021","|Complete|","13.0" "","","","","","" "`P0288R9 `__","LWG","``any_invocable``","October 2021","","" -"`P0798R8 `__","LWG","Monadic operations for ``std::optional``","October 2021","","" +"`P0798R8 `__","LWG","Monadic operations for ``std::optional``","October 2021","|Complete|","14.0" "`P0849R8 `__","LWG","``auto(x)``: ``DECAY_COPY`` in the language","October 2021","","" "`P1072R10 `__","LWG","``basic_string::resize_and_overwrite``","October 2021","","" "`P1147R1 `__","LWG","Printing ``volatile`` Pointers","October 2021","","" 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 @@ -147,6 +159,7 @@ */ #include <__availability> +#include <__concepts/invocable.h> #include <__config> #include <__debug> #include <__functional_base> @@ -582,6 +595,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 +1016,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 @@ -108,6 +108,7 @@ __cpp_lib_math_constants 201907L __cpp_lib_math_special_functions 201603L __cpp_lib_memory_resource 201603L +__cpp_lib_monadic_optional 202110L __cpp_lib_node_extract 201606L __cpp_lib_nonmember_container_access 201411L @@ -359,6 +360,7 @@ #if _LIBCPP_STD_VER > 20 # define __cpp_lib_is_scoped_enum 202011L +# define __cpp_lib_monadic_optional 202110L // # define __cpp_lib_stacktrace 202011L // # define __cpp_lib_stdatomic_h 202011L # define __cpp_lib_string_contains 202011L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.pass.cpp @@ -15,8 +15,9 @@ // Test the feature test macros defined by -/* Constant Value - __cpp_lib_optional 201606L [C++17] +/* Constant Value + __cpp_lib_monadic_optional 202110L [C++2b] + __cpp_lib_optional 201606L [C++17] */ #include @@ -24,18 +25,30 @@ #if TEST_STD_VER < 14 +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifdef __cpp_lib_optional # error "__cpp_lib_optional should not be defined before c++17" # endif #elif TEST_STD_VER == 14 +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifdef __cpp_lib_optional # error "__cpp_lib_optional should not be defined before c++17" # endif #elif TEST_STD_VER == 17 +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifndef __cpp_lib_optional # error "__cpp_lib_optional should be defined in c++17" # endif @@ -45,6 +58,10 @@ #elif TEST_STD_VER == 20 +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifndef __cpp_lib_optional # error "__cpp_lib_optional should be defined in c++20" # endif @@ -54,6 +71,13 @@ #elif TEST_STD_VER > 20 +# ifndef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should be defined in c++2b" +# endif +# if __cpp_lib_monadic_optional != 202110L +# error "__cpp_lib_monadic_optional should have the value 202110L in c++2b" +# endif + # ifndef __cpp_lib_optional # error "__cpp_lib_optional should be defined in c++2b" # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -103,6 +103,7 @@ __cpp_lib_math_constants 201907L [C++20] __cpp_lib_math_special_functions 201603L [C++17] __cpp_lib_memory_resource 201603L [C++17] + __cpp_lib_monadic_optional 202110L [C++2b] __cpp_lib_node_extract 201606L [C++17] __cpp_lib_nonmember_container_access 201411L [C++17] __cpp_lib_not_fn 201603L [C++17] @@ -499,6 +500,10 @@ # error "__cpp_lib_memory_resource should not be defined before c++17" # endif +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifdef __cpp_lib_node_extract # error "__cpp_lib_node_extract should not be defined before c++17" # endif @@ -1059,6 +1064,10 @@ # error "__cpp_lib_memory_resource should not be defined before c++17" # endif +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifdef __cpp_lib_node_extract # error "__cpp_lib_node_extract should not be defined before c++17" # endif @@ -1787,6 +1796,10 @@ # endif # endif +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifndef __cpp_lib_node_extract # error "__cpp_lib_node_extract should be defined in c++17" # endif @@ -2851,6 +2864,10 @@ # endif # endif +# ifdef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should not be defined before c++2b" +# endif + # ifndef __cpp_lib_node_extract # error "__cpp_lib_node_extract should be defined in c++20" # endif @@ -4005,6 +4022,13 @@ # endif # endif +# ifndef __cpp_lib_monadic_optional +# error "__cpp_lib_monadic_optional should be defined in c++2b" +# endif +# if __cpp_lib_monadic_optional != 202110L +# error "__cpp_lib_monadic_optional should have the value 202110L in c++2b" +# endif + # ifndef __cpp_lib_node_extract # error "__cpp_lib_node_extract should be defined in c++2b" # endif 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()); +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -453,6 +453,10 @@ "values": { "c++17": 201603 }, "headers": ["memory_resource"], "unimplemented": True, + }, { + "name": "__cpp_lib_monadic_optional", + "values": { "c++2b": 202110 }, + "headers": ["optional"], }, { "name": "__cpp_lib_node_extract", "values": { "c++17": 201606 },