Skip to content

Reset the proxy when the destructor of the underlying pointer throws - #82

Draft
mingxwa wants to merge 1 commit into
ngcpp:feature/v5from
mingxwa:user/mingxwa/fix-destruction
Draft

Reset the proxy when the destructor of the underlying pointer throws#82
mingxwa wants to merge 1 commit into
ngcpp:feature/v5from
mingxwa:user/mingxwa/fix-destruction

Conversation

@mingxwa

@mingxwa mingxwa commented Sep 8, 2026

Copy link
Copy Markdown
Member

Changes

  • Reset the metadata whenever the contained value is destroyed, by giving the destroy meta an rvalue-qualified overload. A throwing destructor previously left has_value() true over a destroyed object, so the next destruction ran on a dead object. Every path that discards a value was affected, namely reset(), operator=(nullptr), both branches of copy assignment, move assignment, operator=(P&&) and both emplace overloads.
  • Reduced destroy_dispatch to a no-op dispatch.
  • Added three tests covering the destructor, operator=(P&&) and copy assignment.

Codegen is unchanged where destructibility is nothrow.

@mingxwa
mingxwa marked this pull request as draft September 8, 2026 02:02
proxy::destroy() invoked destruction through an lvalue-qualified
overload, so the meta_resetting_guard that invoke_impl applies to
consuming overloads never ran. When the destructor of the underlying
pointer threw, the metadata still pointed at the destroyed object, so
has_value() stayed true and the next destruction ran on a dead object.
Every path that discards a value reached this: reset(),
operator=(nullptr), both branches of the copy assignment operator, the
move assignment operator, operator=(P&&) and both emplace overloads.

Destruction is a consuming operation like relocation, so give it the
same shape. The destroy meta now uses an rvalue-qualified overload,
which makes erased_context destroy the pointer through destroying_guard
and makes invoke_impl clear the metadata on both the normal and the
exceptional path. destroy_dispatch keeps only its tag role and its call
operator becomes a no-op, because the destruction it used to perform is
what the rvalue machinery already does.

The added reset is dead on the non-throwing path and the optimizer
removes it. At -O2 the disassembly of ~proxy, reset and the move
assignment operator is unchanged for a facade whose destructibility is
nothrow.

LifetimeTracker gains ThrowingDestructionSession, a Session whose
destructor throws once the tracker is armed through
ThrowOnNextDestruction, mirroring ThrowOnNextConstruction. It is the
first pointer in the suite with a potentially throwing destructor and
the first use of a facade whose destructibility is nontrivial. The throw
is conditional so that the destructor keeps a normal return path, which
MSVC requires under C4722.
@mingxwa
mingxwa force-pushed the user/mingxwa/fix-destruction branch from 249c6fd to 2a0012b Compare September 8, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant