libstdc++: Make std::function work better with -fno-rtti
authorJonathan Wakely <jwakely@redhat.com>
Thu, 29 Oct 2020 14:47:17 +0000 (14:47 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 29 Oct 2020 14:47:17 +0000 (14:47 +0000)
commit3c9b99ef7115fa88ef4d744cc2afc424bd5c3ef2
tree4f761351c67a25f51e3ad6fabf99175d2f1720b7
parent054d7b9f6f6816a83dcadfdfe2532795cae04ff3
libstdc++: Make std::function work better with -fno-rtti

This change allows std::function::target<F>() to work even without RTTI,
using the same approach as std::any. Because we know what the manager
function would be for a given type, we can check if the stored pointer
has the expected address. If it does, we don't need to use RTTI. If it
isn't equal, we still need to do the RTTI check (when RTTI is enabled)
to handle the case where the same function has different addresses in
different shared objects.

This also changes the implementation of the manager function to return a
null pointer result when asked for the type_info of the target object.
This not only avoids a warning with -Wswitch -Wsystem-headers, but also
avoids prevents std::function::target_type() from dereferencing an
uninitialized pointer when the linker keeps an instantiation of the
manager function that was compiled without RTTI.

Finally, this fixes a bug in the non-const overload of function::target
where calling it with a function type F was ill-formed, due to
attempting to use const_cast<F*>(ptr). The standard only allows
const_cast<T*> when T is an object type.  The solution is to use
*const_cast<F**>(&ptr) instead, because F* is an object type even if F
isn't. I've also used _GLIBCXX17_CONSTEXPR in function::target so that
it doesn't bother instantiating anything for types that can never be a
valid target.

libstdc++-v3/ChangeLog:

* include/bits/std_function.h (_Function_handler<void, void>):
Define explicit specialization used for invalid target types.
(_Base_manager::_M_manager) [!__cpp_rtti]: Return null.
(function::target_type()): Check for null pointer.
(function::target()): Define unconditionall. Fix bug with
const_cast of function pointer type.
(function::target() const): Define unconditionally, but
only use RTTI if enabled.
* testsuite/20_util/function/target_no_rtti.cc: New test.
libstdc++-v3/include/bits/std_function.h
libstdc++-v3/testsuite/20_util/function/target_no_rtti.cc [new file with mode: 0644]