diff mbox series

PR libstdc++/90220 fix experimental::any_cast for non-object types

Message ID 20190523133909.GA11223@redhat.com
State New
Headers show
Series PR libstdc++/90220 fix experimental::any_cast for non-object types | expand

Commit Message

Jonathan Wakely May 23, 2019, 1:39 p.m. UTC
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.

	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.

Tested powerpc64le-linux, committed to trunk.
commit a05a99ecd0ef3a2d187f1f14a7ef2ed7de0cd7a5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu May 23 14:19:04 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.
    
            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.
diff mbox series

Patch

diff --git a/libstdc++-v3/include/experimental/any b/libstdc++-v3/include/experimental/any
index a6e0fc683d9..f1d4bbf788c 100644
--- a/libstdc++-v3/include/experimental/any
+++ b/libstdc++-v3/include/experimental/any
@@ -303,7 +303,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>
@@ -415,17 +416,34 @@  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;
       __any->_M_manager(any::_Op_access, __any, &__arg);
       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
 
   /**
@@ -522,6 +540,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 1875d345c5e..be1e86e8c70 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();
 }