[gcc/devel/omp/gcc-9] PR libstdc++/90220 fix experimental::any_cast for non-object types

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


https://gcc.gnu.org/g:8e355a1c3b292256605180e55b71296f7c13be6b

commit 8e355a1c3b292256605180e55b71296f7c13be6b
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu May 23 15:18:13 2019 +0100

    PR libstdc++/90220 fix experimental::any_cast for non-object types
    
    This corresponds to the fixes done for std::any_cast, but has to be done
    without if-constexpr. The dummy specialization of _Manager_internal<_Op>
    is used to avoid instantiating the real _Manager_internal<T>::_S_manage
    function just to compare its address.
    
    Backport from mainline
    2019-05-23  Jonathan Wakely  <jwakely@redhat.com>
    
    	PR libstdc++/90220
    	* include/experimental/any (__any_caster): Constrain to only be
    	callable for object types. Use remove_cv_t instead of decay_t.
    	If the type decays or isn't copy constructible, compare the manager
    	function to a dummy specialization.
    	(__any_caster): Add overload constrained for non-object types.
    	(any::_Manager_internal<_Op>): Add dummy specialization.
    	* testsuite/experimental/any/misc/any_cast.cc: Test function types
    	and array types.
    
    From-SVN: r271558

Diff:
---
 libstdc++-v3/ChangeLog                             | 15 ++++++
 libstdc++-v3/include/experimental/any              | 38 +++++++++++++--
 .../testsuite/experimental/any/misc/any_cast.cc    | 56 ++++++++++++++++++++--
 3 files changed, 101 insertions(+), 8 deletions(-)

diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 91bd18d..494cf37 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,18 @@
+2019-05-23  Jonathan Wakely  <jwakely@redhat.com>
+
+	Backport from mainline
+	2019-05-23  Jonathan Wakely  <jwakely@redhat.com>
+
+	PR libstdc++/90220
+	* include/experimental/any (__any_caster): Constrain to only be
+	callable for object types. Use remove_cv_t instead of decay_t.
+	If the type decays or isn't copy constructible, compare the manager
+	function to a dummy specialization.
+	(__any_caster): Add overload constrained for non-object types.
+	(any::_Manager_internal<_Op>): Add dummy specialization.
+	* testsuite/experimental/any/misc/any_cast.cc: Test function types
+	and array types.
+
 2019-05-22  Jonathan Wakely  <jwakely@redhat.com>
 
 	Backport from mainline
diff --git a/libstdc++-v3/include/experimental/any b/libstdc++-v3/include/experimental/any
index ed7a115..1ba29c7 100644
--- a/libstdc++-v3/include/experimental/any
+++ b/libstdc++-v3/include/experimental/any
@@ -300,7 +300,8 @@ inline namespace fundamentals_v1
     _Storage _M_storage;
 
     template<typename _Tp>
-      friend void* __any_caster(const any* __any);
+      friend enable_if_t<is_object<_Tp>::value, void*>
+      __any_caster(const any* __any);
 
     // Manage in-place contained object.
     template<typename _Tp>
@@ -410,12 +411,24 @@ inline namespace fundamentals_v1
     }
   // @}
 
+  /// @cond undocumented
   template<typename _Tp>
-    void* __any_caster(const any* __any)
+    enable_if_t<is_object<_Tp>::value, void*>
+    __any_caster(const any* __any)
     {
-      struct _None { };
-      using _Up = decay_t<_Tp>;
-      using _Vp = conditional_t<is_copy_constructible<_Up>::value, _Up, _None>;
+      // any_cast<T> returns non-null if __any->type() == typeid(T) and
+      // typeid(T) ignores cv-qualifiers so remove them:
+      using _Up = remove_cv_t<_Tp>;
+      // The contained value has a decayed type, so if decay_t<U> is not U,
+      // then it's not possible to have a contained value of type U.
+      using __does_not_decay = is_same<decay_t<_Up>, _Up>;
+      // Only copy constructible types can be used for contained values.
+      using __is_copyable = is_copy_constructible<_Up>;
+      // If the type _Tp could never be stored in an any we don't want to
+      // instantiate _Manager<_Tp>, so use _Manager<any::_Op> instead, which
+      // is explicitly specialized and has a no-op _S_manage function.
+      using _Vp = conditional_t<__and_<__does_not_decay, __is_copyable>::value,
+				_Up, any::_Op>;
       if (__any->_M_manager != &any::_Manager<_Vp>::_S_manage)
 	return nullptr;
       any::_Arg __arg;
@@ -423,6 +436,13 @@ inline namespace fundamentals_v1
       return __arg._M_obj;
     }
 
