Skip to content

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

Merged
mingxwa merged 2 commits into
ngcpp:feature/v5from
mingxwa:user/mingxwa/fix-destruction
Sep 9, 2026
Merged

Reset the proxy when the destructor of the underlying pointer throws#82
mingxwa merged 2 commits 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.
  • Removed the use of std::destroy_at because Microsoft STL cleverly strengthened it even though the standard didn't say that.

Codegen is unchanged where destructibility is nothrow.

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, which is the first pointer in the suite with a
potentially throwing destructor and the first use of a facade whose
destructibility is nontrivial.
@mingxwa
mingxwa marked this pull request as draft September 8, 2026 02:02
@mingxwa
mingxwa force-pushed the user/mingxwa/fix-destruction branch from 249c6fd to 2a0012b Compare September 8, 2026 02:05
@mingxwa
mingxwa force-pushed the user/mingxwa/fix-destruction branch from 2a0012b to 7ec78d0 Compare September 9, 2026 02:07
@mingxwa
mingxwa marked this pull request as ready for review September 9, 2026 02:26
@tian-lt-personal

tian-lt-personal commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Microsoft STL cleverly strengthened it

Hmm, pretty clever🫤

@mingxwa
mingxwa merged commit 7c4e174 into ngcpp:feature/v5 Sep 9, 2026
18 checks passed
@mingxwa
mingxwa deleted the user/mingxwa/fix-destruction branch September 9, 2026 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants