mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
libstdc++: Reduce chances of object aliasing for function wrapper.
Previously, an empty functor (EmptyIdFunc) stored inside a std::move_only_function being first member of a Composite class could have the same address as the base of the EmptyIdFunc type (see included test cases), resulting in two objects of the same type at the same address. This commit addresses the issue by moving the internal buffer from the start of the wrapper object to a position after the manager function pointer. This minimizes aliasing with the stored buffer but doesn't completely eliminate it, especially when multiple empty base objects are involved (PR121180). To facilitate this member reordering, the private section of _Mo_base was eliminated, and the corresponding _M_manager and _M_destroy members were made protected. They remain inaccessible to users, as user-facing wrappers derive from _Mo_base privately. libstdc++-v3/ChangeLog: * include/bits/funcwrap.h (__polyfunc::_Mo_base): Reorder _M_manage and _M_storage members. Make _M_destroy protected and remove friend declaration. * testsuite/20_util/copyable_function/call.cc: Add test for aliasing base class. * testsuite/20_util/move_only_function/call.cc: Likewise. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Reviewed-by: Patrick Palka <ppalka@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
@@ -419,6 +419,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
_M_manage = _Manager::_S_empty;
|
||||
}
|
||||
|
||||
void _M_destroy() noexcept
|
||||
{ _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); }
|
||||
|
||||
~_Mo_base()
|
||||
{ _M_destroy(); }
|
||||
|
||||
@@ -434,17 +437,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
std::swap(_M_manage, __x._M_manage);
|
||||
}
|
||||
|
||||
_Storage _M_storage;
|
||||
|
||||
private:
|
||||
void _M_destroy() noexcept
|
||||
{ _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); }
|
||||
|
||||
_Manager::_Func _M_manage;
|
||||
|
||||
#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED
|
||||
friend class _Cpy_base;
|
||||
#endif // __glibcxx_copyable_function
|
||||
_Storage _M_storage;
|
||||
};
|
||||
#endif // __glibcxx_copyable_function || __glibcxx_copyable_function
|
||||
} // namespace __polyfunc
|
||||
|
||||
@@ -214,6 +214,28 @@ test_params()
|
||||
std::copyable_function<void(CompleteEnum)> f4;
|
||||
}
|
||||
|
||||
struct EmptyIdFunc
|
||||
{
|
||||
EmptyIdFunc* operator()()
|
||||
{ return this; }
|
||||
};
|
||||
|
||||
struct Composed : EmptyIdFunc
|
||||
{
|
||||
std::move_only_function<EmptyIdFunc*()> nested;
|
||||
};
|
||||
|
||||
void
|
||||
test_aliasing()
|
||||
{
|
||||
Composed c;
|
||||
c.nested = EmptyIdFunc{};
|
||||
|
||||
EmptyIdFunc* baseAddr = c();
|
||||
EmptyIdFunc* nestedAddr = c.nested();
|
||||
VERIFY( baseAddr != nestedAddr );
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
@@ -222,4 +244,5 @@ int main()
|
||||
test04();
|
||||
test05();
|
||||
test_params();
|
||||
test_aliasing();
|
||||
}
|
||||
|
||||
@@ -214,6 +214,28 @@ test_params()
|
||||
std::move_only_function<void(CompleteEnum)> f4;
|
||||
}
|
||||
|
||||
struct EmptyIdFunc
|
||||
{
|
||||
EmptyIdFunc* operator()()
|
||||
{ return this; }
|
||||
};
|
||||
|
||||
struct Composed : EmptyIdFunc
|
||||
{
|
||||
std::move_only_function<EmptyIdFunc*()> nested;
|
||||
};
|
||||
|
||||
void
|
||||
test_aliasing()
|
||||
{
|
||||
Composed c;
|
||||
c.nested = EmptyIdFunc{};
|
||||
|
||||
EmptyIdFunc* baseAddr = c();
|
||||
EmptyIdFunc* nestedAddr = c.nested();
|
||||
VERIFY( baseAddr != nestedAddr );
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
@@ -222,4 +244,5 @@ int main()
|
||||
test04();
|
||||
test05();
|
||||
test_params();
|
||||
test_aliasing();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user