https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89303
--- Comment #13 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Alexandre Duret-Lutz from comment #6)
> I mentioned in my first comment that I had also cases that threw
> bad_weak_ptr.
> I've been able to reproduce those as follows:
>
> % cat badwptr.cc
> #include <memory>
>
> class blob final: public std::enable_shared_from_this<blob>
> {
> };
>
> int main()
> {
> std::shared_ptr<blob> tg = std::make_shared<blob>();
> return tg->shared_from_this().use_count();
At this line GDB shows that tg._M_ptr->_M_weak_this._M_refcount is not set
correctly:
(gdb) p *tg._M_ptr
$6 = {<std::__enable_shared_from_this<blob, 0>> = {_M_weak_this = {_M_ptr =
0x614e80, _M_refcount = {_M_pi = 0x0}}}, data = 0x614ea0}
(gdb) p tg
$7 = {_M_ptr = 0x614e80, _M_refcount = {_M_pi = 0x614e70}}
The _M_weak_this._M_refcount._M_pi member should be the same pointer as
tg._M_refcount._M_pi.
This suggests that __weak_count::_M_assign didn't work properly, and that could
also explain why the weak count isn't correctly decremented in the other test
case, if _M_pi is null here:
~__weak_count() noexcept
{
if (_M_pi != nullptr)
{
_M_pi->_M_weak_release();
}
#ifdef FIXME
else __builtin_puts("null");
#endif
}
That would explain why _M_weak_release() isn't called.
So if the two bugs have the same root cause, it seems to be a problem in
__weak_count::_M_assign, failing to set _M_pi here:
__weak_count&
operator=(const __shared_count<_Lp>& __r) noexcept
{
_Sp_counted_base<_Lp>* __tmp = __r._M_pi;
if (__tmp != nullptr)
__tmp->_M_weak_add_ref();
if (_M_pi != nullptr)
_M_pi->_M_weak_release();
_M_pi = __tmp;
return *this;
}
> }
>
> % g++ -g -O badwptr.cc
> % ./a.out
> terminate called after throwing an instance of 'std::bad_weak_ptr'
> what(): bad_weak_ptr
>
> When compiled with -O2 or -O0, the program exits with $?=2 as expected.