PR libstdc++/90220 fix experimental::any_cast for non-object types
authorJonathan Wakely <jwakely@redhat.com>
Thu, 23 May 2019 13:39:06 +0000 (14:39 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 23 May 2019 13:39:06 +0000 (14:39 +0100)
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.

From-SVN: r271556

libstdc++-v3/ChangeLog
libstdc++-v3/include/experimental/any
libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc

index d7faf93d0e14a56932ddd468cd711ddff3a3b055..460b734962e1cadd4f6e46dc10dd025bd9ed5716 100644 (file)
@@ -1,3 +1,15 @@
+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>
 
        PR libstdc++/90557
index a6e0fc683d9ecd5356d44a07baa94d0dc25f01df..f1d4bbf788c083afd1e287488e9067646d52b032 100644 (file)
@@ -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
index 1875d345c5e812cbdd9261f447b30190cd9ee5e2..be1e86e8c700defb8537798a5a3d60b645476053 100644 (file)
@@ -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();
 }