diff options
author | Eric Fiselier <eric@efcs.ca> | 2019-01-29 18:01:14 +0000 |
---|---|---|
committer | Eric Fiselier <eric@efcs.ca> | 2019-01-29 18:01:14 +0000 |
commit | a32a775e66e7141185ef83ca225cbc4799cb70bf (patch) | |
tree | dd43bc4e8427d72b651a5b112647ec70562fc247 | |
parent | e0324cb307cc5dd2974faa3e0f2d8693c0aec9a0 (diff) | |
download | libcxx-a32a775e66e7141185ef83ca225cbc4799cb70bf.tar.gz |
Fix PR40495 - is_invokable_v<void> does not compile
The meta-programming that attempted to form the invoke call expression
was not in a SFINAE context. This made it a hard error to provide
non-referencable types like 'void' or 'void (...) const'.
This patch fixes the error by checking the validity of the call
expression within a SFINAE context.
git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@352522 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/type_traits | 37 | ||||
-rw-r--r-- | test/std/utilities/meta/meta.rel/is_invocable.pass.cpp | 305 | ||||
-rw-r--r-- | test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp | 233 |
3 files changed, 376 insertions, 199 deletions
diff --git a/include/type_traits b/include/type_traits index 52a46adc5..37b7ca1a3 100644 --- a/include/type_traits +++ b/include/type_traits @@ -4360,28 +4360,31 @@ _LIBCPP_INVOKE_RETURN(_VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...) #undef _LIBCPP_INVOKE_RETURN // __invokable - template <class _Ret, class _Fp, class ..._Args> struct __invokable_r { - // 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( - _VSTD::__invoke(_VSTD::declval<_Fp>(), _VSTD::declval<_Args>()...)); + template <class _XFp, class ..._XArgs> + static auto __try_call(int) -> decltype( + _VSTD::__invoke(_VSTD::declval<_XFp>(), _VSTD::declval<_XArgs>()...)); + template <class _XFp, class ..._XArgs> + static __nat __try_call(...); - using type = - typename conditional< - !is_same<_Result, __nat>::value, - typename conditional< - is_void<_Ret>::value, - true_type, - is_convertible<_Result, _Ret> - >::type, - false_type - >::type; - static const bool value = type::value; -}; + // 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 = + typename conditional< + !is_same<_Result, __nat>::value, + typename conditional< + is_void<_Ret>::value, + true_type, + is_convertible<_Result, _Ret> + >::type, + false_type + >::type; + static const bool value = type::value; +}; template <class _Fp, class ..._Args> using __invokable = __invokable_r<void, _Fp, _Args...>; diff --git a/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp b/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp index a2dc09072..dab17974b 100644 --- a/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp +++ b/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp @@ -15,9 +15,13 @@ // Most testing of is_invocable is done within the [meta.trans.other] result_of // tests. +// Fn and all types in the template parameter pack ArgTypes shall be +// complete types, cv void, or arrays of unknown bound. + #include <type_traits> #include <functional> #include <memory> +#include <vector> #include "test_macros.h" @@ -37,129 +41,204 @@ struct NotCallableWithInt { int operator()(Tag) { return 42; } }; -int main() -{ +struct Sink { + template <class ...Args> + void operator()(Args&&...) const {} +}; + +int main() { + using AbominableFunc = void(...) const; + + // Non-callable things + { + static_assert(!std::is_invocable<void>::value, ""); + static_assert(!std::is_invocable<const void>::value, ""); + static_assert(!std::is_invocable<volatile void>::value, ""); + static_assert(!std::is_invocable<const volatile void>::value, ""); + static_assert(!std::is_invocable<std::nullptr_t>::value, ""); + static_assert(!std::is_invocable<int>::value, ""); + static_assert(!std::is_invocable<double>::value, ""); + + static_assert(!std::is_invocable<int[]>::value, ""); + static_assert(!std::is_invocable<int[3]>::value, ""); + + static_assert(!std::is_invocable<int*>::value, ""); + static_assert(!std::is_invocable<const int*>::value, ""); + static_assert(!std::is_invocable<int const*>::value, ""); + + static_assert(!std::is_invocable<int&>::value, ""); + static_assert(!std::is_invocable<const int&>::value, ""); + static_assert(!std::is_invocable<int&&>::value, ""); + + static_assert(!std::is_invocable<std::vector<int> >::value, ""); + static_assert(!std::is_invocable<std::vector<int*> >::value, ""); + static_assert(!std::is_invocable<std::vector<int**> >::value, ""); + + static_assert(!std::is_invocable<AbominableFunc>::value, ""); + + // with parameters + static_assert(!std::is_invocable<int, int>::value, ""); + static_assert(!std::is_invocable<int, double, float>::value, ""); + static_assert(!std::is_invocable<int, char, float, double>::value, ""); + static_assert(!std::is_invocable<Sink, AbominableFunc>::value, ""); + static_assert(!std::is_invocable<Sink, void>::value, ""); + static_assert(!std::is_invocable<Sink, const volatile void>::value, + ""); + + + static_assert(!std::is_invocable_r<int, void>::value, ""); + static_assert(!std::is_invocable_r<int, const void>::value, ""); + static_assert(!std::is_invocable_r<int, volatile void>::value, ""); + static_assert(!std::is_invocable_r<int, const volatile void>::value, ""); + static_assert(!std::is_invocable_r<int, std::nullptr_t>::value, ""); + static_assert(!std::is_invocable_r<int, int>::value, ""); + static_assert(!std::is_invocable_r<int, double>::value, ""); + + static_assert(!std::is_invocable_r<int, int[]>::value, ""); + static_assert(!std::is_invocable_r<int, int[3]>::value, ""); + + static_assert(!std::is_invocable_r<int, int*>::value, ""); + static_assert(!std::is_invocable_r<int, const int*>::value, ""); + static_assert(!std::is_invocable_r<int, int const*>::value, ""); + + static_assert(!std::is_invocable_r<int, int&>::value, ""); + static_assert(!std::is_invocable_r<int, const int&>::value, ""); + static_assert(!std::is_invocable_r<int, int&&>::value, ""); + + static_assert(!std::is_invocable_r<int, std::vector<int> >::value, ""); + static_assert(!std::is_invocable_r<int, std::vector<int*> >::value, ""); + static_assert(!std::is_invocable_r<int, std::vector<int**> >::value, ""); + static_assert(!std::is_invocable_r<void, AbominableFunc>::value, ""); + + // with parameters + static_assert(!std::is_invocable_r<int, int, int>::value, ""); + static_assert(!std::is_invocable_r<int, int, double, float>::value, ""); + static_assert(!std::is_invocable_r<int, int, char, float, double>::value, + ""); + static_assert(!std::is_invocable_r<void, Sink, AbominableFunc>::value, ""); + static_assert(!std::is_invocable_r<void, Sink, void>::value, ""); + static_assert(!std::is_invocable_r<void, Sink, const volatile void>::value, + ""); + } + { + using Fn = int (Tag::*)(int); + using RFn = int (Tag::*)(int)&&; + // INVOKE bullet 1, 2 and 3 { - using Fn = int(Tag::*)(int); - using RFn = int(Tag::*)(int) &&; - // INVOKE bullet 1, 2 and 3 - { - // Bullet 1 - static_assert(std::is_invocable<Fn, Tag&, int>::value, ""); - static_assert(std::is_invocable<Fn, DerFromTag&, int>::value, ""); - static_assert(std::is_invocable<RFn, Tag&&, int>::value, ""); - static_assert(!std::is_invocable<RFn, Tag&, int>::value, ""); - static_assert(!std::is_invocable<Fn, Tag&>::value, ""); - static_assert(!std::is_invocable<Fn, Tag const&, int>::value, ""); - } - { - // Bullet 2 - using T = std::reference_wrapper<Tag>; - using DT = std::reference_wrapper<DerFromTag>; - using CT = std::reference_wrapper<const Tag>; - static_assert(std::is_invocable<Fn, T&, int>::value, ""); - static_assert(std::is_invocable<Fn, DT&, int>::value, ""); - static_assert(std::is_invocable<Fn, const T&, int>::value, ""); - static_assert(std::is_invocable<Fn, T&&, int>::value, ""); - static_assert(!std::is_invocable<Fn, CT&, int>::value, ""); - static_assert(!std::is_invocable<RFn, T, int>::value, ""); - } - { - // Bullet 3 - using T = Tag*; - using DT = DerFromTag*; - using CT = const Tag*; - using ST = std::unique_ptr<Tag>; - static_assert(std::is_invocable<Fn, T&, int>::value, ""); - static_assert(std::is_invocable<Fn, DT&, int>::value, ""); - static_assert(std::is_invocable<Fn, const T&, int>::value, ""); - static_assert(std::is_invocable<Fn, T&&, int>::value, ""); - static_assert(std::is_invocable<Fn, ST, int>::value, ""); - static_assert(!std::is_invocable<Fn, CT&, int>::value, ""); - static_assert(!std::is_invocable<RFn, T, int>::value, ""); - } + // Bullet 1 + static_assert(std::is_invocable<Fn, Tag&, int>::value, ""); + static_assert(std::is_invocable<Fn, DerFromTag&, int>::value, ""); + static_assert(std::is_invocable<RFn, Tag&&, int>::value, ""); + static_assert(!std::is_invocable<RFn, Tag&, int>::value, ""); + static_assert(!std::is_invocable<Fn, Tag&>::value, ""); + static_assert(!std::is_invocable<Fn, Tag const&, int>::value, ""); } { - // Bullets 4, 5 and 6 - using Fn = int (Tag::*); - static_assert(!std::is_invocable<Fn>::value, ""); - { - // Bullet 4 - static_assert(std::is_invocable<Fn, Tag&>::value, ""); - static_assert(std::is_invocable<Fn, DerFromTag&>::value, ""); - static_assert(std::is_invocable<Fn, Tag&&>::value, ""); - static_assert(std::is_invocable<Fn, Tag const&>::value, ""); - } - { - // Bullet 5 - using T = std::reference_wrapper<Tag>; - using DT = std::reference_wrapper<DerFromTag>; - using CT = std::reference_wrapper<const Tag>; - static_assert(std::is_invocable<Fn, T&>::value, ""); - static_assert(std::is_invocable<Fn, DT&>::value, ""); - static_assert(std::is_invocable<Fn, const T&>::value, ""); - static_assert(std::is_invocable<Fn, T&&>::value, ""); - static_assert(std::is_invocable<Fn, CT&>::value, ""); - } - { - // Bullet 6 - using T = Tag*; - using DT = DerFromTag*; - using CT = const Tag*; - using ST = std::unique_ptr<Tag>; - static_assert(std::is_invocable<Fn, T&>::value, ""); - static_assert(std::is_invocable<Fn, DT&>::value, ""); - static_assert(std::is_invocable<Fn, const T&>::value, ""); - static_assert(std::is_invocable<Fn, T&&>::value, ""); - static_assert(std::is_invocable<Fn, ST>::value, ""); - static_assert(std::is_invocable<Fn, CT&>::value, ""); - } + // Bullet 2 + using T = std::reference_wrapper<Tag>; + using DT = std::reference_wrapper<DerFromTag>; + using CT = std::reference_wrapper<const Tag>; + static_assert(std::is_invocable<Fn, T&, int>::value, ""); + static_assert(std::is_invocable<Fn, DT&, int>::value, ""); + static_assert(std::is_invocable<Fn, const T&, int>::value, ""); + static_assert(std::is_invocable<Fn, T&&, int>::value, ""); + static_assert(!std::is_invocable<Fn, CT&, int>::value, ""); + static_assert(!std::is_invocable<RFn, T, int>::value, ""); } { - // INVOKE bullet 7 - { - // Function pointer - using Fp = void(*)(Tag&, int); - static_assert(std::is_invocable<Fp, Tag&, int>::value, ""); - static_assert(std::is_invocable<Fp, DerFromTag&, int>::value, ""); - static_assert(!std::is_invocable<Fp, const Tag&, int>::value, ""); - static_assert(!std::is_invocable<Fp>::value, ""); - static_assert(!std::is_invocable<Fp, Tag&>::value, ""); - } - { - // Function reference - using Fp = void(&)(Tag&, int); - static_assert(std::is_invocable<Fp, Tag&, int>::value, ""); - static_assert(std::is_invocable<Fp, DerFromTag&, int>::value, ""); - static_assert(!std::is_invocable<Fp, const Tag&, int>::value, ""); - static_assert(!std::is_invocable<Fp>::value, ""); - static_assert(!std::is_invocable<Fp, Tag&>::value, ""); - } - { - // Function object - using Fn = NotCallableWithInt; - static_assert(std::is_invocable<Fn, Tag>::value, ""); - static_assert(!std::is_invocable<Fn, int>::value, ""); - } + // Bullet 3 + using T = Tag*; + using DT = DerFromTag*; + using CT = const Tag*; + using ST = std::unique_ptr<Tag>; + static_assert(std::is_invocable<Fn, T&, int>::value, ""); + static_assert(std::is_invocable<Fn, DT&, int>::value, ""); + static_assert(std::is_invocable<Fn, const T&, int>::value, ""); + static_assert(std::is_invocable<Fn, T&&, int>::value, ""); + static_assert(std::is_invocable<Fn, ST, int>::value, ""); + static_assert(!std::is_invocable<Fn, CT&, int>::value, ""); + static_assert(!std::is_invocable<RFn, T, int>::value, ""); } + } + { + // Bullets 4, 5 and 6 + using Fn = int(Tag::*); + static_assert(!std::is_invocable<Fn>::value, ""); { - // Check that the conversion to the return type is properly checked - using Fn = int(*)(); - static_assert(std::is_invocable_r<Implicit, Fn>::value, ""); - static_assert(std::is_invocable_r<double, Fn>::value, ""); - static_assert(std::is_invocable_r<const volatile void, Fn>::value, ""); - static_assert(!std::is_invocable_r<Explicit, Fn>::value, ""); + // Bullet 4 + static_assert(std::is_invocable<Fn, Tag&>::value, ""); + static_assert(std::is_invocable<Fn, DerFromTag&>::value, ""); + static_assert(std::is_invocable<Fn, Tag&&>::value, ""); + static_assert(std::is_invocable<Fn, Tag const&>::value, ""); } { - // Check for is_invocable_v - using Fn = void(*)(); - static_assert(std::is_invocable_v<Fn>, ""); - static_assert(!std::is_invocable_v<Fn, int>, ""); + // Bullet 5 + using T = std::reference_wrapper<Tag>; + using DT = std::reference_wrapper<DerFromTag>; + using CT = std::reference_wrapper<const Tag>; + static_assert(std::is_invocable<Fn, T&>::value, ""); + static_assert(std::is_invocable<Fn, DT&>::value, ""); + static_assert(std::is_invocable<Fn, const T&>::value, ""); + static_assert(std::is_invocable<Fn, T&&>::value, ""); + static_assert(std::is_invocable<Fn, CT&>::value, ""); } { - // Check for is_invocable_r_v - using Fn = void(*)(); - static_assert(std::is_invocable_r_v<void, Fn>, ""); - static_assert(!std::is_invocable_r_v<int, Fn>, ""); + // Bullet 6 + using T = Tag*; + using DT = DerFromTag*; + using CT = const Tag*; + using ST = std::unique_ptr<Tag>; + static_assert(std::is_invocable<Fn, T&>::value, ""); + static_assert(std::is_invocable<Fn, DT&>::value, ""); + static_assert(std::is_invocable<Fn, const T&>::value, ""); + static_assert(std::is_invocable<Fn, T&&>::value, ""); + static_assert(std::is_invocable<Fn, ST>::value, ""); + static_assert(std::is_invocable<Fn, CT&>::value, ""); } + } + { // INVOKE bullet 7 + {// Function pointer + using Fp = void(*)(Tag&, int); + static_assert(std::is_invocable<Fp, Tag&, int>::value, ""); + static_assert(std::is_invocable<Fp, DerFromTag&, int>::value, ""); + static_assert(!std::is_invocable<Fp, const Tag&, int>::value, ""); + static_assert(!std::is_invocable<Fp>::value, ""); + static_assert(!std::is_invocable<Fp, Tag&>::value, ""); +} +{ + // Function reference + using Fp = void (&)(Tag&, int); + static_assert(std::is_invocable<Fp, Tag&, int>::value, ""); + static_assert(std::is_invocable<Fp, DerFromTag&, int>::value, ""); + static_assert(!std::is_invocable<Fp, const Tag&, int>::value, ""); + static_assert(!std::is_invocable<Fp>::value, ""); + static_assert(!std::is_invocable<Fp, Tag&>::value, ""); +} +{ + // Function object + using Fn = NotCallableWithInt; + static_assert(std::is_invocable<Fn, Tag>::value, ""); + static_assert(!std::is_invocable<Fn, int>::value, ""); +} +} +{ + // Check that the conversion to the return type is properly checked + using Fn = int (*)(); + static_assert(std::is_invocable_r<Implicit, Fn>::value, ""); + static_assert(std::is_invocable_r<double, Fn>::value, ""); + static_assert(std::is_invocable_r<const volatile void, Fn>::value, ""); + static_assert(!std::is_invocable_r<Explicit, Fn>::value, ""); +} +{ + // Check for is_invocable_v + using Fn = void (*)(); + static_assert(std::is_invocable_v<Fn>, ""); + static_assert(!std::is_invocable_v<Fn, int>, ""); +} +{ + // Check for is_invocable_r_v + using Fn = void (*)(); + static_assert(std::is_invocable_r_v<void, Fn>, ""); + static_assert(!std::is_invocable_r_v<int, Fn>, ""); +} } diff --git a/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp b/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp index e4ce36b50..f21e99b02 100644 --- a/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp +++ b/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp @@ -14,6 +14,7 @@ #include <type_traits> #include <functional> +#include <vector> #include "test_macros.h" @@ -31,90 +32,184 @@ struct Explicit { explicit Explicit(int) noexcept {} }; -template <bool IsNoexcept, class Ret, class ...Args> +template <bool IsNoexcept, class Ret, class... Args> struct CallObject { Ret operator()(Args&&...) const noexcept(IsNoexcept); }; -template <class Fn, class ...Args> +struct Sink { + template <class... Args> + void operator()(Args&&...) const noexcept {} +}; + +template <class Fn, class... Args> constexpr bool throws_invocable() { - return std::is_invocable<Fn, Args...>::value && - !std::is_nothrow_invocable<Fn, Args...>::value; + return std::is_invocable<Fn, Args...>::value && + !std::is_nothrow_invocable<Fn, Args...>::value; } -template <class Ret, class Fn, class ...Args> +template <class Ret, class Fn, class... Args> constexpr bool throws_invocable_r() { - return std::is_invocable_r<Ret, Fn, Args...>::value && - !std::is_nothrow_invocable_r<Ret, Fn, Args...>::value; + return std::is_invocable_r<Ret, Fn, Args...>::value && + !std::is_nothrow_invocable_r<Ret, Fn, Args...>::value; } // FIXME(EricWF) Don't test the where noexcept is *not* part of the type system // once implementations have caught up. -void test_noexcept_function_pointers() -{ - struct Dummy { void foo() noexcept {} static void bar() noexcept {} }; +void test_noexcept_function_pointers() { + struct Dummy { + void foo() noexcept {} + static void bar() noexcept {} + }; #if !defined(__cpp_noexcept_function_type) - { - // Check that PMF's and function pointers *work*. is_nothrow_invocable will always - // return false because 'noexcept' is not part of the function type. - static_assert(throws_invocable<decltype(&Dummy::foo), Dummy&>(), ""); - static_assert(throws_invocable<decltype(&Dummy::bar)>(), ""); - } + { + // Check that PMF's and function pointers *work*. is_nothrow_invocable will always + // return false because 'noexcept' is not part of the function type. + static_assert(throws_invocable<decltype(&Dummy::foo), Dummy&>(), ""); + static_assert(throws_invocable<decltype(&Dummy::bar)>(), ""); + } #else - { - // Check that PMF's and function pointers actually work and that - // is_nothrow_invocable returns true for noexcept PMF's and function - // pointers. - static_assert(std::is_nothrow_invocable<decltype(&Dummy::foo), Dummy&>::value, ""); - static_assert(std::is_nothrow_invocable<decltype(&Dummy::bar)>::value, ""); - } + { + // Check that PMF's and function pointers actually work and that + // is_nothrow_invocable returns true for noexcept PMF's and function + // pointers. + static_assert( + std::is_nothrow_invocable<decltype(&Dummy::foo), Dummy&>::value, ""); + static_assert(std::is_nothrow_invocable<decltype(&Dummy::bar)>::value, ""); + } #endif } -int main() -{ - { - // Check that the conversion to the return type is properly checked - using Fn = CallObject<true, int>; - static_assert(std::is_nothrow_invocable_r<Implicit, Fn>::value, ""); - static_assert(std::is_nothrow_invocable_r<double, Fn>::value, ""); - static_assert(std::is_nothrow_invocable_r<const volatile void, Fn>::value, ""); - static_assert(throws_invocable_r<ThrowsImplicit, Fn>(), ""); - static_assert(!std::is_nothrow_invocable<Fn(), Explicit>(), ""); - } - { - // Check that the conversion to the parameters is properly checked - using Fn = CallObject<true, void, const Implicit&, const ThrowsImplicit&>; - static_assert(std::is_nothrow_invocable<Fn, Implicit&, ThrowsImplicit&>::value, ""); - static_assert(std::is_nothrow_invocable<Fn, int, ThrowsImplicit&>::value, ""); - static_assert(throws_invocable<Fn, int, int>(), ""); - static_assert(!std::is_nothrow_invocable<Fn>::value, ""); - } - { - // Check that the noexcept-ness of function objects is checked. - using Fn = CallObject<true, void>; - using Fn2 = CallObject<false, void>; - static_assert(std::is_nothrow_invocable<Fn>::value, ""); - static_assert(throws_invocable<Fn2>(), ""); - } - { - // Check that PMD derefs are noexcept - using Fn = int (Tag::*); - static_assert(std::is_nothrow_invocable<Fn, Tag&>::value, ""); - static_assert(std::is_nothrow_invocable_r<Implicit, Fn, Tag&>::value, ""); - static_assert(throws_invocable_r<ThrowsImplicit, Fn, Tag&>(), ""); - } - { - // Check for is_nothrow_invocable_v - using Fn = CallObject<true, int>; - static_assert(std::is_nothrow_invocable_v<Fn>, ""); - static_assert(!std::is_nothrow_invocable_v<Fn, int>, ""); - } - { - // Check for is_nothrow_invocable_r_v - using Fn = CallObject<true, int>; - static_assert(std::is_nothrow_invocable_r_v<void, Fn>, ""); - static_assert(!std::is_nothrow_invocable_r_v<int, Fn, int>, ""); - } - test_noexcept_function_pointers(); +int main() { + using AbominableFunc = void(...) const noexcept; + // Non-callable things + { + static_assert(!std::is_nothrow_invocable<void>::value, ""); + static_assert(!std::is_nothrow_invocable<const void>::value, ""); + static_assert(!std::is_nothrow_invocable<volatile void>::value, ""); + static_assert(!std::is_nothrow_invocable<const volatile void>::value, ""); + static_assert(!std::is_nothrow_invocable<std::nullptr_t>::value, ""); + static_assert(!std::is_nothrow_invocable<int>::value, ""); + static_assert(!std::is_nothrow_invocable<double>::value, ""); + + static_assert(!std::is_nothrow_invocable<int[]>::value, ""); + static_assert(!std::is_nothrow_invocable<int[3]>::value, ""); + + static_assert(!std::is_nothrow_invocable<int*>::value, ""); + static_assert(!std::is_nothrow_invocable<const int*>::value, ""); + static_assert(!std::is_nothrow_invocable<int const*>::value, ""); + + static_assert(!std::is_nothrow_invocable<int&>::value, ""); + static_assert(!std::is_nothrow_invocable<const int&>::value, ""); + static_assert(!std::is_nothrow_invocable<int&&>::value, ""); + + static_assert(!std::is_nothrow_invocable<int, std::vector<int> >::value, + ""); + static_assert(!std::is_nothrow_invocable<int, std::vector<int*> >::value, + ""); + static_assert(!std::is_nothrow_invocable<int, std::vector<int**> >::value, + ""); + + static_assert(!std::is_nothrow_invocable<AbominableFunc>::value, ""); + + // with parameters + static_assert(!std::is_nothrow_invocable<int, int>::value, ""); + static_assert(!std::is_nothrow_invocable<int, double, float>::value, ""); + static_assert(!std::is_nothrow_invocable<int, char, float, double>::value, + ""); + static_assert(!std::is_nothrow_invocable<Sink, AbominableFunc>::value, ""); + static_assert(!std::is_nothrow_invocable<Sink, void>::value, ""); + static_assert(!std::is_nothrow_invocable<Sink, const volatile void>::value, + ""); + + static_assert(!std::is_nothrow_invocable_r<int, void>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, const void>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, volatile void>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, const volatile void>::value, + ""); + static_assert(!std::is_nothrow_invocable_r<int, std::nullptr_t>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, int>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, double>::value, ""); + + static_assert(!std::is_nothrow_invocable_r<int, int[]>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, int[3]>::value, ""); + + static_assert(!std::is_nothrow_invocable_r<int, int*>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, const int*>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, int const*>::value, ""); + + static_assert(!std::is_nothrow_invocable_r<int, int&>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, const int&>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, int&&>::value, ""); + + static_assert(!std::is_nothrow_invocable_r<int, std::vector<int> >::value, + ""); + static_assert(!std::is_nothrow_invocable_r<int, std::vector<int*> >::value, + ""); + static_assert(!std::is_nothrow_invocable_r<int, std::vector<int**> >::value, + ""); + static_assert(!std::is_nothrow_invocable_r<void, AbominableFunc>::value, + ""); + + // with parameters + static_assert(!std::is_nothrow_invocable_r<int, int, int>::value, ""); + static_assert(!std::is_nothrow_invocable_r<int, int, double, float>::value, + ""); + static_assert( + !std::is_nothrow_invocable_r<int, int, char, float, double>::value, ""); + static_assert( + !std::is_nothrow_invocable_r<void, Sink, AbominableFunc>::value, ""); + static_assert(!std::is_nothrow_invocable_r<void, Sink, void>::value, ""); + static_assert( + !std::is_nothrow_invocable_r<void, Sink, const volatile void>::value, + ""); + } + + { + // Check that the conversion to the return type is properly checked + using Fn = CallObject<true, int>; + static_assert(std::is_nothrow_invocable_r<Implicit, Fn>::value, ""); + static_assert(std::is_nothrow_invocable_r<double, Fn>::value, ""); + static_assert(std::is_nothrow_invocable_r<const volatile void, Fn>::value, + ""); + static_assert(throws_invocable_r<ThrowsImplicit, Fn>(), ""); + static_assert(!std::is_nothrow_invocable<Fn(), Explicit>(), ""); + } + { + // Check that the conversion to the parameters is properly checked + using Fn = CallObject<true, void, const Implicit&, const ThrowsImplicit&>; + static_assert( + std::is_nothrow_invocable<Fn, Implicit&, ThrowsImplicit&>::value, ""); + static_assert(std::is_nothrow_invocable<Fn, int, ThrowsImplicit&>::value, + ""); + static_assert(throws_invocable<Fn, int, int>(), ""); + static_assert(!std::is_nothrow_invocable<Fn>::value, ""); + } + { + // Check that the noexcept-ness of function objects is checked. + using Fn = CallObject<true, void>; + using Fn2 = CallObject<false, void>; + static_assert(std::is_nothrow_invocable<Fn>::value, ""); + static_assert(throws_invocable<Fn2>(), ""); + } + { + // Check that PMD derefs are noexcept + using Fn = int(Tag::*); + static_assert(std::is_nothrow_invocable<Fn, Tag&>::value, ""); + static_assert(std::is_nothrow_invocable_r<Implicit, Fn, Tag&>::value, ""); + static_assert(throws_invocable_r<ThrowsImplicit, Fn, Tag&>(), ""); + } + { + // Check for is_nothrow_invocable_v + using Fn = CallObject<true, int>; + static_assert(std::is_nothrow_invocable_v<Fn>, ""); + static_assert(!std::is_nothrow_invocable_v<Fn, int>, ""); + } + { + // Check for is_nothrow_invocable_r_v + using Fn = CallObject<true, int>; + static_assert(std::is_nothrow_invocable_r_v<void, Fn>, ""); + static_assert(!std::is_nothrow_invocable_r_v<int, Fn, int>, ""); + } + test_noexcept_function_pointers(); } |