On Tue, 19 Aug 2025, Tomasz Kamiński wrote: > 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.
LGTM > > 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. > --- > Testing on x86_64-linux locally. OK for trunk? > > libstdc++-v3/include/bits/funcwrap.h | 14 ++++------- > .../20_util/copyable_function/call.cc | 23 +++++++++++++++++++ > .../20_util/move_only_function/call.cc | 23 +++++++++++++++++++ > 3 files changed, 50 insertions(+), 10 deletions(-) > > diff --git a/libstdc++-v3/include/bits/funcwrap.h > b/libstdc++-v3/include/bits/funcwrap.h > index 9db4ab7811a..70ecfd93e36 100644 > --- a/libstdc++-v3/include/bits/funcwrap.h > +++ b/libstdc++-v3/include/bits/funcwrap.h > @@ -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 > diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc > b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc > index 0ac5348f2fe..605422d5595 100644 > --- a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc > +++ b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc > @@ -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(); > } > diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc > b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc > index 72c8118d716..34ca73b4eb4 100644 > --- a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc > +++ b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc > @@ -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(); > } > -- > 2.50.1 > >