Skip to content

Commit 7a9998c

Browse files
Account for C++17 deferred temporary materialization in invocable_r (#1282)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent d49e401 commit 7a9998c

File tree

2 files changed

+73
-25
lines changed

2 files changed

+73
-25
lines changed

stl/inc/type_traits

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,32 @@ template <class _From, class _To>
16571657
using is_nothrow_convertible = _Is_nothrow_convertible<_From, _To>;
16581658
#endif // _HAS_CXX20
16591659

1660+
template <class _Ty>
1661+
_Ty _Returns_exactly() noexcept; // not defined
1662+
1663+
template <class _From, class _To, class = void>
1664+
struct _Invoke_convertible : false_type {};
1665+
1666+
template <class _From, class _To>
1667+
struct _Invoke_convertible<_From, _To, void_t<decltype(_Implicitly_convert_to<_To>(_Returns_exactly<_From>()))>>
1668+
: true_type {};
1669+
1670+
template <class _From, class _To>
1671+
struct _Invoke_nothrow_convertible : bool_constant<noexcept(_Implicitly_convert_to<_To>(_Returns_exactly<_From>()))> {};
1672+
1673+
template <class _Result, bool _Nothrow>
1674+
struct _Invoke_traits_common {
1675+
using type = _Result;
1676+
using _Is_invocable = true_type;
1677+
using _Is_nothrow_invocable = bool_constant<_Nothrow>;
1678+
template <class _Rx>
1679+
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, _Invoke_convertible<type, _Rx>>>;
1680+
template <class _Rx>
1681+
using _Is_nothrow_invocable_r = bool_constant<conjunction_v<_Is_nothrow_invocable,
1682+
disjunction<is_void<_Rx>,
1683+
conjunction<_Invoke_convertible<type, _Rx>, _Invoke_nothrow_convertible<type, _Rx>>>>>;
1684+
};
1685+
16601686
template <class _Void, class _Callable>
16611687
struct _Invoke_traits_zero {
16621688
// selected when _Callable isn't callable with zero _Args
@@ -1672,17 +1698,8 @@ template <class _Callable>
16721698
using _Decltype_invoke_zero = decltype(_STD declval<_Callable>()());
16731699

16741700
template <class _Callable>
1675-
struct _Invoke_traits_zero<void_t<_Decltype_invoke_zero<_Callable>>, _Callable> {
1676-
// selected when _Callable is callable with zero _Args
1677-
using type = _Decltype_invoke_zero<_Callable>;
1678-
using _Is_invocable = true_type;
1679-
using _Is_nothrow_invocable = bool_constant<noexcept(_STD declval<_Callable>()())>;
1680-
template <class _Rx>
1681-
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, is_convertible<type, _Rx>>>;
1682-
template <class _Rx>
1683-
using _Is_nothrow_invocable_r = bool_constant<
1684-
conjunction_v<_Is_nothrow_invocable, disjunction<is_void<_Rx>, _Is_nothrow_convertible<type, _Rx>>>>;
1685-
};
1701+
struct _Invoke_traits_zero<void_t<_Decltype_invoke_zero<_Callable>>, _Callable>
1702+
: _Invoke_traits_common<_Decltype_invoke_zero<_Callable>, noexcept(_STD declval<_Callable>()())> {};
16861703

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

17021719
template <class _Callable, class _Ty1, class... _Types2>
17031720
struct _Invoke_traits_nonzero<void_t<_Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>>, _Callable, _Ty1,
1704-
_Types2...> {
1705-
// selected when _Callable is callable with nonzero _Args
1706-
using type = _Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>;
1707-
using _Is_invocable = true_type;
1708-
using _Is_nothrow_invocable = bool_constant<noexcept(_Invoker1<_Callable, _Ty1>::_Call(
1709-
_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...))>;
1710-
template <class _Rx>
1711-
using _Is_invocable_r = bool_constant<disjunction_v<is_void<_Rx>, is_convertible<type, _Rx>>>;
1712-
template <class _Rx>
1713-
using _Is_nothrow_invocable_r = bool_constant<
1714-
conjunction_v<_Is_nothrow_invocable, disjunction<is_void<_Rx>, _Is_nothrow_convertible<type, _Rx>>>>;
1715-
};
1721+
_Types2...> : _Invoke_traits_common<_Decltype_invoke_nonzero<_Callable, _Ty1, _Types2...>,
1722+
noexcept(_Invoker1<_Callable, _Ty1>::_Call(
1723+
_STD declval<_Callable>(), _STD declval<_Ty1>(), _STD declval<_Types2>()...))> {};
17161724

17171725
template <class _Callable, class... _Args>
17181726
using _Select_invoke_traits = conditional_t<sizeof...(_Args) == 0, _Invoke_traits_zero<void, _Callable>,

tests/std/tests/VSO_0105317_expression_sfinae/test.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ using namespace std;
1919
#if _HAS_CXX17
2020
template <typename Void, typename Callable, typename... Args>
2121
struct HasInvokeResultT : false_type {
22-
2322
STATIC_ASSERT(!is_invocable_v<Callable, Args...>);
23+
STATIC_ASSERT(!is_nothrow_invocable_v<Callable, Args...>);
24+
STATIC_ASSERT(!is_invocable_r_v<void, Callable, Args...>);
25+
STATIC_ASSERT(!is_nothrow_invocable_r_v<void, Callable, Args...>);
2426
};
2527

2628
template <typename Callable, typename... Args>
2729
struct HasInvokeResultT<void_t<invoke_result_t<Callable, Args...>>, Callable, Args...> : true_type {
28-
2930
STATIC_ASSERT(is_invocable_v<Callable, Args...>);
31+
STATIC_ASSERT(is_invocable_r_v<void, Callable, Args...>);
3032
};
3133

3234
template <typename A>
@@ -584,6 +586,12 @@ STATIC_ASSERT(is_nothrow_invocable_r_v<long, Kitty, int>);
584586
STATIC_ASSERT(is_invocable_r_v<long, Kitty, int, int>);
585587
STATIC_ASSERT(!is_nothrow_invocable_r_v<long, Kitty, int, int>);
586588

589+
// When the return type is void, does the invocation throw?
590+
STATIC_ASSERT(is_invocable_r_v<void, Kitty, int>);
591+
STATIC_ASSERT(is_nothrow_invocable_r_v<void, Kitty, int>);
592+
STATIC_ASSERT(is_invocable_r_v<void, Kitty, int, int>);
593+
STATIC_ASSERT(!is_nothrow_invocable_r_v<void, Kitty, int, int>);
594+
587595
struct Puppy {
588596
explicit Puppy(int);
589597
Puppy(long) noexcept;
@@ -605,6 +613,38 @@ STATIC_ASSERT(is_nothrow_invocable_r_v<Puppy, Kitty, int>);
605613
STATIC_ASSERT(is_invocable_r_v<Zebra, Kitty, int>);
606614
STATIC_ASSERT(!is_nothrow_invocable_r_v<Zebra, Kitty, int>);
607615

616+
#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1026729
617+
// Defend against regression of VSO-963790, in which is_invocable_r mishandles non-movable return types
618+
struct NonMovable {
619+
NonMovable(NonMovable&&) = delete;
620+
NonMovable(const NonMovable&) = delete;
621+
};
622+
623+
template <bool Nothrow>
624+
NonMovable getNonMovable() noexcept(Nothrow);
625+
626+
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getNonMovable<false>)>);
627+
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getNonMovable<true>)>);
628+
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getNonMovable<false>)>);
629+
STATIC_ASSERT(is_nothrow_invocable_r_v<NonMovable, decltype(&getNonMovable<true>)>);
630+
631+
template <bool Nothrow>
632+
struct ConvertsToNonMovable {
633+
operator NonMovable() const noexcept(Nothrow);
634+
};
635+
636+
template <bool Nothrow, bool NothrowReturn>
637+
ConvertsToNonMovable<NothrowReturn> getConvertsToNonMovable() noexcept(Nothrow);
638+
639+
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, false>)>);
640+
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, true>)>);
641+
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, false>)>);
642+
STATIC_ASSERT(is_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, true>)>);
643+
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, false>)>);
644+
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<false, true>)>);
645+
STATIC_ASSERT(!is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, false>)>);
646+
STATIC_ASSERT(is_nothrow_invocable_r_v<NonMovable, decltype(&getConvertsToNonMovable<true, true>)>);
647+
#endif // TRANSITION, VSO-1026729
608648
#endif // _HAS_CXX17
609649

610650

0 commit comments

Comments
 (0)