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> @@ -200,6 +213,8 @@ inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secret_tag{}}; +struct __optional_construct_from_invoke_tag {}; + template ::value> struct __optional_destruct_base; @@ -234,6 +249,14 @@ : __val_(_VSTD::forward<_Args>(__args)...), __engaged_(true) {} +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI + constexpr __optional_destruct_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) + : __val_(_VSTD::invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...)), __engaged_(true) { + } +#endif + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept { @@ -269,6 +292,14 @@ : __val_(_VSTD::forward<_Args>(__args)...), __engaged_(true) {} +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI + constexpr __optional_destruct_base(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) + : __val_(_VSTD::invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...)), __engaged_(true) { + } +#endif + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 void reset() noexcept { @@ -582,6 +613,14 @@ (is_move_constructible<_Tp>::value && is_move_assignable<_Tp>::value) >; +#if _LIBCPP_STD_VER > 20 +template +class optional; +template +struct __is_std_optional : false_type {}; +template struct __is_std_optional> : true_type {}; +#endif + template class optional : private __optional_move_assign_base<_Tp> @@ -684,6 +723,15 @@ _CheckOptionalLikeConstructor<_QualUp>, __check_tuple_constructor_fail >; + +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI + constexpr explicit optional(__optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args) + : __base(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...) { + } +#endif + public: _LIBCPP_INLINE_VISIBILITY constexpr optional() noexcept {} @@ -993,6 +1041,132 @@ static_cast(_VSTD::forward<_Up>(__v)); } +#if _LIBCPP_STD_VER > 20 + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) & { + using _Up = invoke_result_t<_Func, value_type&>; + static_assert(__is_std_optional<_Up>::value, + "Result of f(value()) must be a specialization of std::optional"); + if (*this) + return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + return remove_cvref_t<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) const& { + using _Up = invoke_result_t<_Func, const value_type&>; + static_assert(__is_std_optional<_Up>::value, + "Result of f(value()) must be a specialization of std::optional"); + if (*this) + return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + return remove_cvref_t<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) && { + using _Up = invoke_result_t<_Func, value_type&&>; + static_assert(__is_std_optional<_Up>::value, + "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<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto and_then(_Func&& __f) const&& { + using _Up = invoke_result_t<_Func, const value_type&&>; + static_assert(__is_std_optional<_Up>::value, + "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<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) & { + using _Up = remove_cv_t>; + static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); + static_assert(!is_same_v<_Up, in_place_t>, + "Result of f(value()) should not be std::in_place_t"); + static_assert(!is_same_v<_Up, nullopt_t>, + "Result of f(value()) should not be std::nullopt_t"); + static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type"); + if (*this) + return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); + return optional<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) const& { + using _Up = remove_cv_t>; + static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); + static_assert(!is_same_v<_Up, in_place_t>, + "Result of f(value()) should not be std::in_place_t"); + static_assert(!is_same_v<_Up, nullopt_t>, + "Result of f(value()) should not be std::nullopt_t"); + static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type"); + if (*this) + return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); + return optional<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) && { + using _Up = remove_cv_t>; + static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); + static_assert(!is_same_v<_Up, in_place_t>, + "Result of f(std::move(value())) should not be std::in_place_t"); + static_assert(!is_same_v<_Up, nullopt_t>, + "Result of f(std::move(value())) should not be std::nullopt_t"); + static_assert(is_object_v<_Up>, "Result of f(std::move(value())) should be an object type"); + if (*this) + return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), _VSTD::move(value())); + return optional<_Up>(); + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto transform(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); + static_assert(!is_same_v<_Up, in_place_t>, + "Result of f(std::move(value())) should not be std::in_place_t"); + static_assert(!is_same_v<_Up, nullopt_t>, + "Result of f(std::move(value())) should not be std::nullopt_t"); + static_assert(is_object_v<_Up>, "Result of f(std::move(value())) should be an object type"); + if (*this) + return optional<_Up>(__optional_construct_from_invoke_tag{}, _VSTD::forward<_Func>(__f), _VSTD::move(value())); + return optional<_Up>(); + } + + 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 the same as this optional"); + if (*this) + return *this; + return _VSTD::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 the same as this optional"); + if (*this) + return _VSTD::move(*this); + return _VSTD::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,142 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +struct LVal { + std::optional operator()(int&) { return 1; } + std::optional operator()(const int&) = delete; + std::optional operator()(int&&) = delete; + std::optional operator()(const int&&) = delete; +}; + +struct CLVal { + std::optional operator()(int&) = delete; + std::optional operator()(const int&) { return 1; } + std::optional operator()(int&&) = delete; + std::optional operator()(const int&&) = delete; +}; + +struct RVal { + std::optional operator()(int&) = delete; + std::optional operator()(const int&) = delete; + std::optional operator()(int&&) { return 1; } + std::optional operator()(const int&&) = delete; +}; + +struct CRVal { + std::optional operator()(int&) = delete; + std::optional operator()(const int&) = delete; + std::optional operator()(int&&) = delete; + std::optional operator()(const int&&) { return 1; } +}; + +struct RefQual { + std::optional operator()(int) & { return 1; } + std::optional operator()(int) const& = delete; + std::optional operator()(int) && = delete; + std::optional operator()(int) const&& = delete; +}; + +struct CRefQual { + std::optional operator()(int) & = delete; + std::optional operator()(int) const& { return 1; } + std::optional operator()(int) && = delete; + std::optional operator()(int) const&& = delete; +}; + +struct RVRefQual { + std::optional operator()(int) & = delete; + std::optional operator()(int) const& = delete; + std::optional operator()(int) && { return 1; } + std::optional operator()(int) const&& = delete; +}; + +struct RVCRefQual { + std::optional operator()(int) & = delete; + std::optional operator()(int) const& = delete; + std::optional operator()(int) && = delete; + std::optional operator()(int) const&& { return 1; } +}; + +struct NoCopy { + NoCopy() = default; + NoCopy(const NoCopy&) { assert(false); } + std::optional operator()(const NoCopy&&) { return 1; } +}; + +struct NonConst { + std::optional non_const() { return 1; } +}; + +constexpr void test_val_types() { + std::optional i{}; + const auto& ci = i; + + i.and_then(LVal{}); + ci.and_then(CLVal{}); + std::move(i).and_then(RVal{}); + std::move(ci).and_then(CRVal{}); + + RefQual l{}; + i.and_then(l); + const CRefQual cl{}; + i.and_then(cl); + i.and_then(RVRefQual{}); + const RVCRefQual cr{}; + i.and_then(std::move(cr)); +} + +constexpr void test_sfinae() { + std::optional opt{}; + auto l = [](auto&& x) { return x.non_const(); }; + opt.and_then(l); + std::move(opt).and_then(l); +} + +constexpr bool test() { + std::optional opt{}; + const auto& copt = opt; + + const auto never_called = [](int) { + assert(false); + return std::optional{}; + }; + + opt.and_then(never_called); + std::move(opt).and_then(never_called); + copt.and_then(never_called); + std::move(copt).and_then(never_called); + + 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(*copt.and_then([](int i) { return std::optional{i + 1}; }) == 2); + assert(*std::move(opt).and_then([](int i) { return std::optional{i + 1}; }) == 2); + + std::optional nc; + const auto& cnc = nc; + std::move(cnc).and_then(NoCopy{}); + std::move(nc).and_then(NoCopy{}); + + 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,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +struct NonMovable { + NonMovable() = default; + NonMovable(NonMovable&&) = delete; +}; + +template +requires requires(O&& o, F&& f) { {std::forward(o).or_else(std::forward(f))}; } +constexpr bool has_or_else(O&&, F&&) { return true; } + +template +constexpr bool has_or_else(O&&, F&&) { + return false; +} + +constexpr void test_sfinae() { + std::optional o1; + std::optional o2; + std::optional o3; + + static_assert(has_or_else(o1, [&] { return std::optional{}; })); + static_assert(has_or_else(std::move(o1), [&] { return std::optional{}; })); + static_assert(!has_or_else(o2, [&] { return std::optional{}; })); + static_assert(has_or_else(std::move(o2), [&] { return std::optional{}; })); + static_assert(!has_or_else(o3, [&] { return std::optional{}; })); + static_assert(!has_or_else(o3, [&] { return std::optional{}; })); + + static_assert(!has_or_else(o1, [&](int) { return std::optional(); })); + static_assert(!has_or_else(o1, [&](int) {})); + static_assert(!has_or_else(o1, 42)); +} + +constexpr bool test() { + test_sfinae(); + { + 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,157 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include + +struct LVal { + 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; + 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; + 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; + int operator()(const int&&) { return 1; } +}; + +struct RefQual { + 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; + 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; + 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; + int operator()(int) const&& { return 1; } +}; + +struct NoCopy { + NoCopy() = default; + NoCopy(const NoCopy&) { assert(false); } + int operator()(const NoCopy&&) { return 1; } +}; + +struct NoMove { + NoMove() = default; + NoMove(NoMove&&) = delete; + NoMove operator()(const NoCopy&&) { return NoMove{}; } +}; + +constexpr void test_val_types() { + std::optional i; + const auto& ci = i; + + using U = std::invoke_result_t; + static_assert(std::is_same_v); + + i.transform(LVal{}); + ci.transform(CLVal{}); + std::move(i).transform(RVal{}); + std::move(ci).transform(CRVal{}); + + RefQual l{}; + i.transform(l); + const CRefQual cl{}; + i.transform(cl); + i.transform(RVRefQual{}); + const RVCRefQual cr{}; + i.transform(std::move(cr)); +} + +struct NonConst { + int non_const() { return 1; } +}; + +constexpr void test_sfinae() { + std::optional opt{}; + auto l = [](auto&& x) { return x.non_const(); }; + opt.transform(l); + std::move(opt).transform(l); +} + +constexpr bool test() { + test_sfinae(); + std::optional opt; + const auto& copt = opt; + + const auto never_called = [](int) { + assert(false); + return 0; + }; + + opt.transform(never_called); + std::move(opt).transform(never_called); + copt.transform(never_called); + std::move(copt).transform(never_called); + + opt = 1; + + assert(opt.transform([](int i) { return i + 1; }) == 2); + assert(std::move(opt).transform([](int i) { return i + 1; }) == 2); + assert(copt.transform([](int i) { return i + 1; }) == 2); + assert(std::move(copt).transform([](int i) { return i + 1; }) == 2); + + std::optional nc; + const auto& cnc = nc; + std::move(nc).transform(NoCopy{}); + std::move(cnc).transform(NoCopy{}); + + std::move(nc).transform(NoMove{}); + std::move(cnc).transform(NoMove{}); + + 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 },