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 <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
Tomasz Kamiński
2025-08-01 09:21:27 +02:00
parent d9f34c951a
commit d49d359b0c
3 changed files with 116 additions and 2 deletions

View File

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

View File

@@ -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());
}

View File

@@ -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());
}