https://gcc.gnu.org/g:d49d359b0c5266b314bcf31405746909d99927a1
commit r16-2746-gd49d359b0c5266b314bcf31405746909d99927a1 Author: Tomasz Kamiński <tkami...@redhat.com> Date: Fri Aug 1 09:21:27 2025 +0200 libstdc++: Fix dereferencing of std::indirect xvalues [PR121128] 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. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Signed-off-by: Tomasz Kamiński <tkami...@redhat.com> Diff: --- libstdc++-v3/include/bits/indirect.h | 7 ++- .../testsuite/std/memory/indirect/access.cc | 58 ++++++++++++++++++++++ .../testsuite/std/memory/polymorphic/access.cc | 53 ++++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/include/bits/indirect.h b/libstdc++-v3/include/bits/indirect.h index e8000d7c0243..89fa8c874fbd 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 000000000000..cf21275a115b --- /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 000000000000..7b95bb192631 --- /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()); +}