]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR libstdc++/90220 Fix any_cast for non-object types
authorJonathan Wakely <jwakely@redhat.com>
Thu, 23 May 2019 15:08:58 +0000 (16:08 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 23 May 2019 15:08:58 +0000 (16:08 +0100)
Backport from mainline
2019-04-24  Jonathan Wakely  <jwakely@redhat.com>

PR libstdc++/90220 (partial)
* include/std/any (any_cast<T>(any*), any_cast<T>(const any*)): Do
not attempt ill-formed static_cast to pointers to non-object types.
* testsuite/20_util/any/misc/any_cast.cc: Test std::any_cast with
function types.

Backport from mainline
2019-04-24  Jonathan Wakely  <jwakely@redhat.com>

PR libstdc++/90220
* include/std/any (__any_caster): Use remove_cv_t instead of decay_t.
Avoid a runtime check for types that can never be stored in std::any.
* testsuite/20_util/any/misc/any_cast.cc: Test std::any_cast with
array types.

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: r271565

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

index f8480a46dbb0a4598de31c629f50a4ec88d081c1..4f0466c3edfb43c6a6fb02bf3276e0a939e8c5b9 100644 (file)
@@ -1,3 +1,36 @@
+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.
+
+       Backport from mainline
+       2019-04-24  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/90220
+       * include/std/any (__any_caster): Use remove_cv_t instead of decay_t.
+       Avoid a runtime check for types that can never be stored in std::any.
+       * testsuite/20_util/any/misc/any_cast.cc: Test std::any_cast with
+       array types.
+
+       Backport from mainline
+       2019-04-24  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/90220 (partial)
+       * include/std/any (any_cast<T>(any*), any_cast<T>(const any*)): Do
+       not attempt ill-formed static_cast to pointers to non-object types.
+       * testsuite/20_util/any/misc/any_cast.cc: Test std::any_cast with
+       function types.
+
 2019-05-23  Jonathan Wakely  <jwakely@redhat.com>
 
        Backported from mainline
index b1d11398b793fe10be3e33ef8c80f95cd24c9f50..519e6536336cf2b16cbbae46b73d150f6d8f429e 100644 (file)
@@ -300,7 +300,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _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 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
   // @}
 
+  /// @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 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       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 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     }
 
+  // Dummy specialization used by __any_caster.
+  template<>
+    struct any::_Manager_internal<any::_Op>
+    {
+      static void
+      _S_manage(_Op, const any*, _Arg*) { }
+    };
+
   // @} group any
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace fundamentals_v1
index b0047180fb5d142e26daf6965679f1453824abba..b904cd6cdba1eb96716fb81f354ab57fd4cdf689 100644 (file)
@@ -516,14 +516,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     void* __any_caster(const any* __any)
     {
-      if constexpr (is_copy_constructible_v<decay_t<_Tp>>)
+      // 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:
+      if constexpr (!is_same_v<decay_t<_Up>, _Up>)
+       return nullptr;
+      // Only copy constructible types can be used for contained values:
+      else if constexpr (!is_copy_constructible_v<_Up>)
+       return nullptr;
+      // This check is equivalent to __any->type() == typeid(_Tp)
+      else if (__any->_M_manager == &any::_Manager<_Up>::_S_manage)
        {
-         if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage)
-           {
-             any::_Arg __arg;
-             __any->_M_manager(any::_Op_access, __any, &__arg);
-             return __arg._M_obj;
-           }
+         any::_Arg __arg;
+         __any->_M_manager(any::_Op_access, __any, &__arg);
+         return __arg._M_obj;
        }
       return nullptr;
     }
@@ -542,16 +550,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _ValueType>
     inline const _ValueType* any_cast(const any* __any) noexcept
     {
-      if (__any)
-       return static_cast<_ValueType*>(__any_caster<_ValueType>(__any));
+      if constexpr (is_object_v<_ValueType>)
+       if (__any)
+         return static_cast<_ValueType*>(__any_caster<_ValueType>(__any));
       return nullptr;
     }
 
   template<typename _ValueType>
     inline _ValueType* any_cast(any* __any) noexcept
     {
-      if (__any)
-       return static_cast<_ValueType*>(__any_caster<_ValueType>(__any));
+      if constexpr (is_object_v<_ValueType>)
+       if (__any)
+         return static_cast<_ValueType*>(__any_caster<_ValueType>(__any));
       return nullptr;
     }
   // @}
index 72581e6b3650bb59a6dead751b6903b1592eed74..824ebd7805c27514d2e94c48a4686eeda422bc7c 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <any>
 #include <string>
+#include <utility>
 #include <cstring>
 #include <testsuite_hooks.h>
 
@@ -130,6 +131,54 @@ void test05()
   VERIFY( p == nullptr );
 }
 
+void test06()
+{
+  // The contained value of a std::any is always an object type,
+  // but std::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()>(&std::as_const(a));
+  VERIFY( p3 == nullptr );
+
+  try {
+    any_cast<int(&)()>(a);
+    VERIFY( false );
+  } catch (const std::bad_any_cast&) {
+  }
+
+  try {
+    any_cast<int(&)()>(std::move(a));
+    VERIFY( false );
+  } catch (const std::bad_any_cast&) {
+  }
+
+  try {
+    any_cast<int(&)()>(std::as_const(a));
+    VERIFY( false );
+  } catch (const std::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[]>(&std::as_const(a));
+  VERIFY( p3 == nullptr );
+}
+
 int main()
 {
   test01();
@@ -137,4 +186,6 @@ int main()
   test03();
   test04();
   test05();
+  test06();
+  test07();
 }
index 62ab1b38a33ab9286e341ada769891b53d4abe22..5f6c3790802682f853ad137a081818fd9d0d17ed 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();
 }