[libcxx-commits] [libcxx] r352522 - Fix PR40495 - is_invokable_v<void> does not compile

Eric Fiselier via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jan 29 10:01:15 PST 2019


Author: ericwf
Date: Tue Jan 29 10:01:14 2019
New Revision: 352522

URL: http://llvm.org/viewvc/llvm-project?rev=352522&view=rev
Log:
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.

Modified:
    libcxx/trunk/include/type_traits
    libcxx/trunk/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp
    libcxx/trunk/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp

Modified: libcxx/trunk/include/type_traits
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/type_traits?rev=352522&r1=352521&r2=352522&view=diff
==============================================================================
--- libcxx/trunk/include/type_traits (original)
+++ libcxx/trunk/include/type_traits Tue Jan 29 10:01:14 2019
@@ -4360,28 +4360,31 @@ _LIBCPP_INVOKE_RETURN(_VSTD::forward<_Fp
 #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...>;
 

Modified: libcxx/trunk/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp?rev=352522&r1=352521&r2=352522&view=diff
==============================================================================
--- libcxx/trunk/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp (original)
+++ libcxx/trunk/test/std/utilities/meta/meta.rel/is_invocable.pass.cpp Tue Jan 29 10:01:14 2019
@@ -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
+    {
+      // 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, "");
+    }
+  }
+  {
+    // 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, "");
+    }
+  }
+  { // 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, "");
+}
 {
-    {
-        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, "");
-        }
-    }
-    {
-        // 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, "");
-        }
-    }
-    {
-        // 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>, "");
-    }
+  // 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>, "");
+}
 }

Modified: libcxx/trunk/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp?rev=352522&r1=352521&r2=352522&view=diff
==============================================================================
--- libcxx/trunk/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp (original)
+++ libcxx/trunk/test/std/utilities/meta/meta.rel/is_nothrow_invocable.pass.cpp Tue Jan 29 10:01:14 2019
@@ -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();
 }




More information about the libcxx-commits mailing list