Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions stl/inc/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -1671,17 +1671,31 @@ struct _Invoke_traits_zero {
template <class _Callable>
using _Decltype_invoke_zero = decltype(_STD declval<_Callable>()());

template <class _Void, class _Rx, class _Callable>
struct _Invocable_r_zero_nonvoid : false_type {};

template <class _Rx, class _Callable>
struct _Invocable_r_zero_nonvoid<void_t<decltype(_Implicitly_convert_to<_Rx>(_STD declval<_Callable>()()))>, _Rx,
_Callable> : true_type {};

template <class _Rx, class _Callable>
struct _Is_nothrow_invocable_r_zero_impl
: bool_constant<noexcept(_Implicitly_convert_to<_Rx>(_STD declval<_Callable>()()))> {};

template <class _Callable>
struct _Is_nothrow_invocable_r_zero_impl<void, _Callable> : bool_constant<noexcept(_STD declval<_Callable>()())> {};

template <class _Callable>
struct _Invoke_traits_zero<void_t<_Decltype_invoke_zero<_Callable>>, _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<noexcept(_STD declval<_Callable>()())>;
template <class _Rx>
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, is_convertible<type, _Rx>>>;
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, _Invocable_r_zero_nonvoid<void, _Rx, _Callable>>>;
template <class _Rx>
using _Is_nothrow_invocable_r = bool_constant<
conjunction_v<_Is_nothrow_invocable, disjunction<is_void<_Rx>, _Is_nothrow_convertible<type, _Rx>>>>;
using _Is_nothrow_invocable_r =
bool_constant<conjunction_v<_Is_invocable_r<_Rx>, _Is_nothrow_invocable_r_zero_impl<_Rx, _Callable>>>;
};

template <class _Void, class... _Types>
Expand All @@ -1699,6 +1713,24 @@ template <class _Callable, class _Ty1, class... _Types2>
using _Decltype_invoke_nonzero = decltype(
_Invoker1<_Callable, _Ty1>::_Call(_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...));

template <class _Void, class _Rx, class _Callable, class _Ty1, class... _Types2>
struct _Invocable_r_nonzero_nonvoid : false_type {};

template <class _Rx, class _Callable, class _Ty1, class... _Types2>
struct _Invocable_r_nonzero_nonvoid<void_t<decltype(_Implicitly_convert_to<_Rx>(_Invoker1<_Callable, _Ty1>::_Call(
_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...)))>,
_Rx, _Callable, _Ty1, _Types2...> : true_type {};

template <class _Rx, class _Callable, class _Ty1, class... _Types2>
struct _Is_nothrow_invocable_r_nonzero_impl
: bool_constant<noexcept(_Implicitly_convert_to<_Rx>(_Invoker1<_Callable, _Ty1>::_Call(
_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...)))> {};

template <class _Callable, class _Ty1, class... _Types2>
struct _Is_nothrow_invocable_r_nonzero_impl<void, _Callable, _Ty1, _Types2...>
: bool_constant<noexcept(_Invoker1<_Callable, _Ty1>::_Call(
_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...))> {};

template <class _Callable, class _Ty1, class... _Types2>
struct _Invoke_traits_nonzero<void_t<_Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>>, _Callable, _Ty1,
_Types2...> {
Expand All @@ -1708,10 +1740,10 @@ struct _Invoke_traits_nonzero<void_t<_Decltype_invoke_nonzero<_Callable, _Ty1, _
using _Is_nothrow_invocable = bool_constant<noexcept(_Invoker1<_Callable, _Ty1>::_Call(
_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...))>;
template <class _Rx>
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, is_convertible<type, _Rx>>>;
using _Is_invocable_r = _Invocable_r_nonzero_nonvoid<void, _Rx, _Callable, _Ty1, _Types2...>;
template <class _Rx>
using _Is_nothrow_invocable_r = bool_constant<
conjunction_v<_Is_nothrow_invocable, disjunction<is_void<_Rx>, _Is_nothrow_convertible<type, _Rx>>>>;
conjunction_v<_Is_invocable_r<_Rx>, _Is_nothrow_invocable_r_nonzero_impl<_Rx, _Callable, _Ty1, _Types2...>>>;
};

template <class _Callable, class... _Args>
Expand Down
46 changes: 44 additions & 2 deletions tests/std/tests/VSO_0105317_expression_sfinae/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ using namespace std;
#if _HAS_CXX17
template <typename Void, typename Callable, typename... Args>
struct HasInvokeResultT : false_type {

STATIC_ASSERT(!is_invocable_v<Callable, Args...>);
STATIC_ASSERT(!is_nothrow_invocable_v<Callable, Args...>);
STATIC_ASSERT(!is_invocable_r_v<void, Callable, Args...>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<void, Callable, Args...>);
};

template <typename Callable, typename... Args>
struct HasInvokeResultT<void_t<invoke_result_t<Callable, Args...>>, Callable, Args...> : true_type {

STATIC_ASSERT(is_invocable_v<Callable, Args...>);
STATIC_ASSERT(is_invocable_r_v<void, Callable, Args...>);
};

template <typename A>
Expand Down Expand Up @@ -584,6 +586,12 @@ STATIC_ASSERT(is_nothrow_invocable_r_v<long, Kitty, int>);
STATIC_ASSERT(is_invocable_r_v<long, Kitty, int, int>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<long, Kitty, int, int>);

// When the return type is void, does the invocation throw?
STATIC_ASSERT(is_invocable_r_v<void, Kitty, int>);
STATIC_ASSERT(is_nothrow_invocable_r_v<void, Kitty, int>);
STATIC_ASSERT(is_invocable_r_v<void, Kitty, int, int>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<void, Kitty, int, int>);

struct Puppy {
explicit Puppy(int);
Puppy(long) noexcept;
Expand All @@ -605,6 +613,40 @@ STATIC_ASSERT(is_nothrow_invocable_r_v<Puppy, Kitty, int>);
STATIC_ASSERT(is_invocable_r_v<Zebra, Kitty, int>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<Zebra, Kitty, int>);

#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 <bool Nothrow>
NonMovable getNonMovable() noexcept(Nothrow);

STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getNonMovable<false>)>);
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getNonMovable<true>)>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getNonMovable<false>)>);
STATIC_ASSERT(is_nothrow_invocable_r_v<NonMovable, decltype(&getNonMovable<true>)>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getNonMovable<false>)>);
STATIC_ASSERT(is_nothrow_invocable_r_v<NonMovable, decltype(&getNonMovable<true>)>);

template <bool Nothrow>
struct ConvertsToNonMovable {
operator NonMovable() const noexcept(Nothrow);
};

template <bool Nothrow, bool NothrowReturn>
ConvertsToNonMovable<NothrowReturn> getConvertsToNonMovable() noexcept(Nothrow);

STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, false>)>);
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, true>)>);
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, false>)>);
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, true>)>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, false>)>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, true>)>);
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, false>)>);
STATIC_ASSERT(is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, true>)>);
#endif // TRANSITION, VSO-1026729
#endif // _HAS_CXX17


Expand Down