summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2019-01-29 18:01:14 +0000
committerEric Fiselier <eric@efcs.ca>2019-01-29 18:01:14 +0000
commita32a775e66e7141185ef83ca225cbc4799cb70bf (patch)
treedd43bc4e8427d72b651a5b112647ec70562fc247
parente0324cb307cc5dd2974faa3e0f2d8693c0aec9a0 (diff)
downloadlibcxx-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_traits37
-rw-r--r--test/std/utilities/meta/meta.rel/is_invocable.pass.cpp305
-rw-r--r--test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp233
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();
}