diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits index b56f5395dd2611..5908850cba5edd 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -2992,16 +2992,10 @@ struct __invokable_r // or incomplete array types as required by the standard. using _Result = decltype(__try_call<_Fp, _Args...>(0)); - using type = - typename conditional< + using type = typename conditional< _IsNotSame<_Result, __nat>::value, - typename conditional< - is_void<_Ret>::value, - true_type, - is_convertible<_Result, _Ret> - >::type, - false_type - >::type; + typename conditional< is_void<_Ret>::value, true_type, __is_core_convertible<_Result, _Ret> >::type, + false_type >::type; static const bool value = type::value; }; template 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 index 00000000000000..778b24d99e91e9 --- /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 index 00000000000000..32530f2c208652 --- /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 index 63ecc202ca83d0..fa6048e869e182 100644 --- 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,24 @@ int main(int, char**) { 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); + + static_assert(std::is_nothrow_invocable_r_v); + static_assert(!std::is_nothrow_invocable_r_v); + } { // Check for is_nothrow_invocable_v using Fn = CallObject;