Index: include/__hash_table =================================================================== --- include/__hash_table +++ include/__hash_table @@ -874,9 +874,9 @@ template #ifndef _LIBCPP_CXX03_LANG - _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Equal const&, _Key const&, _Key const&>::value, + _LIBCPP_DIAGNOSE_WARNING(!__flexible_invokable<_Equal const&, _Key const&, _Key const&>::value, "the specified comparator type does not provide a const call operator") - _LIBCPP_DIAGNOSE_WARNING(!__invokable<_Hash const&, _Key const&>::value, + _LIBCPP_DIAGNOSE_WARNING(!__flexible_invokable<_Hash const&, _Key const&>::value, "the specified hash functor does not provide a const call operator") #endif typename __enforce_unordered_container_requirements<_Key, _Hash, _Equal>::type Index: include/__tree =================================================================== --- include/__tree +++ include/__tree @@ -970,7 +970,7 @@ template #ifndef _LIBCPP_CXX03_LANG - _LIBCPP_DIAGNOSE_WARNING(!std::__invokable<_Compare const&, _Tp const&, _Tp const&>::value, + _LIBCPP_DIAGNOSE_WARNING(!std::__flexible_invokable<_Compare const&, _Tp const&, _Tp const&>::value, "the specified comparator type does not provide a const call operator") #endif int __diagnose_non_const_comparator(); Index: include/type_traits =================================================================== --- include/type_traits +++ include/type_traits @@ -1355,6 +1355,9 @@ template using remove_all_extents_t = typename remove_all_extents<_Tp>::type; #endif +template struct _LIBCPP_TEMPLATE_VIS __is_unbounded_array : false_type {}; +template struct _LIBCPP_TEMPLATE_VIS __is_unbounded_array<_Tp[]> : true_type {}; + #if _LIBCPP_STD_VER > 17 // is_bounded_array @@ -1367,8 +1370,8 @@ // is_unbounded_array -template struct _LIBCPP_TEMPLATE_VIS is_unbounded_array : false_type {}; -template struct _LIBCPP_TEMPLATE_VIS is_unbounded_array<_Tp[]> : true_type {}; +template struct _LIBCPP_TEMPLATE_VIS is_unbounded_array : + __is_unbounded_array<_Tp> {}; template _LIBCPP_INLINE_VAR _LIBCPP_CONSTEXPR @@ -4406,18 +4409,107 @@ #undef _LIBCPP_INVOKE_RETURN +template +struct __remove_all_bound_extents +{ using type = _Tp; }; + +template +struct __remove_all_bound_extents<_Tp[]> +{ using type = _Tp[]; }; + +template +struct __remove_all_bound_extents<_Tp[__n]> +{ using type = _Tp; }; + +template +struct __make_pure +{ + using type = typename remove_reference< + typename remove_pointer< + typename __remove_all_bound_extents<_Tp>::type + >::type + >::type; +}; + +template struct __complete_type : true_type { }; + +template +struct __complete_type<_Tp> +{ + template + static true_type __test(int); + + template + static false_type __test(...); + + template + using __test_validity = decltype(__test<_T2>(0)); + + using __pure_type = typename __make_pure<_Tp>::type; + + using type = typename conditional< + is_function<__pure_type>::value, + true_type, + __test_validity<__pure_type> + >::type; + + static const bool value = type::value; +}; + +template +struct __complete_type<_Tp, _T2, _Args...> +{ + static const bool value = __complete_type<_Tp>::value && + __complete_type<_T2>::value && + __complete_type<_Args...>::value; +}; + +template struct __valid_invokable_type : true_type { }; + +template +struct __valid_invokable_type<_Tp> +{ + using __pure_type = typename __make_pure<_Tp>::type; + + static const bool value = __complete_type<_Tp>::value || // already makes type pure + is_void<__pure_type>::value || + __is_unbounded_array<__pure_type>::value; +}; + +template +struct __valid_invokable_type<_Tp, _T2, _Args...> +{ + static const bool value = __valid_invokable_type<_Tp>::value && + __valid_invokable_type<_T2>::value && + __valid_invokable_type<_Args...>::value; +}; + +template +struct __flexible_invokable +{ + template + static auto __try_call(int) -> decltype(__invoke(declval<_XFp>(), + declval<_XArgs>()...)); + template + static __nat __try_call(...); + + using _Result = decltype(__try_call<_Fp, _Args...>(0)); + static const bool value = !is_same<_Result, __nat>::value; +}; + // __invokable template struct __invokable_r { + static_assert(__valid_invokable_type<_Ret, _Fp, _Args...>::value, + "_Ret, _Fp and all _Args must be complete types, void, or unbounded arrays"); + template - static auto __try_call(int) -> decltype( - _VSTD::__invoke(_VSTD::declval<_XFp>(), _VSTD::declval<_XArgs>()...)); + static auto __try_call(int) -> decltype(__invoke(declval<_XFp>(), + declval<_XArgs>()...)); template static __nat __try_call(...); - // FIXME: Check that _Ret, _Fp, and _Args... are all complete types, cv void, - // or incomplete array types as required by the standard. using _Result = decltype(__try_call<_Fp, _Args...>(0)); using type = @@ -4432,6 +4524,7 @@ >::type; static const bool value = type::value; }; + template using __invokable = __invokable_r; Index: include/utility =================================================================== --- include/utility +++ include/utility @@ -1593,7 +1593,7 @@ using __check_hash_requirements = integral_constant::value && is_move_constructible<_Hash>::value && - __invokable_r::value + __flexible_invokable<_Hash, _Key const&>::value >; template > Index: test/libcxx/type_traits/invokable.fail.cpp =================================================================== --- /dev/null +++ test/libcxx/type_traits/invokable.fail.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, c++03, c++11 + +#include + +// Testing internal templates +// __invokable_r tests if a function may be called using a given +// set of parameter types and a return type (or void). +// It is expected to fail (with a static assertion failure) when it +// is given an incomplete type +// (with the exception of void or an unbounded array). + +// Not defined +class Foo; +struct Bar; + +int main() +{ + static_assert(!std::__invokable_r::value); + static_assert(!std::__invokable_r::value); + static_assert(!std::__invokable_r::value); + static_assert(!std::__invokable_r::value); + + return 0; +} \ No newline at end of file Index: test/libcxx/type_traits/invokable.pass.cpp =================================================================== --- /dev/null +++ test/libcxx/type_traits/invokable.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++98, c++03, c++11 + +#include + +// Testing internal templates +// __complete_type checks if type is "complete". +// __invokable_r tests if a function may be called using a given +// set of parameter types and a return type (or void). + +struct Foo { + int operator()(int a, int b) { return a + b; } +}; + +struct Baz { + int operator() (int const & a, int const & b) const { return a + b; } +}; + +struct Bar; // Not defined + +int func(int) { return true; } + +void simple () { } + +int main() +{ + const int arr[4][4][4] = {{{}}}; + const int (*i)[4]; + + static_assert( std::__complete_type::value); + static_assert( std::__complete_type::value); + static_assert( std::__complete_type::value); + static_assert( std::__complete_type::value); + static_assert(!std::__complete_type::value); + static_assert(!std::__complete_type::value); + static_assert(!std::__complete_type::value); + static_assert(!std::__complete_type::value); + + static_assert( std::__invokable_r::value); + static_assert( std::__invokable_r::value); + static_assert( std::__invokable_r::value); + static_assert( std::__invokable_r::value); + static_assert( std::__invokable_r::value); + static_assert( std::__invokable_r::value); + static_assert(!std::__invokable_r::value); + static_assert(!std::__invokable_r::value); + + return 0; +} \ No newline at end of file