Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
54 changes: 31 additions & 23 deletions stl/inc/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,32 @@ template <class _From, class _To>
using is_nothrow_convertible = _Is_nothrow_convertible<_From, _To>;
#endif // _HAS_CXX20

template <class _Ty>
_Ty _Returns_exactly() noexcept; // not defined

template <class _From, class _To, class = void>
struct _Invoke_convertible : false_type {};

template <class _From, class _To>
struct _Invoke_convertible<_From, _To, void_t<decltype(_Implicitly_convert_to<_To>(_Returns_exactly<_From>()))>>
: true_type {};

template <class _From, class _To>
struct _Invoke_nothrow_convertible : bool_constant<noexcept(_Implicitly_convert_to<_To>(_Returns_exactly<_From>()))> {};

template <class _Result, bool _Nothrow>
struct _Invoke_traits_common {
using type = _Result;
using _Is_invocable = true_type;
using _Is_nothrow_invocable = bool_constant<_Nothrow>;
template <class _Rx>
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, _Invoke_convertible<type, _Rx>>>;
template <class _Rx>
using _Is_nothrow_invocable_r = bool_constant<conjunction_v<_Is_nothrow_invocable,
disjunction<is_void<_Rx>,
conjunction<_Invoke_convertible<type, _Rx>, _Invoke_nothrow_convertible<type, _Rx>>>>>;
};

template <class _Void, class _Callable>
struct _Invoke_traits_zero {
// selected when _Callable isn't callable with zero _Args
Expand All @@ -1672,17 +1698,8 @@ template <class _Callable>
using _Decltype_invoke_zero = decltype(_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>>>;
template <class _Rx>
using _Is_nothrow_invocable_r = bool_constant<
conjunction_v<_Is_nothrow_invocable, disjunction<is_void<_Rx>, _Is_nothrow_convertible<type, _Rx>>>>;
};
struct _Invoke_traits_zero<void_t<_Decltype_invoke_zero<_Callable>>, _Callable>
: _Invoke_traits_common<_Decltype_invoke_zero<_Callable>, noexcept(_STD declval<_Callable>()())> {};

template <class _Void, class... _Types>
struct _Invoke_traits_nonzero {
Expand All @@ -1701,18 +1718,9 @@ using _Decltype_invoke_nonzero = decltype(

template <class _Callable, class _Ty1, class... _Types2>
struct _Invoke_traits_nonzero<void_t<_Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>>, _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<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>>>;
template <class _Rx>
using _Is_nothrow_invocable_r = bool_constant<
conjunction_v<_Is_nothrow_invocable, disjunction<is_void<_Rx>, _Is_nothrow_convertible<type, _Rx>>>>;
};
_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 <class _Callable, class... _Args>
using _Select_invoke_traits = conditional_t<sizeof...(_Args) == 0, _Invoke_traits_zero<void, _Callable>,
Expand Down
44 changes: 42 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,38 @@ 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>)>);

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