refactor(lifecycle): Phase 285 P2.1 hygiene - lifecycle.rs 箱化完了

責務分離により KeepAlive 処理を handlers/lifecycle.rs に隔離:
- handlers/lifecycle.rs 新規作成(47行)
  - handle_keepalive() で drop_after 判定
  - release_strong_refs() で Arc 同一性探索を集約
  - HashSet による O(1) 検索最適化
- handlers/mod.rs を dispatch 専用化(49行削除→1行呼び出し)
- README.md に P2.1 完了状態を記載(SKIP→PASS)
- phase-29y 将来設計相談パケット追加(NyRT ABI/RC insertion pass)

Test: 154/154 PASS 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-26 13:36:17 +09:00
parent 3bf0dee2b0
commit f74ff6116c
4 changed files with 254 additions and 67 deletions

View File

@ -0,0 +1,173 @@
Status: Active
Date: 2025-12-26
Scope: Self-host後に “脱RustランタイムNyRT/.hako” を進めるため、MIR命令語彙と runtime ABIlifecycle/RC/weakの境界をどう固めるべきか相談するパケット
Related:
- docs/reference/language/lifecycle.md
- docs/development/current/main/phases/phase-285/README.md
- docs/development/current/main/design/edgecfg-fragments.md
- docs/development/current/main/investigations/phase-286-plan-normalization-consult.md
# Phase 29y (future): MIR lifecycle vocab freeze 相談パケット
## 0. 相談の意図
この文書は「今すぐ MIR 命令を増やす」提案を求めない。
self-host後に脱Rustを進める前提で、**MIRのどこまでを語彙として固定し、どこからを runtime ABINyRT/.hakoに委譲するか**を設計SSOT化するための相談パケット。
強調:
- “脱Rust” はゴールだが、**実装移行NyRT/.hako化は別フェーズ**でやるこの文書はその設計SSOTの下敷き
- Phase 285weak conformance / hidden root 根治)が未解決なら、先に Phase 285 を優先する。
## 1. 背景(いま分かったこと)
- weak/strong の意味論は言語SSOT`docs/reference/language/lifecycle.md`)で定義されている。
- VM 実装では “hidden root” が起きうる(例: SSA値を保持する `regs` / `mem` 等が strong を保持しうる)。
- LLVM/wasm/他言語へ広げるには、Rust VM の内部構造に依存しない **共通の境界ABI/観測点)**が必要。
## 2. 相談したい焦点
### Q1. MIRに参照カウンタretain/releaseを載せるべきか
候補:
- A) **MIRは寿命を語らず**、runtime ABI で retain/release/weak を表現するMIR→loweringがABI呼び出しへ
- B) MIRに **薄い寿命effect**Retain/Release等を追加し、後段で最適化するliveness/use-count由来
求める回答:
- “移植性LLVM/wasm” と “実装コスト/破綻リスクPHI/early-exit/例外)” の観点で、段階移行のおすすめ。
### Q2. “観測点” をどこに置くべきか?
目的は「weak_to_strong が成功する理由strong root の残留)」を追えること。
候補:
- runtime 側で root summary APIregs/mem/obj_fields/handlesの強参照数
- MIR 側で liveness の契約を固定し、VMがそれに従って解放する
## 2.1 推奨の設計分離(相談ベースライン)
相談の叩き台(推奨):
- **参照カウンタの実体(カウンタ値)は runtimeNyRT**に置くMIRは“値”として持たない
- **retain/release/weak_* の“発火点”はコンパイラ側が決める**が、語彙の増殖を避けるため “1箇所” に寄せる。
- 候補: `CorePlan → Frag → emit_frag()` の流れの **Frag直前/直後に 1 回だけ**走る RC insertion pass。
- backendVM/LLVM/wasmは、その RC event 列を **NyRT ABI 呼び出し**へ落とすだけにする。
狙い:
- MIR/CorePlan は “意味論の語彙BoxRef/WeakRef/weak_to_strong” に留め、RC 実装の詳細を押し込めない。
- RC の発火点は “見える” ようにしつつ、分散実装を防ぐhidden root の再発を減らす)。
- self-host後に NyRT を `.hako runtime` に置換しても、ABIがSSOTなら導線が崩れにくい。
## 2.2 最小 NyRT ABI相談先に提示したい候補
### strong参照カウント
- `nyrt_retain(BoxRef)`
- `nyrt_release(BoxRef)`
注意:
- `release` が strong==0 になったとき “物理解放” は runtime の責務。
- `fini()` は言語SSOT上 “論理終端” であり、GC/RCで勝手に呼ばない`docs/reference/language/lifecycle.md`)。
### weak弱参照
- `nyrt_weak_new(BoxRef) -> WeakRef`
- `nyrt_weak_drop(WeakRef)`
- `nyrt_weak_to_strong(WeakRef) -> BoxRef | null`
SSOT整合:
- `weak_to_strong()` は Alive のみ成功し、Dead/Freed は `null` を返す(`docs/reference/language/lifecycle.md`)。
### optional診断/可観測性)
実装は後でよいが、相談では “観測点” を固定しておきたい。
- `nyrt_debug_roots_summary()`(カテゴリ別の strong root 数など)
- `nyrt_debug_refcounts(BoxRef)`(単体の strong/weak 数など)
制約:
- 診断は意味論を変えないON/OFFで挙動が変わらない
- env sprawl を防ぐため、既存の verbose/trace と統合するPhase 285 の方針と整合)。
## 2.3 関数 ABIowned/borrowed の最小規約)
相談したい焦点(推奨案):
- 引数は **borrowed**(呼び出し中は alive を呼び出し側が保証、callee は retain/release しない)。
- 戻り値は **owned**callee は +1 を返し、caller が release 責務を持つ)。
この規約の狙い:
- 関数境界の retain/release を最小語彙で固定できる。
- VM/LLVM/wasm で ABI を揃えやすい。
## 2.4 “RC insertion pass” の置き場所SSOT候補
目的:
- retain/release/weak_drop を “分散実装” させず、**1箇所**で決める。
候補(相談):
- `Frag` に入る直前CorePlan から “CFGが確定した後”に、RC event 列を挿入する。
- `PHI/loop/early-exit/cleanup` を見た上で挿入できる(誤りが減る)。
- Plan/Frag SSOT の “出口語彙return/break/continue” と整合しやすい。
相談したい点:
- “RC event” を MIR 命令として持つか(`Retain/Release/WeakDrop`)、あるいは `Frag` の effect として表すか。
- 最初は正しさを優先し、冗長操作削除(最適化)は別フェーズに分離するのが安全か。
## 3. 非目標(今回はやらない)
- MIR語彙の大改造型システムへ所有モデルを埋め込む等
- GCアルゴリズム刷新
- self-host前に NyRT を .hako 化する実装
## 4. 期待するアウトプット(相談先に求める形式)
- 最終形ではなく **段階移行1フェーズ=小差分)**のマイルストーン
- 各フェーズの受け入れ条件smoke/verify/contract
- “やると破綻しやすい罠” の列挙PHI/early return/exception/cleanup
## 4.1 マイルストーン案(相談先に求めたい “小刻み” の形)
Milestone Adocs-first:
- NyRT ABI最小セットと関数 ABIowned/borrowedを SSOT 化
- 受け入れ: doc が `lifecycle.md` と矛盾しない、smoke の観測点が明確
Milestone Bconformance-first:
- Phase 285 の weak-failhidden rootを根治し、VM で conformance を固定
- 受け入れ: weak-fail fixture が PASS、quick gate が green
Milestone Ccompiler-side SSOT:
- RC insertion pass の設計SSOTどこで何を挿入するかを確定まだ実装しない
- 受け入れ: PHI/loop/return/cleanup のケース分類がある、破綻ポイントが明文化されている
Milestone Dimplementation:
- RC insertion pass を実装し、VM/LLVM/wasm が同じ ABI を呼ぶ形にする
- 受け入れ: 既存 fixture を使って VM/LLVM parity を維持、診断が意味論を変えない
## 4.2 罠リスト(相談先に “やらない方がいい” として確認したい)
- “SSAの last-use = 言語寿命” を意味論にしてしまうweak_to_strong で観測できて破綻しやすい)。
- by-name で例外処理Box名/関数名の文字列一致で retain/release を変える)。
- 環境変数で挙動を分岐し、既定が揺れるenv sprawl
- RC と `fini()` を混ぜるGC/RC で勝手に `fini()` を呼ぶ等)。
- “万能 structOptionだらけ” で語彙統合し、結局デバッグできなくなる。
## 5. 参考現時点のSSOT
- lifecycle semantics SSOT: `docs/reference/language/lifecycle.md`
- Phase 285 conformance: `docs/development/current/main/phases/phase-285/README.md`
## 6. 相談先に渡すための最小質問テンプレ
「ソースコードを見なくても回答できる」ことを重視する。
1) 推奨の分離は A/B のどれか?
- A: MIRは寿命を語らず、NyRT ABI を呼ぶ
- B: MIR/Frag に薄い寿命effectRetain/Releaseを載せて“見える化”する
2) 関数 ABI は “args borrowed / return owned” が妥当か?
- もし違うなら、最小で安全な代替案は何か?
3) RC insertion pass を置く場所はどこが安全か?
- CorePlan→Frag 直前/直後/emit直前など、PHI/loop/cleanup を踏まえて推奨を教えてほしい
4) weak_to_strong の失敗条件Dead/Freedを実装間で揃えるための “観測点” はどこが良いか?
- runtime に root summary API を置くのが妥当か?
5) 破綻しやすい罠(特に PHI/early return/cleanupを列挙してほしい

View File

@ -1,6 +1,6 @@
# Phase 285: Box lifecycle / weakref / finalization / GC conformance
Status: P0 COMPLETE (2025-12-26); A1 series + LLVM sub-phases implemented
Status: P0/P1/P2/P2.1 ✅ COMPLETE (2025-12-26)
## Next (P0 docs-only → P1/P2)
@ -9,6 +9,32 @@ Status: P0 COMPLETE (2025-12-26); A1 series + LLVM sub-phases implemented
- lifecycle/weak/fini/GC: `docs/reference/language/lifecycle.md`
- `null`/`void`/truthiness: `docs/reference/language/types.md`
## P2.1Hidden root investigation & fix✅ COMPLETE (2025-12-26)
P2 の weak-fail fixture明示 drop `x = null`)が “失敗→null” にならず、`weak_to_strong()` が成功してしまうhidden root問題を根治した。
### Root cause要旨
- VM の `regs` が古い `ValueId` を保持し続け、`Arc` が drop されない
- SSA last-use をそのまま寿命にすると、言語の block-scoped locals と衝突して `weak_basic` が壊れる
### Fix要旨
MIR 命令 `KeepAlive` を “スコープ維持 / 上書きdrop” の両方に使い分ける。
- `drop_after=false`scope-end keepalive: スコープ終端まで値を生存維持language scope semantics
- `drop_after=true`overwrite drop: 変数上書き前の旧値を解放weak-fail を成立させる)
- SSA `Copy` により同一 `Arc` を複数 `ValueId` が参照するため、VM は **alias も含めて** `regs` から除去する
実装の責務分離hygiene:
- VM 側の KeepAlive 処理は `src/backend/mir_interpreter/handlers/lifecycle.rs` に隔離dispatch から分離)
### Verification
- `apps/tests/phase285_weak_basic.hako`: exit 2 ✅
- `apps/tests/phase285_p2_weak_upgrade_fail_min.hako`: exit 1 ✅
- quick gate: `./tools/smokes/v2/run.sh --profile quick` → 154/154 PASS ✅
## LLVM Sub-Phases Status
| Phase | Status | Summary |
@ -233,18 +259,13 @@ If any of the following are missing, treat weak smokes as **unsupported** and sc
1. `tools/smokes/v2/profiles/integration/apps/phase285_p2_weak_upgrade_success_vm.sh`
- Fixture A 実行、期待: **exit 2****PASS**
2. `tools/smokes/v2/profiles/integration/apps/phase285_p2_weak_upgrade_fail_vm.sh`
- Fixture B 実行、**SKIP**(既知の hidden root 問題)
- Fixture B 実行、期待: **exit 1****PASS**
**LLVM smoke scripts2本**:
3. `tools/smokes/v2/profiles/integration/apps/phase285_p2_weak_upgrade_success_llvm.sh`
- Fixture A 実行LLVM harness、期待: **exit 2****PASS** または理由付き SKIP
4. `tools/smokes/v2/profiles/integration/apps/phase285_p2_weak_upgrade_fail_llvm.sh`
- Fixture B 実行LLVM harness**SKIP**(既知の hidden root 問題、VM と同じ)
**既知の問題Fixture B**:
- **Hidden root issue**: `x = null` で明示 drop しても weak_to_strong が成功exit 0
- 原因: 隠れた root が strong ref を保持している可能性
- 対応: Phase 285 P2.1 (investigation) で root 保持箇所を棚卸し
- Fixture B 実行LLVM harness期待: **exit 1****PASS** または理由付き SKIP
**LLVM 対応**:
- WeakNew/WeakLoad は **両バックエンド実装済み**P1 確認済み)→ PASS が理想
@ -256,30 +277,16 @@ If any of the following are missing, treat weak smokes as **unsupported** and sc
- ✅ Fixture B 新規作成(明示 drop 方式)
- ✅ VM smoke success PASS
- ✅ LLVM smoke success PASSまたは理由付き SKIP
- ✅ VM/LLVM smoke fail SKIPhidden root 問題で理由付き
- ✅ VM smoke fail PASSexit 1
- ✅ LLVM smoke fail PASSまたは理由付き SKIP
- ✅ quick 154/154 PASS 維持
- ✅ Finalizer は「VM のみ・LLVM 未対応」と差分表に明記済み(上記 VM/LLVM 差分分類テーブル参照)
**P2 で扱わない項目**:
- **Block scope drop conformance** → 別タスク(未整合の可能性あり)
- **Hidden root investigation** → Phase 285 P2.1 で root 保持箇所を棚卸し
- Finalizer (`fini()`) の統一テスト → 両バックエンド未実装のため Phase 286+ で検討
- GC cycle collection → Reference Count のみで既知の制約
### P2.1investigation- 提案
**目的**: Hidden root 問題の原因特定(`x = null` で明示 drop しても weak_to_strong が成功する理由)
**調査対象**:
- VM の strong ref 保持箇所VMValue/registry/handles/scope_tracker/等)
- Arc drop タイミング(明示 `x = null` 代入時に Arc が drop されるか)
- 隠れた root 候補MIR interpreter state/local variables/等)
**期待成果**:
- Hidden root の特定(どこが strong ref を保持しているか)
- 分類: (B) 未実装 / (C) 既知バグ / (D) 仕様外
- 修正方針の提示Phase 286+ で修正)
## Non-goals
- GC アルゴリズム刷新RC→tracing 等の設計変更)