PR libstdc++/82254 fix std::is_nothrow_invocable_r w.r.t throwing conversions
authorJonathan Wakely <jwakely@redhat.com>
Tue, 19 Sep 2017 14:33:51 +0000 (15:33 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 19 Sep 2017 14:33:51 +0000 (15:33 +0100)
PR libstdc++/82254
* include/std/type_traits (__is_invocable): Add partial specialization
for INVOKE<void> case and remove is_void<R> check from partial
specialization for INVOKE<R> case.
(__is_nt_invocable_impl): New helper for is_nothrow_invocable_r.
(is_nothrow_invocable_r): Use __is_nt_invocable_impl.
* testsuite/20_util/is_nothrow_invocable/value.cc: Add tests for
conversions that can throw or fail to convert. Use static assert
strings to explain negative results.
* testsuite/20_util/is_nothrow_invocable/value_ext.cc: Use
is_nothrow_constructible in is_nt_invocable_conv.

From-SVN: r252977

libstdc++-v3/ChangeLog
libstdc++-v3/include/std/type_traits
libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value.cc
libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value_ext.cc

index 28a9609103e78809f8c65beea34d864f1ca1d942..3c75c7639cad1b0fa2b8c76ac6222a6e96186eea 100644 (file)
@@ -1,3 +1,17 @@
+2017-09-19  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/82254
+       * include/std/type_traits (__is_invocable): Add partial specialization
+       for INVOKE<void> case and remove is_void<R> check from partial
+       specialization for INVOKE<R> case.
+       (__is_nt_invocable_impl): New helper for is_nothrow_invocable_r.
+       (is_nothrow_invocable_r): Use __is_nt_invocable_impl.
+       * testsuite/20_util/is_nothrow_invocable/value.cc: Add tests for
+       conversions that can throw or fail to convert. Use static assert
+       strings to explain negative results.
+       * testsuite/20_util/is_nothrow_invocable/value_ext.cc: Use
+       is_nothrow_constructible in is_nt_invocable_conv.
+
 2017-09-18  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/81468
index 15b0d92bcb61197d55766817c31e5ea972b3a024..036f7667bd812652c12838c6dcdc41976235bec4 100644 (file)
@@ -2592,7 +2592,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Result, typename _Ret>
     struct __is_invocable_impl<_Result, _Ret, __void_t<typename _Result::type>>
-    : __or_<is_void<_Ret>, is_convertible<typename _Result::type, _Ret>>::type
+    : is_convertible<typename _Result::type, _Ret>::type
+    { };
+
+  template<typename _Result>
+    struct __is_invocable_impl<_Result, void, __void_t<typename _Result::type>>
+    : true_type
     { };
 
   template<typename _Fn, typename... _ArgTypes>
@@ -2691,10 +2696,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
              __call_is_nothrow_<_Fn, _ArgTypes...>>::type
     { };
 
+  template<typename _Result, typename _Ret, typename = void>
+    struct __is_nt_invocable_impl : false_type { };
+
+  template<typename _Result, typename _Ret>
+    struct __is_nt_invocable_impl<_Result, _Ret,
+                                 __void_t<typename _Result::type>>
+    : __and_<is_convertible<typename _Result::type, _Ret>,
+            is_nothrow_constructible<_Ret, typename _Result::type>>
+    { };
+
+  template<typename _Result>
+    struct __is_nt_invocable_impl<_Result, void,
+                                 __void_t<typename _Result::type>>
+    : true_type
+    { };
+
   /// std::is_nothrow_invocable_r
   template<typename _Ret, typename _Fn, typename... _ArgTypes>
     struct is_nothrow_invocable_r
-    : __and_<__is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, _Ret>,
+    : __and_<__is_nt_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, _Ret>,
              __call_is_nothrow_<_Fn, _ArgTypes...>>::type
     { };
 
index 4ccb459e0f5c22585453ffdd60e48f971c8189ff..dfa76aae61c23acd8ac26315b41f81e8f786143e 100644 (file)
@@ -40,6 +40,10 @@ template<typename R, typename... T>
 
 void test01()
 {
+  struct T { T(int) { } };
+  struct NT { NT(int) noexcept { } };
+  struct Ex { explicit Ex(int) noexcept { } };
+
   using func_type = void(*)();
   static_assert( ! is_nt_invocable< func_type>(),     "");
 
@@ -55,28 +59,46 @@ void test01()
   static_assert( ! is_nt_invocable< mem_type, int >(),   "");
   static_assert( ! is_nt_invocable< mem_type, int& >(),        "");
 
-  static_assert(   is_nt_invocable< mem_type, X& >(),       "");
-  static_assert(   is_nt_invocable_r< int,  mem_type, X& >(), "");
-  static_assert(   is_nt_invocable_r< int&, mem_type, X& >(), "");
-  static_assert(   is_nt_invocable_r< long, mem_type, X& >(), "");
-  static_assert(   is_nt_invocable_r< int&, mem_type, X* >(), "");
+  static_assert(   is_nt_invocable< mem_type, X& >(),          "");
+  static_assert(   is_nt_invocable_r< int,   mem_type, X& >(), "");
+  static_assert(   is_nt_invocable_r< int&,  mem_type, X& >(), "");
+  static_assert(   is_nt_invocable_r< long,  mem_type, X& >(), "");
+  static_assert( ! is_nt_invocable_r< long&, mem_type, X& >(),
+                  "conversion fails, cannot bind long& to int");
+  static_assert(   is_nt_invocable_r< int&,  mem_type, X* >(), "");
+
+  static_assert( ! is_nt_invocable_r< T,  mem_type, X& >(),
+                  "conversion throws");
+  static_assert(   is_nt_invocable_r< NT, mem_type, X& >(), "");
+  static_assert( ! is_nt_invocable_r< Ex, mem_type, X& >(),
+                  "conversion fails, would use explicit constructor");
 
   using memfun_type = int (X::*)();
 
-  static_assert( ! is_nt_invocable< memfun_type >(),     "");
-  static_assert( ! is_nt_invocable< memfun_type, int >(),  "");
-  static_assert( ! is_nt_invocable< memfun_type, int& >(), "");
-  static_assert( ! is_nt_invocable< memfun_type, X& >(),   "");
-  static_assert( ! is_nt_invocable< memfun_type, X* >(),   "");
+  static_assert( ! is_nt_invocable< memfun_type >(),       "no object");
+  static_assert( ! is_nt_invocable< memfun_type, int >(),  "no object");
+  static_assert( ! is_nt_invocable< memfun_type, int& >(), "no object");
+  static_assert( ! is_nt_invocable< memfun_type, X& >(),   "call throws");
+  static_assert( ! is_nt_invocable< memfun_type, X* >(),   "call throws");
+
+  static_assert( ! is_nt_invocable_r< T,  memfun_type, X& >(), "call throws");
+  static_assert( ! is_nt_invocable_r< NT, memfun_type, X& >(), "call throws");
+  static_assert( ! is_nt_invocable_r< Ex, memfun_type, X& >(), "call throws");
 
 #if __cpp_noexcept_function_type
   using memfun_type_nt = int (X::*)() noexcept;
 
-  static_assert( ! is_nt_invocable< memfun_type_nt >(),            "");
-  static_assert( ! is_nt_invocable< memfun_type_nt, int >(),  "");
-  static_assert( ! is_nt_invocable< memfun_type_nt, int& >(), "");
+  static_assert( ! is_nt_invocable< memfun_type_nt >(),              "no object");
+  static_assert( ! is_nt_invocable< memfun_type_nt, int >(),  "no object");
+  static_assert( ! is_nt_invocable< memfun_type_nt, int& >(), "no object");
   static_assert(   is_nt_invocable< memfun_type_nt, X& >(),   "");
   static_assert(   is_nt_invocable< memfun_type_nt, X* >(),   "");
+
+  static_assert( ! is_nt_invocable_r< T,  memfun_type_nt, X& >(),
+                  "conversion throws");
+  static_assert(   is_nt_invocable_r< NT, memfun_type_nt, X& >(), "");
+  static_assert( ! is_nt_invocable_r< Ex, memfun_type_nt, X& >(),
+                  "conversion fails, would use explicit constructor");
 #endif
 
   struct F {
@@ -89,12 +111,44 @@ void test01()
   };
   using CF = const F;
 
-  static_assert( ! is_nt_invocable_r< int&,  F  >(), "");
-  static_assert(   is_nt_invocable_r< long&, CF >(),  "");
-  static_assert( ! is_nt_invocable_r< short&, F,   int >(), "" );
-  static_assert(   is_nt_invocable_r< char&,  F&,  int >(), "" );
-  static_assert(   is_nt_invocable_r< char&,  CF,  int >(), "" );
-  static_assert(   is_nt_invocable_r< char&,  CF&, int >(), "" );
-
-  static_assert( ! is_nt_invocable< F, int, int >(), "");
+  static_assert( ! is_nt_invocable< F  >(), "call throws");
+  static_assert(   is_nt_invocable< CF >(), "");
+
+  static_assert( ! is_nt_invocable_r< int&,  F  >(), "call throws");
+  static_assert(   is_nt_invocable_r< long&, CF >(), "");
+  static_assert( ! is_nt_invocable_r< T,     F  >(), "call throws");
+  static_assert( ! is_nt_invocable_r< NT,    F  >(), "call throws");
+  static_assert( ! is_nt_invocable_r< Ex,    F  >(), "call throws");
+  static_assert( ! is_nt_invocable_r< T,     CF >(), "conversion throws");
+  static_assert(   is_nt_invocable_r< NT,    CF >(), "" );
+  static_assert( ! is_nt_invocable_r< Ex,    CF >(), "conversion fails");
+
+  static_assert( ! is_nt_invocable< F,   int >(), "call throws");
+  static_assert(   is_nt_invocable< F&,  int >(), "");
+
+  static_assert( ! is_nt_invocable_r< short&, F,   int >(),
+                  "call throws" );
+  static_assert(   is_nt_invocable_r< char&,  F&,  int >(), "");
+  static_assert( ! is_nt_invocable_r< T,      F&,  int >(),
+                  "conversion throws");
+  static_assert(   is_nt_invocable_r< NT,     F&,  int >(), "");
+  static_assert( ! is_nt_invocable_r< Ex,     F&,  int >(),
+                  "conversion fails, would use explicit constructor");
+
+  static_assert(   is_nt_invocable< CF,   int >(), "");
+  static_assert(   is_nt_invocable< CF&,  int >(), "");
+
+  static_assert(   is_nt_invocable_r< char&,  CF,  int >(), "");
+  static_assert(   is_nt_invocable_r< char&,  CF&, int >(), "");
+
+  static_assert( ! is_nt_invocable_r< T,      CF&, int >(),
+                  "conversion throws");
+  static_assert(   is_nt_invocable_r< NT,     CF&, int >(), "");
+  static_assert( ! is_nt_invocable_r< Ex,     CF&, int >(),
+                  "conversion fails, would use explicit constructor");
+
+  static_assert( ! is_nt_invocable< F, int, int >(),
+                  "would call private member");
+  static_assert( ! is_nt_invocable_r<void, F, int, int >(),
+                  "would call private member");
 }
index 7217324e1b3d5e86e0ea54e91d8e1094a00bfbd4..7fd3d92484333fdb52cf64e39a78384e29987f6c 100644 (file)
@@ -27,7 +27,9 @@ template<typename... T>
   constexpr bool is_nt_invocable_conv(std::true_type)
   {
     using result_type = typename std::__invoke_result<T...>::type;
-    return std::is_void<R>::value || std::is_convertible<result_type, R>::value;
+    return std::is_void<R>::value
+      || (std::is_convertible<result_type, R>::value
+         && std::is_nothrow_constructible<R, result_type>::value);
   }
 
 template<typename R, typename... T>