diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -298,6 +298,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","|Complete|","14.0" 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,13 @@ : __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 +291,13 @@ : __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 +611,12 @@ (is_move_constructible<_Tp>::value && is_move_assignable<_Tp>::value) >; +template +class optional; +template +struct __is_std_optional : false_type {}; +template struct __is_std_optional> : true_type {}; + template class optional : private __optional_move_assign_base<_Tp> @@ -684,6 +719,7 @@ _CheckOptionalLikeConstructor<_QualUp>, __check_tuple_constructor_fail >; + public: _LIBCPP_INLINE_VISIBILITY constexpr optional() noexcept {} @@ -759,6 +795,14 @@ this->__construct_from(_VSTD::move(__v)); } +#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 + _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 optional& operator=(nullopt_t) noexcept { @@ -993,6 +1037,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>::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>::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>::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>::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 type 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 type 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 @@ -109,6 +109,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 @@ -347,6 +348,7 @@ #if _LIBCPP_STD_VER > 20 # define __cpp_lib_byteswap 202110L # 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 @@ -104,6 +104,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] @@ -504,6 +505,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 @@ -1068,6 +1073,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 @@ -1794,6 +1803,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 @@ -2832,6 +2845,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 @@ -3963,6 +3980,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,262 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include "test_macros.h" + +struct LVal { + constexpr 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; + constexpr 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; + constexpr 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; + constexpr std::optional operator()(const int&&) { return 1; } +}; + +struct RefQual { + constexpr 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; + constexpr 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; + constexpr 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; + constexpr std::optional operator()(int) const&& { return 1; } +}; + +struct NOLVal { + constexpr std::optional operator()(int&) { return std::nullopt; } + std::optional operator()(const int&) = delete; + std::optional operator()(int&&) = delete; + std::optional operator()(const int&&) = delete; +}; + +struct NOCLVal { + std::optional operator()(int&) = delete; + constexpr std::optional operator()(const int&) { return std::nullopt; } + std::optional operator()(int&&) = delete; + std::optional operator()(const int&&) = delete; +}; + +struct NORVal { + std::optional operator()(int&) = delete; + std::optional operator()(const int&) = delete; + constexpr std::optional operator()(int&&) { return std::nullopt; } + std::optional operator()(const int&&) = delete; +}; + +struct NOCRVal { + std::optional operator()(int&) = delete; + std::optional operator()(const int&) = delete; + std::optional operator()(int&&) = delete; + constexpr std::optional operator()(const int&&) { return std::nullopt; } +}; + +struct NORefQual { + constexpr std::optional operator()(int) & { return std::nullopt; } + std::optional operator()(int) const& = delete; + std::optional operator()(int) && = delete; + std::optional operator()(int) const&& = delete; +}; + +struct NOCRefQual { + std::optional operator()(int) & = delete; + constexpr std::optional operator()(int) const& { return std::nullopt; } + std::optional operator()(int) && = delete; + std::optional operator()(int) const&& = delete; +}; + +struct NORVRefQual { + std::optional operator()(int) & = delete; + std::optional operator()(int) const& = delete; + constexpr std::optional operator()(int) && { return std::nullopt; } + std::optional operator()(int) const&& = delete; +}; + +struct NORVCRefQual { + std::optional operator()(int) & = delete; + std::optional operator()(int) const& = delete; + std::optional operator()(int) && = delete; + constexpr std::optional operator()(int) const&& { return std::nullopt; } +}; + +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() { + // Test & overload + { + // Without & qualifier on F's operator() + { + std::optional i{0}; + assert(i.and_then(LVal{}) == 1); + assert(i.and_then(NOLVal{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(LVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + std::optional i{0}; + RefQual l{}; + assert(i.and_then(l) == 1); + NORefQual nl{}; + assert(i.and_then(nl) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + const std::optional i{0}; + assert(i.and_then(CLVal{}) == 1); + assert(i.and_then(NOCLVal{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(CLVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + const std::optional i{0}; + const CRefQual l{}; + assert(i.and_then(l) == 1); + const NOCRefQual nl{}; + assert(i.and_then(nl) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(l)), std::optional); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + std::optional i{0}; + assert(std::move(i).and_then(RVal{}) == 1); + assert(std::move(i).and_then(NORVal{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(std::move(i).and_then(RVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + std::optional i{0}; + assert(i.and_then(RVRefQual{}) == 1); + assert(i.and_then(NORVRefQual{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(RVRefQual{})), std::optional); + } + } + + // Test const&& overload + { + // Without & qualifier on F's operator() + { + const std::optional i{0}; + assert(std::move(i).and_then(CRVal{}) == 1); + assert(std::move(i).and_then(NOCRVal{}) == std::nullopt); + ASSERT_SAME_TYPE(decltype(std::move(i).and_then(CRVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + const std::optional i{0}; + const RVCRefQual l{}; + assert(i.and_then(std::move(l)) == 1); + const NORVCRefQual nl{}; + assert(i.and_then(std::move(nl)) == std::nullopt); + ASSERT_SAME_TYPE(decltype(i.and_then(std::move(l))), std::optional); + } + } +} + +// check that the lambda body is not instantiated during overload resolution +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() { + test_val_types(); + 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); + + 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,72 @@ +//===----------------------------------------------------------------------===// +// +// 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 +concept has_or_else = requires(Opt&& opt, F&& f) { + {std::forward(opt).or_else(std::forward(f))}; +}; + +template +std::optional return_optional() {} + +static_assert(has_or_else&, decltype(return_optional)>); +static_assert(has_or_else&&, decltype(return_optional)>); +static_assert(!has_or_else&, decltype(return_optional)>); +static_assert(has_or_else&&, decltype(return_optional)>); +static_assert(!has_or_else&, decltype(return_optional)>); +static_assert(!has_or_else&&, decltype(return_optional)>); + +std::optional take_int(int) { return 0; } +void take_int_return_void(int) {} + +static_assert(!has_or_else, decltype(take_int)>); +static_assert(!has_or_else, decltype(take_int_return_void)>); +static_assert(!has_or_else, int>); + +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,205 @@ +//===----------------------------------------------------------------------===// +// +// 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 "test_macros.h" +#include +#include +#include + +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 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() { + // Test & overload + { + // Without & qualifier on F's operator() + { + std::optional i{0}; + assert(i.transform(LVal{}) == 1); + ASSERT_SAME_TYPE(decltype(i.transform(LVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + std::optional i{0}; + RefQual l{}; + assert(i.transform(l) == 1); + ASSERT_SAME_TYPE(decltype(i.transform(l)), std::optional); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + const std::optional i{0}; + assert(i.transform(CLVal{}) == 1); + ASSERT_SAME_TYPE(decltype(i.transform(CLVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + const std::optional i{0}; + const CRefQual l{}; + assert(i.transform(l) == 1); + ASSERT_SAME_TYPE(decltype(i.transform(l)), std::optional); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + std::optional i{0}; + assert(std::move(i).transform(RVal{}) == 1); + ASSERT_SAME_TYPE(decltype(std::move(i).transform(RVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + std::optional i{0}; + assert(i.transform(RVRefQual{}) == 1); + ASSERT_SAME_TYPE(decltype(i.transform(RVRefQual{})), std::optional); + } + } + + // Test const&& overload + { + // Without & qualifier on F's operator() + { + const std::optional i{0}; + assert(std::move(i).transform(CRVal{}) == 1); + ASSERT_SAME_TYPE(decltype(std::move(i).transform(CRVal{})), std::optional); + } + + //With & qualifier on F's operator() + { + const std::optional i{0}; + const RVCRefQual l{}; + assert(i.transform(std::move(l)) == 1); + ASSERT_SAME_TYPE(decltype(i.transform(std::move(l))), std::optional); + } + } +} + +struct NonConst { + int non_const() { return 1; } +}; + +// check that the lambda body is not instantiated during overload resolution +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(); + test_val_types(); + 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); + + 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 @@ -442,6 +442,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 },