diff --git a/stl/inc/type_traits b/stl/inc/type_traits index 477f3a47e12..cae9e1b0baf 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -1657,6 +1657,32 @@ template using is_nothrow_convertible = _Is_nothrow_convertible<_From, _To>; #endif // _HAS_CXX20 +template +_Ty _Returns_exactly() noexcept; // not defined + +template +struct _Invoke_convertible : false_type {}; + +template +struct _Invoke_convertible<_From, _To, void_t(_Returns_exactly<_From>()))>> + : true_type {}; + +template +struct _Invoke_nothrow_convertible : bool_constant(_Returns_exactly<_From>()))> {}; + +template +struct _Invoke_traits_common { + using type = _Result; + using _Is_invocable = true_type; + using _Is_nothrow_invocable = bool_constant<_Nothrow>; + template + using _Is_invocable_r = bool_constant, _Invoke_convertible>>; + template + using _Is_nothrow_invocable_r = bool_constant, + conjunction<_Invoke_convertible, _Invoke_nothrow_convertible>>>>; +}; + template struct _Invoke_traits_zero { // selected when _Callable isn't callable with zero _Args @@ -1672,17 +1698,8 @@ template using _Decltype_invoke_zero = decltype(_STD declval<_Callable>()()); template -struct _Invoke_traits_zero>, _Callable> { - // selected when _Callable is callable with zero _Args - using type = _Decltype_invoke_zero<_Callable>; - using _Is_invocable = true_type; - using _Is_nothrow_invocable = bool_constant()())>; - template - using _Is_invocable_r = bool_constant, is_convertible>>; - template - using _Is_nothrow_invocable_r = bool_constant< - conjunction_v<_Is_nothrow_invocable, disjunction, _Is_nothrow_convertible>>>; -}; +struct _Invoke_traits_zero>, _Callable> + : _Invoke_traits_common<_Decltype_invoke_zero<_Callable>, noexcept(_STD declval<_Callable>()())> {}; template struct _Invoke_traits_nonzero { @@ -1701,18 +1718,9 @@ using _Decltype_invoke_nonzero = decltype( template struct _Invoke_traits_nonzero>, _Callable, _Ty1, - _Types2...> { - // selected when _Callable is callable with nonzero _Args - using type = _Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>; - using _Is_invocable = true_type; - using _Is_nothrow_invocable = bool_constant::_Call( - _STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...))>; - template - using _Is_invocable_r = bool_constant, is_convertible>>; - template - using _Is_nothrow_invocable_r = bool_constant< - conjunction_v<_Is_nothrow_invocable, disjunction, _Is_nothrow_convertible>>>; -}; + _Types2...> : _Invoke_traits_common<_Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>, + noexcept(_Invoker1<_Callable, _Ty1>::_Call( + _STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...))> {}; template using _Select_invoke_traits = conditional_t, diff --git a/tests/std/tests/VSO_0105317_expression_sfinae/test.cpp b/tests/std/tests/VSO_0105317_expression_sfinae/test.cpp index e65467d1dd5..2884e162cbf 100644 --- a/tests/std/tests/VSO_0105317_expression_sfinae/test.cpp +++ b/tests/std/tests/VSO_0105317_expression_sfinae/test.cpp @@ -19,14 +19,16 @@ using namespace std; #if _HAS_CXX17 template struct HasInvokeResultT : false_type { - STATIC_ASSERT(!is_invocable_v); + STATIC_ASSERT(!is_nothrow_invocable_v); + STATIC_ASSERT(!is_invocable_r_v); + STATIC_ASSERT(!is_nothrow_invocable_r_v); }; template struct HasInvokeResultT>, Callable, Args...> : true_type { - STATIC_ASSERT(is_invocable_v); + STATIC_ASSERT(is_invocable_r_v); }; template @@ -584,6 +586,12 @@ STATIC_ASSERT(is_nothrow_invocable_r_v); STATIC_ASSERT(is_invocable_r_v); STATIC_ASSERT(!is_nothrow_invocable_r_v); +// When the return type is void, does the invocation throw? +STATIC_ASSERT(is_invocable_r_v); +STATIC_ASSERT(is_nothrow_invocable_r_v); +STATIC_ASSERT(is_invocable_r_v); +STATIC_ASSERT(!is_nothrow_invocable_r_v); + struct Puppy { explicit Puppy(int); Puppy(long) noexcept; @@ -605,6 +613,38 @@ STATIC_ASSERT(is_nothrow_invocable_r_v); STATIC_ASSERT(is_invocable_r_v); STATIC_ASSERT(!is_nothrow_invocable_r_v); +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1026729 +// Defend against regression of VSO-963790, in which is_invocable_r mishandles non-movable return types +struct NonMovable { + NonMovable(NonMovable&&) = delete; + NonMovable(const NonMovable&) = delete; +}; + +template +NonMovable getNonMovable() noexcept(Nothrow); + +STATIC_ASSERT(is_invocable_r_v)>); +STATIC_ASSERT(is_invocable_r_v)>); +STATIC_ASSERT(!is_nothrow_invocable_r_v)>); +STATIC_ASSERT(is_nothrow_invocable_r_v)>); + +template +struct ConvertsToNonMovable { + operator NonMovable() const noexcept(Nothrow); +}; + +template +ConvertsToNonMovable getConvertsToNonMovable() noexcept(Nothrow); + +STATIC_ASSERT(is_invocable_r_v)>); +STATIC_ASSERT(is_invocable_r_v)>); +STATIC_ASSERT(is_invocable_r_v)>); +STATIC_ASSERT(is_invocable_r_v)>); +STATIC_ASSERT(!is_nothrow_invocable_r_v)>); +STATIC_ASSERT(!is_nothrow_invocable_r_v)>); +STATIC_ASSERT(!is_nothrow_invocable_r_v)>); +STATIC_ASSERT(is_nothrow_invocable_r_v)>); +#endif // TRANSITION, VSO-1026729 #endif // _HAS_CXX17