diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -3491,10 +3491,36 @@ -> decltype( static_cast<_Fp&&>(__f)(static_cast<_Args&&>(__args)...)) { return static_cast<_Fp&&>(__f)(static_cast<_Args&&>(__args)...); } -// __invokable -template -struct __invokable_r -{ +// __invokable_r + +// Detects whether an express of type _From can be implicitly converted to _To +// according to [conv]. Note that this is distinct from is_convertible, which +// for non-reference _From types relies on returning an rvalue reference from a +// function and therefore gives the wrong answer for non-moveable types. +// +// REQUIRES: _From is not cv void. +template +struct __is_implicitly_convertible { +private: + template + static void _RequireImplicitlyConvertibleTo(_Tp); + + template + static _Tp _Make(); + + template (_Make<_Tp>()))> + static true_type _TestImplicitConversion(int); + + template + static false_type _TestImplicitConversion(...); + +public: + using type = decltype(_TestImplicitConversion<_From>(0)); + static constexpr bool value = type::value; +}; + +template +struct __invokable_r { template static auto __try_call(int) -> decltype( _VSTD::__invoke(declval<_XFp>(), declval<_XArgs>()...)); @@ -3511,12 +3537,14 @@ typename conditional< is_void<_Ret>::value, true_type, - is_convertible<_Result, _Ret> + __is_implicitly_convertible<_Result, _Ret> >::type, false_type >::type; static const bool value = type::value; }; + +// __invokable template using __invokable = __invokable_r; diff --git a/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r.compile.pass.cpp b/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r.compile.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// is_invocable_r + +#include + +// Non-invocable types + +static_assert(!std::is_invocable_r::value); +static_assert(!std::is_invocable_r::value); +static_assert(!std::is_invocable_r::value); +static_assert(!std::is_invocable_r::value); +static_assert(!std::is_invocable_r::value); + +// Result type matches + +template +T Return(); + +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); + +// void result type + +// Any actual return type should be useable with a result type of void. +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); + +// const- and volatile-qualified void should work too. +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); + +// Conversion of result type + +// It should be possible to use a result type to which the actual return type +// can be converted. +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); +static_assert(std::is_invocable_r)>::value); + +// But not a result type where the conversion doesn't work. +static_assert(!std::is_invocable_r)>::value); +static_assert(!std::is_invocable_r)>::value); + +// Non-moveable result type + +// Define a type that can't be move-constructed. +struct CantMove { + CantMove() = default; + CantMove(CantMove&&) = delete; +}; + +static_assert(!std::is_move_constructible_v); +static_assert(!std::is_copy_constructible_v); + +// Define functions that return that type. +CantMove MakeCantMove() { return {}; } +CantMove MakeCantMoveWithArg(int) { return {}; } + +// Assumption check: it should be possible to call one of those functions and +// use it to initialize a CantMove object. +CantMove cant_move = MakeCantMove(); + +// Therefore std::is_invocable_r should agree that they can be invoked to yield +// a CantMove. +static_assert(std::is_invocable_r::value); +static_assert(std::is_invocable_r::value); + +// Of course it still shouldn't be possible to call one of the functions and get +// back some other type. +static_assert(!std::is_invocable_r::value); + +// And the argument types should still be important. +static_assert(!std::is_invocable_r::value); +static_assert(!std::is_invocable_r::value); + +// is_invocable_r + +// The struct form should be available too, not just the _v variant. +static_assert(std::is_invocable_r)>::value); +static_assert(!std::is_invocable_r)>::value); diff --git a/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r_v.compile.pass.cpp b/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r_v.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.rel/is_invocable_r_v.compile.pass.cpp @@ -0,0 +1,103 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// is_invocable_r + +#include + +// Non-invocable types + +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); + +// Result type matches + +template +T Return(); + +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// void result type + +// Any actual return type should be useable with a result type of void. +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// const- and volatile-qualified void should work too. +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// Conversion of result type + +// It should be possible to use a result type to which the actual return type +// can be converted. +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); +static_assert(std::is_invocable_r_v)>); + +// But not a result type where the conversion doesn't work. +static_assert(!std::is_invocable_r_v)>); +static_assert(!std::is_invocable_r_v)>); + +// Non-moveable result type + +// Define a type that can't be move-constructed. +struct CantMove { + CantMove() = default; + CantMove(CantMove&&) = delete; +}; + +static_assert(!std::is_move_constructible_v); +static_assert(!std::is_copy_constructible_v); + +// Define functions that return that type. +CantMove MakeCantMove() { return {}; } +CantMove MakeCantMoveWithArg(int) { return {}; } + +// Assumption check: it should be possible to call one of those functions and +// use it to initialize a CantMove object. +CantMove cant_move = MakeCantMove(); + +// Therefore std::is_invocable_r should agree that they can be invoked to yield +// a CantMove. +static_assert(std::is_invocable_r_v); +static_assert(std::is_invocable_r_v); + +// Of course it still shouldn't be possible to call one of the functions and get +// back some other type. +static_assert(!std::is_invocable_r_v); + +// And the argument types should still be important. +static_assert(!std::is_invocable_r_v); +static_assert(!std::is_invocable_r_v); + +// is_invocable_r + +// The struct form should be available too, not just the _v variant. +static_assert(std::is_invocable_r)>::value); +static_assert(!std::is_invocable_r)>::value); diff --git a/libcxx/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp b/libcxx/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp --- a/libcxx/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp +++ b/libcxx/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp @@ -185,6 +185,21 @@ static_assert(std::is_nothrow_invocable_r::value, ""); static_assert(throws_invocable_r(), ""); } + { + // Check that it's fine if the result type is non-moveable. + struct CantMove { + CantMove() = default; + CantMove(CantMove&&) = delete; + }; + + static_assert(!std::is_move_constructible_v); + static_assert(!std::is_copy_constructible_v); + + using Fn = CantMove() noexcept; + + static_assert(std::is_nothrow_invocable_r::value); + static_assert(!std::is_nothrow_invocable_r::value); + } { // Check for is_nothrow_invocable_v using Fn = CallObject;