On Fri, 1 Aug 2025, 12:55 Tomasz Kamiński, <tkami...@redhat.com> wrote:
> Forr rvalues the _Self parameter deduces a non-reference type. > Consequently, > ((_Self)__self) moved the object to a temporary, which then destroyed on > function exit. > > This patch fixes this by using a C-style cast __self to (const indirect&). > This not only resolves the above issue but also correctly handles types > that > are derived (publicly and privately) from indirect. Allocator requirements > in > [allocator.requirements.general] p22 guarantee that dereferencing const > _M_objp > works with equivalent semantics to dereferencing _M_objp. > > PR libstdc++/121128 > > libstdc++-v3/ChangeLog: > > * include/bits/indirect.h (indirect::operator*): > Cast __self to approparietly qualified indirect. > * testsuite/std/memory/indirect/access.cc: New test. > * testsuite/std/memory/polymorphic/access.cc: New test. > --- > I picked this up, as another case in "obvious fix" category, and would > still clasify it as so. However, I decied to not perfectly forward pointer, > as allowed by the standard. > OK, thanks > Tested on x86_64-linux. > > libstdc++-v3/include/bits/indirect.h | 7 ++- > .../testsuite/std/memory/indirect/access.cc | 58 +++++++++++++++++++ > .../std/memory/polymorphic/access.cc | 53 +++++++++++++++++ > 3 files changed, 116 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/memory/indirect/access.cc > create mode 100644 libstdc++-v3/testsuite/std/memory/polymorphic/access.cc > > diff --git a/libstdc++-v3/include/bits/indirect.h > b/libstdc++-v3/include/bits/indirect.h > index e8000d7c024..89fa8c874fb 100644 > --- a/libstdc++-v3/include/bits/indirect.h > +++ b/libstdc++-v3/include/bits/indirect.h > @@ -286,8 +286,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > constexpr auto&& > operator*(this _Self&& __self) noexcept > { > - __glibcxx_assert(__self._M_objp != nullptr); > - return std::forward_like<_Self>(*((_Self)__self)._M_objp); > + // n.b. [allocator.requirements.general] p22 implies > + // dereferencing const pointer is same as pointer > + const indirect& __iself = (const indirect&)__self; > + __glibcxx_assert(__iself._M_objp != nullptr); > + return std::forward_like<_Self>(*__iself._M_objp); > } > > constexpr const_pointer > diff --git a/libstdc++-v3/testsuite/std/memory/indirect/access.cc > b/libstdc++-v3/testsuite/std/memory/indirect/access.cc > new file mode 100644 > index 00000000000..cf21275a115 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/memory/indirect/access.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++26 } } > + > +#include <memory> > +#include <vector> > + > +#include <testsuite_hooks.h> > + > +template<template<typename> class Indirect> > +constexpr void > +test_access() > +{ > + const std::vector<int> src{1, 2, 3, 4, 5}; > + Indirect<std::vector<int>> i(src); > + auto const& ci = i; > + VERIFY( *i == src ); > + VERIFY( *ci == src ); > + VERIFY( *std::move(ci) == src ); > + > + std::vector<int>&& vr = *std::move(i); > + VERIFY( vr == src ); > + VERIFY( *i == src ); > + > + std::vector<int> vc = *std::move(i); > + VERIFY( vc == src ); > + VERIFY( vr.empty() ); > + VERIFY( i->empty() ); > + VERIFY( ci->empty() ); > +} > + > +template<typename T> > +struct PublicBase : std::indirect<T> > +{ > + using std::indirect<T>::indirect; > +}; > + > +template<typename T> > +class PrivateBase : std::indirect<T> > +{ > +public: > + using std::indirect<T>::indirect; > + using std::indirect<T>::operator*; > + using std::indirect<T>::operator->; > +}; > + > +constexpr bool > +test_all() > +{ > + test_access<std::indirect>(); > + test_access<PublicBase>(); > + test_access<PrivateBase>(); > + return true; > +} > + > +int main() > +{ > + test_all(); > + static_assert(test_all()); > +} > diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/access.cc > b/libstdc++-v3/testsuite/std/memory/polymorphic/access.cc > new file mode 100644 > index 00000000000..7b95bb19263 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/access.cc > @@ -0,0 +1,53 @@ > +// { dg-do run { target c++26 } } > + > +#include <memory> > +#include <vector> > + > +#include <testsuite_hooks.h> > + > +template<template<typename> class Polymorhpic> > +constexpr void > +test_access() > +{ > + const std::vector<int> src{1, 2, 3, 4, 5}; > + Polymorhpic<std::vector<int>> i(src); > + auto const& ci = i; > + VERIFY( *i == src ); > + VERIFY( *ci == src ); > + VERIFY( *std::move(ci) == src ); > + > + auto&& vr = *std::move(i); > + static_assert( std::is_same_v<decltype(vr), std::vector<int>&> ); > + VERIFY( vr == src ); > + VERIFY( *i == src ); > +} > + > +template<typename T> > +struct PublicBase : std::polymorphic<T> > +{ > + using std::polymorphic<T>::polymorphic; > +}; > + > +template<typename T> > +class PrivateBase : std::polymorphic<T> > +{ > +public: > + using std::polymorphic<T>::polymorphic; > + using std::polymorphic<T>::operator*; > + using std::polymorphic<T>::operator->; > +}; > + > +constexpr bool > +test_all() > +{ > + test_access<std::polymorphic>(); > + test_access<PublicBase>(); > + test_access<PrivateBase>(); > + return true; > +} > + > +int main() > +{ > + test_all(); > +// static_assert(test_all()); > +} > -- > 2.49.0 > >