spec: 0056 Deterministic Resource Cleanup — defer によるスコープ脱出時のファイナライザ - #21
Merged
Merged
Conversation
ホスト資源(fd・ソケット)をスコープ脱出の全経路で確定的に解放する言語機構を 定める。ブロック項 `defer <expr>` を導入し、正常完了・エラーチャネル・自己末尾 呼び出し(0045)のいずれでも action がちょうど 1 回走ることを保証する。 0024/0048 A2 の finalizer 禁止とは衝突しない。禁止対象は RC 回収の観測であり、 `defer` はレキシカルで参照カウントを見ない(D12)。RC を持たないバックエンドで 意味論が変わらないことがその実証になる。 キャンセルは第 3 のチャネルを作らず、協調キャンセルとしてエラーチャネルに載せる 方針を Open Questions に記す。接続断・タイムアウトは既に throws チャネル上にある。 README の Current Drafts に 0056 を追記し、漏れていた 0053・0054 も補う。
This was referenced Jul 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ホスト資源(fd・ソケット)をスコープ脱出の全経路で確定的に解放する言語機構を定める SPEC 0056 を追加する。ブロック項
defer <expr>を導入し、その action がthrow/?/ 伝播)のいずれにおいてもちょうど 1 回実行されることを保証する(D4)。
経路 3 が必須なのは、Emela がループ構文を持たずすべての反復が自己末尾呼び出しだからである。これを欠くと
deferはループ本体で一度も実行されない。Motivation
現在、解放は明示的な
close呼び出しだけが担っており、エラー経路で確実に漏れる。std.fsのread_fileが実例で、read_all_loop(f)?が throw するとraw_close(f.id)に到達しない。同型の漏れがwrite_fileと 0046 のHttpServer.respond/accept_oneにもある。とりわけ accept ループは 0045 により有界メモリで無限に回るため、1 反復 1 fd の漏れは確実に枯渇へ至る。0011 の
try/catchは「必ず実行される節」を持たないためこの問題を解けず、?を使う限り脱出点はソース上に現れない。主な設計判断
bracket(acquire, release, body)を stdlib 関数として書く案は採らない。 body をクロージャにすると 0045 T4 によりその中の再帰呼び出しが自己末尾呼び出しにならず、サーバの accept ループを bracket の中に書けない。加えてthrows多相が言語に存在せず、クロージャはヒープ確保を伴う。deferはuse x = e形を包含するので、糖衣は後から足せる。0024 / 0048 A2 の finalizer 禁止とは衝突しない。 禁止対象は RC 回収(refcount が 0 になる瞬間)の観測である。
deferはレキシカルで参照カウントを見ず(D12)、値の生存に依存しない。RC 命令を無視するバックエンド(0048 A9)でも意味論が同一であることがその実証になる — デストラクタならそこで意味が変わる。所有権・借用・線形性は導入しない(D11、0000 の非目標を維持)。安全性は「
closeは冪等」(0050 P7 / 0055 P2)という既存方針に依拠する。キャンセルは第 3 のチャネルを作らない。 実際に途中で打ち切られる事象(接続断
SocketError::ConnectionClosed、HttpError::Timeout、ホストの失敗)はすべて既に throws チャネルに乗っている。協調キャンセルを採る限り D4 の経路 2 がそのまま覆う。非協調なプリエンプションは trap 相当で、D10 により action は走らない。その他
panicは action を実行しない(D10)。ホスト資源はインスタンス破棄時にホストが回収する。参照実装では両バックエンドが追加実装ゼロでこう振る舞う。throwsを持てず、型はUnitかNever(D8)。Socket.close/Fs.closeは既に infallible なので実用上の制約はほぼない。実装は emela 側で完了済み(3 経路 × wasm/js-node の両方で検証、
live_bytes == 0、fd リーク回帰テストつき)。