+  // This overload exists so that std::any_cast<void(*)()>(a) is well-formed.
+  template<typename _Tp>
+    enable_if_t<!is_object<_Tp>::value, _Tp*>
+    __any_caster(const any*) noexcept
+    { return nullptr; }
+  /// @endcond
+
   /**
    * @brief Access the contained object.
    *
@@ -517,6 +537,14 @@ inline namespace fundamentals_v1
       }
     }
 
+  // Dummy specialization used by __any_caster.
+  template<>
+    struct any::_Manager_internal<any::_Op>
+    {
+      static void
+      _S_manage(_Op, const any*, _Arg*) { }
+    };
+
   // @} group any
 } // namespace fundamentals_v1
 } // namespace experimental
diff --git a/libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc b/libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc
index 1875d34..be1e86e 100644
--- a/libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc
+++ b/libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc
@@ -24,6 +24,7 @@
 
 using std::experimental::any;
 using std::experimental::any_cast;
+using std::experimental::bad_any_cast;
 
 void test01()
 {
@@ -56,7 +57,6 @@ void test01()
 
 void test02()
 {
-  using std::experimental::bad_any_cast;
   any x(1);
   auto p = any_cast<double>(&x);
   VERIFY(p == nullptr);
@@ -105,7 +105,7 @@ void test03()
   MoveDeleted&& md3 = any_cast<MoveDeleted&&>(any(std::move(md)));
 }
 
-void test04()
+void test05()
 {
   // PR libstdc++/69321
   struct noncopyable {
@@ -117,10 +117,60 @@ void test04()
   VERIFY( p == nullptr );
 }
 
+void test06()
+{
+  // The contained value of a std::any is always an object type,
+  // but any_cast does not forbid checking for function types.
+
+  any a(1);
+  void (*p1)() = any_cast<void()>(&a);
+  VERIFY( p1 == nullptr );
+  int (*p2)(int) = any_cast<int(int)>(&a);
+  VERIFY( p2 == nullptr );
+  int (*p3)() = any_cast<int()>(&const_cast<const any&>(a));
+  VERIFY( p3 == nullptr );
+
+  try {
+    any_cast<int(&)()>(a);
+    VERIFY( false );
+  } catch (const bad_any_cast&) {
+  }
+
+  try {
+    any_cast<int(&)()>(std::move(a));
+    VERIFY( false );
+  } catch (const bad_any_cast&) {
+  }
+
+  try {
+    any_cast<int(&)()>(const_cast<const any&>(a));
+    VERIFY( false );
+  } catch (const bad_any_cast&) {
+  }
+}
+
+void test07()
+{
+  int arr[3];
+  any a(arr);
+  VERIFY( a.type() == typeid(int*) );	// contained value is decayed
+
+  int (*p1)[3] = any_cast<int[3]>(&a);
+  VERIFY( a.type() != typeid(int[3]) ); // so any_cast should return nullptr
+  VERIFY( p1 == nullptr );
+  int (*p2)[] = any_cast<int[]>(&a);
+  VERIFY( a.type() != typeid(int[]) );	// so any_cast should return nullptr
+  VERIFY( p2 == nullptr );
+  const int (*p3)[] = any_cast<int[]>(&const_cast<const any&>(a));
+  VERIFY( p3 == nullptr );
+}
+
 int main()
 {
   test01();
   test02();
   test03();
-  test04();
+  test05();
+  test06();
+  test07();
 }



More information about the Libstdc++-cvs mailing list