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
>
>

Reply via email to