From f82a8e0a73b6be5680a8f3a64d7ae708f549f0a3 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 23 May 2019 16:08:58 +0100 Subject: [PATCH] PR libstdc++/90220 Fix any_cast for non-object types Backport from mainline 2019-04-24 Jonathan Wakely PR libstdc++/90220 (partial) * include/std/any (any_cast(any*), any_cast(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 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 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 | 33 +++++++++++ libstdc++-v3/include/experimental/any | 38 +++++++++++-- libstdc++-v3/include/std/any | 32 +++++++---- .../testsuite/20_util/any/misc/any_cast.cc | 51 +++++++++++++++++ .../experimental/any/misc/any_cast.cc | 56 ++++++++++++++++++- 5 files changed, 191 insertions(+), 19 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index f8480a46dbb0..4f0466c3edfb 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,36 @@ +2019-05-23 Jonathan Wakely + + Backport from mainline + 2019-05-23 Jonathan Wakely + + 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 + + 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 + + PR libstdc++/90220 (partial) + * include/std/any (any_cast(any*), any_cast(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 Backported from mainline diff --git a/libstdc++-v3/include/experimental/any b/libstdc++-v3/include/experimental/any index b1d11398b793..519e6536336c 100644 --- a/libstdc++-v3/include/experimental/any +++ b/libstdc++-v3/include/experimental/any @@ -300,7 +300,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Storage _M_storage; template - friend void* __any_caster(const any* __any); + friend enable_if_t::value, void*> + __any_caster(const any* __any); // Manage in-place contained object. template @@ -410,12 +411,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } // @} + /// @cond undocumented template - void* __any_caster(const any* __any) + enable_if_t::value, void*> + __any_caster(const any* __any) { - struct _None { }; - using _Up = decay_t<_Tp>; - using _Vp = conditional_t::value, _Up, _None>; + // any_cast 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 is not U, + // then it's not possible to have a contained value of type U. + using __does_not_decay = is_same, _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 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(a) is well-formed. + template + enable_if_t::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 + { + static void + _S_manage(_Op, const any*, _Arg*) { } + }; + // @} group any _GLIBCXX_END_NAMESPACE_VERSION } // namespace fundamentals_v1 diff --git a/libstdc++-v3/include/std/any b/libstdc++-v3/include/std/any index b0047180fb5d..b904cd6cdba1 100644 --- a/libstdc++-v3/include/std/any +++ b/libstdc++-v3/include/std/any @@ -516,14 +516,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template void* __any_caster(const any* __any) { - if constexpr (is_copy_constructible_v>) + // any_cast 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 is not U, + // then it's not possible to have a contained value of type U: + if constexpr (!is_same_v, _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>::_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 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 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; } // @} diff --git a/libstdc++-v3/testsuite/20_util/any/misc/any_cast.cc b/libstdc++-v3/testsuite/20_util/any/misc/any_cast.cc index 72581e6b3650..824ebd7805c2 100644 --- a/libstdc++-v3/testsuite/20_util/any/misc/any_cast.cc +++ b/libstdc++-v3/testsuite/20_util/any/misc/any_cast.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -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(&a); + VERIFY( p1 == nullptr ); + int (*p2)(int) = any_cast(&a); + VERIFY( p2 == nullptr ); + int (*p3)() = any_cast(&std::as_const(a)); + VERIFY( p3 == nullptr ); + + try { + any_cast(a); + VERIFY( false ); + } catch (const std::bad_any_cast&) { + } + + try { + any_cast(std::move(a)); + VERIFY( false ); + } catch (const std::bad_any_cast&) { + } + + try { + any_cast(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(&a); + VERIFY( a.type() != typeid(int[3]) ); // so any_cast should return nullptr + VERIFY( p1 == nullptr ); + int (*p2)[] = any_cast(&a); + VERIFY( a.type() != typeid(int[]) ); // so any_cast should return nullptr + VERIFY( p2 == nullptr ); + const int (*p3)[] = any_cast(&std::as_const(a)); + VERIFY( p3 == nullptr ); +} + int main() { test01(); @@ -137,4 +186,6 @@ int main() test03(); test04(); test05(); + test06(); + test07(); } diff --git a/libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc b/libstdc++-v3/testsuite/experimental/any/misc/any_cast.cc index 62ab1b38a33a..5f6c37908026 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(&x); VERIFY(p == nullptr); @@ -105,7 +105,7 @@ void test03() MoveDeleted&& md3 = any_cast(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(&a); + VERIFY( p1 == nullptr ); + int (*p2)(int) = any_cast(&a); + VERIFY( p2 == nullptr ); + int (*p3)() = any_cast(&const_cast(a)); + VERIFY( p3 == nullptr ); + + try { + any_cast(a); + VERIFY( false ); + } catch (const bad_any_cast&) { + } + + try { + any_cast(std::move(a)); + VERIFY( false ); + } catch (const bad_any_cast&) { + } + + try { + any_cast(const_cast(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(&a); + VERIFY( a.type() != typeid(int[3]) ); // so any_cast should return nullptr + VERIFY( p1 == nullptr ); + int (*p2)[] = any_cast(&a); + VERIFY( a.type() != typeid(int[]) ); // so any_cast should return nullptr + VERIFY( p2 == nullptr ); + const int (*p3)[] = any_cast(&const_cast(a)); + VERIFY( p3 == nullptr ); +} + int main() { test01(); test02(); test03(); - test04(); + test05(); + test06(); + test07(); } -- 2.47.2