[gcc/devel/omp/gcc-9] PR libstdc++/91456 make INVOKE<R> work with uncopyable prvalues

Tobias Burnus burnus@gcc.gnu.org
Thu Mar 5 14:14:00 GMT 2020


https://gcc.gnu.org/g:b53e76509f94cce90ce50b5d02f5ed99d3581f3c

commit b53e76509f94cce90ce50b5d02f5ed99d3581f3c
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Oct 24 13:54:13 2019 +0100

    PR libstdc++/91456 make INVOKE<R> work with uncopyable prvalues
    
    In C++17 a function can return a prvalue of a type that cannot be moved
    or copied. The current implementation of std::is_invocable_r uses
    std::is_convertible to test the conversion to R required by INVOKE<R>.
    That fails for non-copyable prvalues, because std::is_convertible is
    defined in terms of std::declval which uses std::add_rvalue_reference.
    In C++17 conversion from R to R involves no copies and so is not the
    same as conversion from R&& to R.
    
    This commit changes std::is_invocable_r to check the conversion without
    using std::is_convertible.
    
    std::function also contains a similar check using std::is_convertible,
    which can be fixed by simply reusing std::is_invocable_r (but because
    std::is_invocable_r is not defined for C++11 it uses the underlying
    std::__is_invocable_impl trait directly).
    
    Backport from mainline
    2019-08-15  Jonathan Wakely  <jwakely@redhat.com>
    
    	PR libstdc++/91456
    	* include/bits/std_function.h (__check_func_return_type): Remove.
    	(function::_Callable): Use std::__is_invocable_impl instead of
    	__check_func_return_type.
    	* include/std/type_traits (__is_invocable_impl): Add another defaulted
    	template parameter. Define a separate partial specialization for
    	INVOKE and INVOKE<void>. For INVOKE<R> replace is_convertible check
    	with a check that models delayed temporary materialization.
    	* testsuite/20_util/function/91456.cc: New test.
    	* testsuite/20_util/is_invocable/91456.cc: New test.
    
    From-SVN: r277380

Diff:
---
 libstdc++-v3/ChangeLog                             | 14 ++++++++
 libstdc++-v3/include/bits/std_function.h           | 10 +++---
 libstdc++-v3/include/std/type_traits               | 41 ++++++++++++++++++++--
 libstdc++-v3/testsuite/20_util/function/91456.cc   | 37 +++++++++++++++++++
 .../testsuite/20_util/is_invocable/91456.cc        | 34 ++++++++++++++++++
 5 files changed, 127 insertions(+), 9 deletions(-)

diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index a349b55..2bcf59d 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,6 +1,20 @@
 2019-10-24  Jonathan Wakely  <jwakely@redhat.com>
 
 	Backport from mainline
+	2019-08-15  Jonathan Wakely  <jwakely@redhat.com>
+
+	PR libstdc++/91456
+	* include/bits/std_function.h (__check_func_return_type): Remove.
+	(function::_Callable): Use std::__is_invocable_impl instead of
+	__check_func_return_type.
+	* include/std/type_traits (__is_invocable_impl): Add another defaulted
+	template parameter. Define a separate partial specialization for
+	INVOKE and INVOKE<void>. For INVOKE<R> replace is_convertible check
+	with a check that models delayed temporary materialization.
+	* testsuite/20_util/function/91456.cc: New test.
+	* testsuite/20_util/is_invocable/91456.cc: New test.
+
+	Backport from mainline
 	2019-09-30  Andreas Tobler  <andreast@gcc.gnu.org>
 
 	* include/experimental/internet: Include netinet/in.h if we have
diff --git a/libstdc++-v3/include/bits/std_function.h b/libstdc++-v3/include/bits/std_function.h
index b70ed56..b59f697 100644
--- a/libstdc++-v3/include/bits/std_function.h
+++ b/libstdc++-v3/include/bits/std_function.h
@@ -359,10 +359,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     };
 
-  template<typename _From, typename _To>
-    using __check_func_return_type
-      = __or_<is_void<_To>, is_same<_From, _To>, is_convertible<_From, _To>>;
-
   /**
    *  @brief Primary class template for std::function.
    *  @ingroup functors
@@ -375,8 +371,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       private _Function_base
     {
       template<typename _Func,
-	       typename _Res2 = typename result_of<_Func&(_ArgTypes...)>::type>
-	struct _Callable : __check_func_return_type<_Res2, _Res> { };
+	       typename _Res2 = __invoke_result<_Func&, _ArgTypes...>>
+	struct _Callable
+	: __is_invocable_impl<_Res2, _Res>::type
+	{ };
 
       // Used so the return type convertibility checks aren't done when
       // performing overload resolution for copy construction/assignment.
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 440813d..9bad558 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2662,14 +2662,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // __is_invocable (std::is_invocable for C++11)
 
-  template<typename _Result, typename _Ret, typename = void>
+  // The primary template is used for invalid INVOKE expressions.
+  template<typename _Result, typename _Ret,
+	   bool = is_void<_Ret>::value, typename = void>
     struct __is_invocable_impl : false_type { };
 
+  // Used for valid INVOKE and INVOKE<void> expressions.
   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
+    struct __is_invocable_impl<_Result, _Ret,
+			       /* is_void<_Ret> = */ true,
+			       __void_t<typename _Result::type>>
+    : true_type
     { };
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
+  // Used for INVOKE<R> expressions to check the implicit conversion to R.
+  template<typename _Result, typename _Ret>
+    struct __is_invocable_impl<_Result, _Ret,
+			       /* is_void<_Ret> = */ false,
+			       __void_t<typename _Result::type>>
+    {
+    private:
+      // The type of the INVOKE expression.
+      // Unlike declval, this doesn't add_rvalue_reference.
+      static typename _Result::type _S_get();
+
+      template<typename _Tp>
+	static void _S_conv(_Tp);
+
+      // This overload is viable if INVOKE(f, args...) can convert to _Tp.
+      template<typename _Tp, typename = decltype(_S_conv<_Tp>(_S_get()))>
+	static true_type
+	_S_test(int);
+
+      template<typename _Tp>
+	static false_type
+	_S_test(...);
+
+    public:
+      using type = decltype(_S_test<_Ret>(1));
+    };
+#pragma GCC diagnostic pop
+
   template<typename _Fn, typename... _ArgTypes>
     struct __is_invocable
     : __is_invocable_impl<__invoke_result<_Fn, _ArgTypes...>, void>::type
diff --git a/libstdc++-v3/testsuite/20_util/function/91456.cc b/libstdc++-v3/testsuite/20_util/function/91456.cc
new file mode 100644
index 0000000..a2d412d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function/91456.cc
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+// { dg-do compile { target c++17 } }
+
+#include <functional>
+
+struct Immovable {
+  Immovable() = default;
+  Immovable(const Immovable&) = delete;
+  Immovable& operator=(const Immovable&) = delete;
+};
+
+Immovable get() { return {}; }
+const Immovable i = get();                      // OK
+std::function<const Immovable()> f{&get};       // fails
+const Immovable i2 = f();
+
+const Immovable cget() { return {}; }
+Immovable ci = cget();                          // OK
+std::function<Immovable()> cf{&cget};           // fails
+Immovable ci2 = cf();
diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/91456.cc b/libstdc++-v3/testsuite/20_util/is_invocable/91456.cc
new file mode 100644
index 0000000..d510d22
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/is_invocable/91456.cc
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+// { dg-do compile { target c++17 } }
+
+#include <type_traits>
+
+#include <functional>
+
+struct Immovable {
+  Immovable() = default;
+  Immovable(const Immovable&) = delete;
+  Immovable& operator=(const Immovable&) = delete;
+};
+
+Immovable get() { return {}; }
+const Immovable i = get();                      // OK
+std::function<const Immovable()> f{&get};       // fails
+const Immovable i2 = f();



More information about the Libstdc++-cvs mailing list