Files
hakorune/docs/development/roadmap/phases/phase-25.1l/README.md
nyash-codex 39f5256c18 📊 Phase 25.1l: Region観測レイヤー骨格 + スコープ契約設計理解
**Region Box統一理論の実装開始**

新規追加:
- src/mir/region/mod.rs: Region/RefSlotKind型定義
- src/mir/region/observer.rs: Region観測レイヤー
- docs/development/roadmap/phases/phase-25.1l/: 設計ドキュメント

主要概念:
- Region Box = Function/Loop/If の統一箱
- RefSlotKind = GC管理用スロット種別(Strong/Weak/Borrowed/NonRef)
- 観測専用(NYASH_REGION_TRACE=1で動作、挙動変更なし)

設計理解の深化:
- ValueId(40)問題 = LoopForm v2スコープ契約違反の症状
- 根本解決 = Region観測で無名一時値のスコープまたぎを検出
- 箱理論3原則: 境界明確化/差し替え可能/段階的移行

関連議論:
- ChatGPT提案: Region統一理論でGC/寿命管理の基盤構築
- SlotRegistry: 変数の単一真実源(SSOT)
- 階層構造: FunctionRegion → LoopRegion → IfRegion

次のステップ:
- Phase 1: Region観測(現在)- 非破壊的追加
- Phase 2: メタデータ出力(MIR JSON拡張)
- Phase 3: GC統合(retain/release挿入)

テスト追加:
- lang/src/compiler/tests/stageb_mini_driver.hako
- tools/test_loopssa_breakfinder_slot.sh

Build:  全警告は既存のもの
Tests: 既存テスト全て緑維持
2025-11-19 02:44:40 +09:00

7.8 KiB
Raw Blame History

Phase 25.1l — Region/GC 観測レイヤーLoopForm v2 × RefSlotKind

Status: in progressRust 側観測レイヤーの最小実装まで完了/挙動変更なし)

ゴール

  • 既に導入済みの LoopForm v2 / ControlForm を「Region Box寿命管理の箱」として扱い、
    Rust MIR 側に スコープと参照種別RefKindの観測レイヤーを追加するフェーズだよ。
  • このフェーズではあくまで:
    • RefSlotKind / Region / SlotMetadata といった 型と観測器Observer を定義し、
    • NYASH_REGION_TRACE=1 のときだけ Region 単位の live スロットと RefKind をログ出力する。
  • 挙動実行結果・SSA・PHIは一切変えないことが前提。GC の retain/release 挿入や .hako 側のロジック変更は後続フェーズ25.1m 以降)の仕事にする。

背景と位置づけ

  • これまでの 25.1d〜25.1k で:
    • LoopForm v2 / ControlForm / Conservative PHI Box によって、If/Loop の SSA/PHI は Rust 側で安定。
    • StageB/LoopSSA 由来の SSA 問題の多くは .hako 側(特に StageBBodyExtractorBox.build_body_src/2)の
      「巨大なループif 内での一時値の扱い」に起因することが分かってきた。
  • いっぽう GC 観点では:
    • 変数スロットごとに「StrongRoot / WeakRoot / Borrowed / NonRef」を区別し、
    • Region= LoopForm/IfForm境界を GC スコープ境界として扱う設計が箱理論的にきれいにハマる。
  • 25.1l はそのための 観測フェーズ であり:
    • LoopForm v2 = Region Box という概念を Rust 型として明示し、
    • StageB など複雑な関数で「どの Region でどのスロットが生きているか」をログで確認できるようにする。

スコープ25.1l でやること)

LA: RefSlotKind / SlotMetadata / Region 型の導入(実装済み)

  • 新規モジュール: src/mir/region/mod.rs
  • 型設計:
    • RefSlotKind:
      • StrongRoot — GC root 候補となる強参照スロット。
      • WeakRoot — 弱参照GC root としては数えない)。
      • Borrowed — 借用SSA 寿命のみ管理、GC root ではない)。
      • NonRef — プリミティブなど、GC 対象外の値。
    • SlotMetadata:
      • name: String — 変数スロット名(i, body_src, args など)。
      • ref_kind: RefSlotKind — 上記の種別。
    • Region:
      • id: RegionIdu32 ラッパ)。
      • kind: RegionKindLoop / If25.1l 時点では Function は未導入)。
      • parent: Option<RegionId> / children: Vec<RegionId> — 制御構造の親子関係将来用。25.1l では未設定)。
      • entry_block: BasicBlockId / exit_blocks: Vec<BasicBlockId> — ControlForm から引き継ぐ。
      • slots: Vec<SlotMetadata> — その Region で live とみなすスロット一覧(観測用)。
  • RefKind 判定は 25.1l では 簡易ヒューリスティック に留める:
    • MirType::Box(_) / Array(_) / Future(_)StrongRoot
    • 明らかなプリミティブ(整数/bool/文字列)→ NonRef
    • Weak 系/借用系の精密な分類は後続フェーズで詰める。

