[PATCH] libstdc++: Fix dereferencing std::indirect xvalues [PR121128]
Jonathan Wakely
jwakely.gcc@gmail.com
Fri Aug 1 13:44:04 GMT 2025
On Fri, 1 Aug 2025, 12:55 Tomasz Kamiński, <tkaminsk@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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250801/6a6138ea/attachment.htm>
More information about the Libstdc++
mailing list