From 7dbab5dc84e3782fe6f366a985e507e2ea2726d2 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 23 May 2019 14:39:06 +0100 Subject: [PATCH] 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::_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 | 12 ++++ libstdc++-v3/include/experimental/any | 36 ++++++++++-- .../experimental/any/misc/any_cast.cc | 56 ++++++++++++++++++- 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index d7faf93d0e1..460b734962e 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,15 @@ +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. + 2019-05-22 Jonathan Wakely PR libstdc++/90557 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 - friend void* __any_caster(const any* __any); + friend enable_if_t::value, void*> + __any_caster(const any* __any); // Manage in-place contained object. template @@ -415,17 +416,34 @@ inline namespace fundamentals_v1 /// @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; __any->_M_manager(any::_Op_access, __any, &__arg); 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 /** @@ -522,6 +540,14 @@ inline namespace fundamentals_v1 } } + // Dummy specialization used by __any_caster. + template<> + struct any::_Manager_internal + { + 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(&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.30.2