LB: RegionObserver の実装と ControlForm からの接続(実装済み)

  • 新規モジュール: src/mir/region/observer.rs
  • 現実装の責務:
    • observe_control_form(builder: &MirBuilder, form: &ControlForm) という関数型 API で:
      • ControlForm から entry/exits/Loop/If の形を読む。
      • 該当スコープ時点の builder.variable_map に載っている「名前付きスロット」を走査して SlotMetadata を構築。
      • Region を作成し、NYASH_REGION_TRACE=1 のときだけ:
        • [region/observe] fn=StageBBodyExtractorBox.build_body_src/2 id=RegionId(..) kind=Loop entry=bb.. exits=[..] slots=[..] という形でログ出力する(メモリには保持しない)。
  • Hook の置き場所(いずれも読み取り専用):
    • src/mir/loop_builder.rs:
      • ループ構築完了後、LoopShapeControlForm::from_loop 生成直後に observe_control_form(self.parent_builder, &form) を呼ぶ。
    • src/mir/loop_builder.rsif 降下部):
      • IfShapeControlForm::from_if 生成直後に observe_control_form(self.parent_builder, &form) を呼ぶ。
    • .hako / GC / SSA ロジックには一切影響しない。
  • dev フィルタ:
    • 現時点では StageB 周辺の観測に絞るため、func_name.contains("StageB") の関数のみログ対象にしている(ログ爆発防止)。

LC: 関数スコープ Slot 管理箱SlotRegistryとの関係設計メモ

  • 将来像:
    • 各関数ごとに「SlotRegistry仮称」箱を 1 つだけ持ち、
      • slot_id -> { name, MirType, RefSlotKind }
      • name -> slot_id を管理する SSOT として扱う。
    • RegionBox本 README での Regionは、この SlotRegistry 上で 「どの SlotId がこの Region で live か」を指すだけにする。
  • 25.1l の暫定実装:
    • まだ明示的な SlotRegistry 型は導入せず、
      • MirBuilder.variable_map.keys() を「その地点で live なスロット一覧」とみなす。
      • value_types から MirType を拾って RefSlotKind を判定する。
    • これは SlotRegistry 導入までのリーズナブルな暫定策として位置付け、 将来は variable_map→SlotRegistry/SlotId ベースに移行する。

LD: Region メタデータの足場(将来の JSON 拡張のための入口だけ)

  • 25.1l では まだ MIR JSON への出力は行わないが、将来のために:
    • Region::to_json() 相当のメソッドを #[cfg(feature = "region-meta")] 等のガード付きで用意しておく。
    • MirCompiler 側に「RegionObserver を渡しておけば、あとでメタデータを JSON に差し込める」拡張ポイントだけ作っておく。
  • GC 統合フェーズ25.1m 以降)では、このメタデータを:
    • Program(JSON v0) → MIR(JSON) 変換結果の横に "regions":[...] として添付する形を想定している。

このフェーズで「やらない」こと

  • GC の retain/release 挿入:
    • Region 情報を使って retain(slot) / release(slot) 命令を実際に MIR に埋め込むのは、25.1m 以降のタスクとし、このフェーズでは一切行わない。
  • LoopForm v2 / Conservative PHI / ControlForm の設計変更:
    • 25.1l はあくまで「読み取り専用の観測レイヤー」を追加するだけで、既存の SSA/PHI 実装には手を入れない。
  • .hako 側の GC 実装や StageB 本体の大規模リファクタ:
    • StageB/LoopSSA の箱分解(StageBBodyExtractorBox のサブ箱化)や、.hako 側 GC API の導入は、Region 観測結果を見ながら次フェーズで設計する。
  • 関数スコープ SlotRegistry の本実装:
    • 現段階では variable_map ベースの暫定観測に留め、SlotId ベースの SSOT 化は次のフェーズRegion 木 + FunctionRegion 導入時)に回す。

受け入れ条件25.1l

  • NYASH_REGION_TRACE=0(既定)のとき:
    • すべての既存テストLoopForm v2 / Stage1 resolver / StageB Rust テスト)が挙動一切変化せず緑のまま。
  • NYASH_REGION_TRACE=1 のとき:
    • 代表関数(特に StageBBodyExtractorBox.build_body_src/2)に対して Region/Slot のログが出力される。
    • ログは「Region id / kind(If/Loop) / entry/exit blocks / slots + RefKind」を含み、
      今後の GC/寿命設計の議論に耐えうる解像度になっている。
  • 変更差分は Rust 側に限定され、.hako 側コードや StageB 本体には影響を与えない。*** End Patch***"/>