Skip to content

lifter: destroy an inheritable object through its vtable, even an emp… - #2532

Merged
Araq merged 1 commit into
masterfrom
closure-env-destroy
Sep 16, 2026
Merged

Araq merged 1 commit into
masterfrom
closure-env-destroy

Conversation

@Araq

@Araq Araq commented Sep 16, 2026

Copy link
Copy Markdown
Member

…ty one

A closure's environment is a ref to a compiler-made object deriving from RootObj, and the closure value knows it only as (ref RootObj). Its =destroy decremented the count and freed the block, but never ran the env's OWN destructor: RootObj has no fields, isTrivialTypeDecl called it trivial, and emitRefDestructor skipped the payload. Every captured string, ref or resource in every closure leaked.

An object with RTTI is never trivial for =destroy now: the value may be a DERIVED object that owns something, and its =destroy is a method. The synthesized =destroy_RootObj is an empty method — the vtable slot is the point — and the ref RootObj hook dispatches through it to the env's synthesized destroy, which releases the captures. Same mechanism the existing user-hierarchy dispatch (txdestroy) relies on; =wasMoved already had this exception for the vtable field.

Test: tclosure_env_release — a captured resource with a loud destructor is released when the last reference to the closure goes, and not before.

…ty one

A closure's environment is a ref to a compiler-made object deriving from
RootObj, and the closure value knows it only as `(ref RootObj)`. Its
=destroy decremented the count and freed the block, but never ran the env's
OWN destructor: RootObj has no fields, `isTrivialTypeDecl` called it
trivial, and `emitRefDestructor` skipped the payload. Every captured
string, ref or resource in every closure leaked.

An object with RTTI is never trivial for `=destroy` now: the value may be
a DERIVED object that owns something, and its `=destroy` is a method. The
synthesized `=destroy_RootObj` is an empty method — the vtable slot is the
point — and the `ref RootObj` hook dispatches through it to the env's
synthesized destroy, which releases the captures. Same mechanism the
existing user-hierarchy dispatch (txdestroy) relies on; `=wasMoved`
already had this exception for the vtable field.

Test: tclosure_env_release — a captured resource with a loud destructor is
released when the last reference to the closure goes, and not before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Araq
Araq merged commit 2ded351 into master Sep 16, 2026
9 checks passed
@Araq
Araq deleted the closure-env-destroy branch September 16, 2026 21:12
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.

2 participants