feat(mir): Phase 25.1f完了 - Conservative PHI + ControlForm観測レイヤー
🎉 Conservative PHI Box理論による完全SSA構築 **Phase 7-B: Conservative PHI実装** - 片方branchのみ定義変数に対応(emit_void使用) - 全変数にPHI生成(Conservative Box理論) - Stage-1 resolver全テスト緑化(3/3 PASS) **Phase 25.1f: ControlForm観測レイヤー** - LoopShape/IfShape/ControlForm構造定義 - Loop/If統一インターフェース実装 - debug_dump/debug_validate機能追加 - NYASH_CONTROL_FORM_TRACE環境変数対応 **主な変更**: - src/mir/builder/phi.rs: Conservative PHI実装 - src/mir/control_form.rs: ControlForm構造(NEW) - src/mir/loop_builder.rs: LoopForm v2デフォルト化 **テスト結果**: ✅ mir_stage1_using_resolver_min_fragment_verifies ✅ mir_stage1_using_resolver_full_collect_entries_verifies ✅ mir_parserbox_parse_program2_harness_parses_minimal_source 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <chatgpt@openai.com>
This commit is contained in:
@ -1,5 +1,34 @@
|
||||
# Current Task — Phase 21.8 / 25 / 25.1 / 25.2(Numeric Core & Stage0/Stage1 Bootstrap)
|
||||
|
||||
Update (2025-11-18 — Phase 25.1g: Conservative PHI ↔ ControlForm 統合の準備)
|
||||
- Context:
|
||||
- 25.1f までに Rust 側の ControlForm(LoopShape / IfShape + ControlForm)と .hako 側の ControlFormBox を定義し、観測レイヤーは安定した。
|
||||
- 次のステップとして、Conservative PHI Box(If/Loop の SSA/PHI ロジック)に ControlForm ベースの入口を用意するフェーズ(25.1g)に入る。
|
||||
- Plan (25.1g):
|
||||
- If 側:
|
||||
- `phi_core::if_phi` に `merge_modified_with_control` のような薄いラッパを追加し、将来的に ControlForm から PHI マージが呼べるようにする(初回は既存関数への委譲だけ)。
|
||||
- `LoopBuilder::lower_if_in_loop` からの呼び出し位置をコメントで固定し、切り替えポイントを明示。
|
||||
- Loop 側:
|
||||
- `phi_core::loopform_builder` に LoopForm v2 Exit PHI 用の ControlForm ラッパを追加(同じく最初は既存 `build_exit_phis` に委譲するだけ)。
|
||||
- Docs:
|
||||
- `docs/development/roadmap/phases/phase-25.1g/README.md` に Option B の範囲とタスク粒度(G‑1〜G‑3)を記述済み。
|
||||
- Guard:
|
||||
- 各ステップ後に `mir_stage1_using_resolver_*` / `mir_loopform_exit_phi` / `mir_stageb_loop_break_continue` / `tools/test_stageb_min.sh` を流し、SSA/PHI の赤ログが増えていないことを確認しながら進める。
|
||||
|
||||
Update (2025-11-18 — Phase 25.1f: ControlForm / Stage‑B ハーネス準備の設計開始)
|
||||
- Context:
|
||||
- Phase 25.1d/e で Rust MIR 側の SSA/PHI(特に LoopForm v2 + Conservative PHI)と Stage‑1 UsingResolver/Program v0 PHI スモークは一通り緑になった。
|
||||
- 次の大きな山は Stage‑B 最小ハーネス(`test_stageb_min.sh`)と selfhost 側 LoopSSA だが、その前に「Loop/If を共通ビューで眺める ControlForm 層」を挟んでおきたい。
|
||||
- Done (this step):
|
||||
- 新フェーズ `Phase 25.1f` を切り、`docs/development/roadmap/phases/phase-25.1f/README.md` を追加。
|
||||
- ControlForm(LoopShape / IfShape + ControlKind)と、その役割(LoopForm v2 と If の構造を SSOT としてまとめる観測レイヤ)を文章で定義。
|
||||
- このフェーズでは挙動を変えず、「構造だけを先に固定する」方針を明記。
|
||||
- Next:
|
||||
- `src/mir/control_form.rs` に ControlForm / LoopShape / IfShape のスケルトンを追加(Conservative PHI にはまだ接続しない)。
|
||||
- Stage‑1 / Stage‑B 代表テスト(`mir_stage1_using_resolver_*` / `mir_loopform_exit_phi.rs` / `mir_stageb_loop_break_continue.rs`)から
|
||||
dev フラグ(例: `NYASH_CONTROL_FORM_TRACE=1`)付きで ControlForm を生成し、CFG 形をトレースできるようにする。
|
||||
- Stage‑B ハーネスや .hako LoopSSA の本格実装は、ControlForm の設計が固まってから次フェーズで扱う。
|
||||
|
||||
Update (2025-11-17 PM — Phase 25.1d: CalleeBoxKind構造ガード実装完了)
|
||||
- ✅ **静的Box/ランタイムBox混線問題を構造レベルで解決!**
|
||||
- **実装内容**:
|
||||
|
||||
@ -47,6 +47,8 @@ llvm-inkwell-legacy = ["dep:inkwell"]
|
||||
llvm = ["llvm-harness"]
|
||||
# Legacy AST interpreter (off by default). Gates NyashInterpreter usage in runner/tests.
|
||||
interpreter-legacy = []
|
||||
# Cranelift JIT cfg guard (ARCHIVED): keep feature empty to silence check-cfg warnings.
|
||||
cranelift-jit = []
|
||||
# (removed) Optional modular MIR builder feature
|
||||
# cranelift-jit = [ # ARCHIVED: Moved to archive/jit-cranelift/ during Phase 15
|
||||
# "dep:cranelift-codegen",
|
||||
|
||||
252
docs/development/roadmap/phases/phase-25.1f/README.md
Normal file
252
docs/development/roadmap/phases/phase-25.1f/README.md
Normal file
@ -0,0 +1,252 @@
|
||||
# Phase 25.1f — ControlForm(Loop/If 共通ビュー)& Stage‑B ハーネス準備
|
||||
|
||||
Status: planning(設計+観測レイヤ追加/挙動は変えない)
|
||||
|
||||
## ゴール
|
||||
|
||||
- LoopForm v2(ループ)と If まわり(`phi_core::if_phi`)に対して、**制御構造の共通ビュー `ControlForm`** を用意する。
|
||||
- すでに導入済みの Conservative PHI Box(If/Loop 用 SSA/PHI ロジック)を、
|
||||
- ループ専用/If 専用でバラバラに持つのではなく、
|
||||
- `ControlForm` を単一の SSOT(Single Source of Truth)として参照する方向に寄せる。
|
||||
- Stage‑B 最小ハーネスや Stage‑1 Resolver など、「複雑な if + loop」を含む経路で、
|
||||
- ループと条件分岐の形を **構造として観測・検証できる足場** を整える(いきなり本番導線は切り替えない)。
|
||||
|
||||
※ このフェーズでは Rust の挙動は変えず、「制御構造の箱(LoopShape / IfShape)+ ControlForm の定義」と、
|
||||
デバッグ/テスト用の観測導線(トレース)までに留める。
|
||||
|
||||
## 背景(25.1d / 25.1e までの位置づけ)
|
||||
|
||||
- 25.1d:
|
||||
- Rust MIR ビルダーの SSA/PHI バグ(ValueId 二重定義/non‑dominating use/undefined Value)を、
|
||||
小さな Rust テスト(`mir_*_verifies`)で炙り出して修正済み。
|
||||
- CalleeBoxKind / BoxCompilationContext によって Stage‑B / Stage‑1 の静的 Box とランタイム Box の混線も構造的に解消。
|
||||
- 25.1e:
|
||||
- ループの PHI 生成の SSOT を LoopForm v2 + `phi_core` に寄せ、
|
||||
- Exit PHI、break/continue、pinned/carrier/invariant 変数を整理。
|
||||
- Conservative PHI Box(If/Loop 両方で「安全側に倒す PHI 生成」を行い、不要な PHI は将来の削除で最適化する方針)を実装。
|
||||
- Stage‑1 UsingResolver 系テストと Program v0 PHI スモーク、Stage‑B 風ループテストはすべて緑。
|
||||
|
||||
ここまでで「LoopForm v2 + Conservative PHI」の根本バグはだいたい取り切れたが、
|
||||
|
||||
- PHI ロジックが If 用と Loop 用で散在している。
|
||||
- Stage‑B ハーネス側で「どの loop/if がどう組み合わさっているか」を横断的に眺める手段がまだ弱い。
|
||||
|
||||
→ 25.1f では、**Loop / If を一段上から包む ControlForm レイヤ**を定義し、Stage‑B ハーネスに進む前準備として構造を固める。
|
||||
|
||||
## 方針 — ControlForm / Shape の設計
|
||||
|
||||
### 型と場所
|
||||
|
||||
- 新規モジュール案:
|
||||
- `src/mir/control_form.rs`
|
||||
- 基本構造:
|
||||
- `LoopShape`:
|
||||
- `preheader: BasicBlockId`
|
||||
- `header: BasicBlockId`
|
||||
- `body: BasicBlockId`(代表 body ブロック)
|
||||
- `latch: BasicBlockId`
|
||||
- `exit: BasicBlockId`
|
||||
- `continue_targets: Vec<BasicBlockId>`
|
||||
- `break_targets: Vec<BasicBlockId>`
|
||||
- `IfShape`:
|
||||
- `cond_block: BasicBlockId`
|
||||
- `then_block: BasicBlockId`
|
||||
- `else_block: Option<BasicBlockId>`
|
||||
- `merge_block: BasicBlockId`
|
||||
- `ControlKind`:
|
||||
- `Loop(LoopShape)` / `If(IfShape)`(将来 `Switch` / `Match` も追加可能な余地だけ確保)
|
||||
- `ControlForm`:
|
||||
- `entry: BasicBlockId`(構造全体の入口)
|
||||
- `exits: Vec<BasicBlockId>`(構造を抜けた先のブロック群)
|
||||
- `kind: ControlKind`
|
||||
|
||||
### 生成パス(from_loop / from_if)
|
||||
|
||||
- `LoopForm` → `ControlForm`:
|
||||
- 既存の LoopForm v2(`LoopFormBuilder` が返す構造)から `LoopShape` を構築し、
|
||||
- `ControlForm::from_loop(loop_form: &LoopForm)` で共通ビューに変換する。
|
||||
- `entry = preheader`, `exits = [exit]` というルールで統一。
|
||||
- If 用:
|
||||
- 既存の `phi_core::if_phi` / builder 側の if lowering から、必要な情報をまとめた薄い `IfForm`(もしくは `IfShape` 直構築)を定義。
|
||||
- `ControlForm::from_if(if_form: &IfForm)` で共通ビューに変換。
|
||||
- `entry = cond_block`, `exits = [merge_block]` を基本とし、将来 `else if` や switch なども扱える形で設計。
|
||||
|
||||
### Invariant / Debug 用の Trait
|
||||
|
||||
- 25.1f では「挙動を変えない」ため、ControlForm 自体は **観測・検証専用** として設計する:
|
||||
- `debug_dump(&self)`:
|
||||
- Loop / If の各ブロック ID を一括でログ出力し、Stage‑1 / Stage‑B テストから CFG の形を視覚的に確認できるようにする。
|
||||
- `CfgLike` トレイト(任意):
|
||||
- `fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool;`
|
||||
- `fn predecessors_len(&self, block: BasicBlockId) -> usize;`
|
||||
- `LoopShape::debug_validate<C: CfgLike>(&self, cfg: &C)` などを `#[cfg(debug_assertions)]` で用意し、
|
||||
Loop/If の形が想定どおりかを assert できるようにする。
|
||||
|
||||
## タスク粒度(25.1f TODO)
|
||||
|
||||
### F‑1: ControlForm / Shape の設計ドキュメント
|
||||
|
||||
- 本 README で:
|
||||
- `LoopShape` / `IfShape` / `ControlKind` / `ControlForm` の責務とフィールドを明文化。
|
||||
- LoopForm v2 との対応関係(preheader/header/latch/exit/continue/break)を図解レベルで通るところまで整理。
|
||||
- 25.1e の LoopForm 仕様(Carrier/Pinned/Invariant / break/continue / exit PHI)と整合していることを確認。
|
||||
|
||||
### F‑2: Rust 側のスケルトン実装(導線のみ)
|
||||
|
||||
- `src/mir/control_form.rs` を追加し、以下のみ実装:
|
||||
- 型定義(`LoopShape` / `IfShape` / `ControlKind` / `ControlForm`)。
|
||||
- `ControlForm::from_loop(&LoopForm)` / `ControlForm::from_if(&IfForm)` の雛形。
|
||||
- `debug_dump()` と `debug_validate()`(`#[cfg(debug_assertions)]` 前提、もしくはテスト専用)。
|
||||
- 既存のビルダー/PHI ロジックは **まだ ControlForm を使わない**:
|
||||
- 25.1e で安定した Conservative PHI 実装を壊さないため、Phase 25.1f では「観測オンリー」に留める。
|
||||
|
||||
### F‑3: Stage‑1 / Stage‑B テストへの観測フック
|
||||
|
||||
- 対象テスト:
|
||||
- `src/tests/mir_stage1_using_resolver_verify.rs` 系(Stage‑1 UsingResolver の min/full)。
|
||||
- Stage‑B 風ループ/Program v0 PHI スモーク(`mir_loopform_exit_phi.rs` / `mir_stageb_loop_break_continue.rs` / `tools/smokes/v2/...`)。
|
||||
- 方針:
|
||||
- `NYASH_CONTROL_FORM_TRACE=1` などの dev フラグが立っているときだけ、
|
||||
- LoopForm v2 から `ControlForm` を生成し、
|
||||
- `debug_dump()` で構造をトレースログに出す。
|
||||
- これにより、Stage‑B ハーネスを組む前に「実際にどんな Loop/If 形が発生しているか」を CFG レベルで確認できる。
|
||||
|
||||
### F‑4: Conservative PHI との接続計画(設計のみ)
|
||||
|
||||
- 25.1e で実装した Conservative PHI Box(If/Loop)のロジックを整理し、将来像を設計として固定する:
|
||||
- 目標: `merge_modified_at_merge_with` / LoopForm v2 Exit PHI 生成などを、
|
||||
- `ControlForm` ベースの API で呼べるようにする(例: `build_phi_for_control(form: &ControlForm, ...)`)。
|
||||
- 25.1f では:
|
||||
- どの関数をどこまで ControlForm 受けにするか、
|
||||
- 既存 API との互換性をどう保つか、
|
||||
を README 上で設計するところまで。
|
||||
- 実際の実装切り替え(PHI ロジックを ControlForm ベースに差し替える)は、Stage‑B ハーネスと合わせて **別フェーズ(25.1g または 25.2 以降)** に送る。
|
||||
|
||||
### F‑5: Stage‑B ハーネスへの橋渡し準備
|
||||
|
||||
- Stage‑B 最小ハーネス(`tools/test_stageb_min.sh` / `lang/src/compiler/tests/stageb_min_sample.hako`)における:
|
||||
- ループパターン(`skip_ws` などの単純ループ、複数 break/continue を含むループ)。
|
||||
- if + loop のネスト構造。
|
||||
を洗い出し、LoopForm v2 + IfShape + ControlForm ですべて表現できることを確認する。
|
||||
- 将来のタスク(このフェーズでは設計のみ):
|
||||
- Stage‑B ハーネスの SSA/PHI 検証を `ControlForm` 経由で行う Rust テストをどう設計するかをまとめる。
|
||||
- Stage‑B 側の .hako LoopSSA(`lang/src/compiler/builder/ssa/loopssa.hako`)が、LoopForm v2 / ControlForm の設計と齟齬を起こさないように、必要な制約・前提条件を明記する。
|
||||
|
||||
### F‑6: .hako 側 ControlFormBox(Layer 2 の箱)定義
|
||||
|
||||
- 目的:
|
||||
- Rust 側 `LoopShape` / `IfShape` / `ControlForm` に対応する **Hakorune 実装版の箱** を用意し、
|
||||
Stage‑B / LoopSSA が Rust と同じモデルでループ/if を扱えるようにする。
|
||||
- 将来の LoopSSA 実装は、この箱経由で構造を受け取ることを前提に設計する。
|
||||
- 現行構文版(Layer 2):
|
||||
- ファイル案: `lang/src/shared/mir/control_form_box.hako`
|
||||
- 定義案(BlockId は現状 i64/IntegerBox で表現):
|
||||
```hako
|
||||
static box ControlFormBox {
|
||||
kind_name: StringBox // "loop" or "if"
|
||||
entry: IntegerBox // BasicBlockId 相当
|
||||
exits: ArrayBox // of IntegerBox (BlockId)
|
||||
|
||||
// Loop fields (kind_name == "loop" の時のみ有効)
|
||||
loop_preheader: IntegerBox
|
||||
loop_header: IntegerBox
|
||||
loop_body: IntegerBox
|
||||
loop_latch: IntegerBox
|
||||
loop_exit: IntegerBox
|
||||
|
||||
// If fields (kind_name == "if" の時のみ有効)
|
||||
if_cond: IntegerBox
|
||||
if_then: IntegerBox
|
||||
if_else: IntegerBox
|
||||
if_merge: IntegerBox
|
||||
|
||||
birth(kind) {
|
||||
me.kind_name = kind
|
||||
me.exits = new ArrayBox()
|
||||
}
|
||||
|
||||
is_loop() {
|
||||
return me.kind_name == "loop"
|
||||
}
|
||||
|
||||
is_if() {
|
||||
return me.kind_name == "if"
|
||||
}
|
||||
}
|
||||
```
|
||||
- 対応表(Rust ↔ .hako):
|
||||
- Rust `LoopShape.preheader/header/body/latch/exit`
|
||||
↔ Hako `loop_preheader/loop_header/loop_body/loop_latch/loop_exit`
|
||||
- Rust `IfShape.cond_block/then_block/else_block/merge_block`
|
||||
↔ Hako `if_cond/if_then/if_else/if_merge`
|
||||
- Rust `ControlForm.entry/exits`
|
||||
↔ Hako `entry` / `exits`(`exits` は BlockId の配列)
|
||||
- 将来構文版(Layer 3 の理想形・variant 導入前提):
|
||||
- 将来の Nyash variant 構文が入ったら、`ControlKind` を enum 的に表現し、
|
||||
```hako
|
||||
box ControlKind {
|
||||
variant Loop { preheader: BlockIdBox, header: BlockIdBox, ... }
|
||||
variant If { cond_block: BlockIdBox, then_block: BlockIdBox, ... }
|
||||
}
|
||||
|
||||
static box ControlFormBox {
|
||||
kind: ControlKind
|
||||
entry: BlockIdBox
|
||||
exits: ArrayBox
|
||||
}
|
||||
```
|
||||
のような形に寄せる(現段階では設計メモのみ)。
|
||||
- 25.1f でのスコープ:
|
||||
- `control_form_box.hako` に上記 Layer 2 版 `ControlFormBox` を実装する。
|
||||
- ただし Stage‑B / LoopSSA からはまだ使用しない(コンパイルが通る最小限の実装+将来用の箱)。
|
||||
- Stage‑B LoopSSA 統合(ControlFormBox を実際に使って JSON v0→MIR を整理する)は、次フェーズに回す。
|
||||
|
||||
## 移行ステップ(Option A → B → C のロードマップ)
|
||||
|
||||
このフェーズ(25.1f)では、あくまで **Option A = 観測レイヤーの完成** までをスコープにするよ。その上で、将来の B/C も含めて合意しておきたい移行順序はこうだよ:
|
||||
|
||||
1. **Option A: 観測レイヤーを完璧にする(Phase 25.1f)**
|
||||
- Rust:
|
||||
- `LoopShape` / `IfShape` / `ControlForm` 実装済み。
|
||||
- `LoopBuilder` から LoopForm v2 / `lower_if_in_loop` の出口で `ControlForm::Loop/If` を生成して `debug_dump` 済み。
|
||||
- `NYASH_CONTROL_FORM_TRACE` は「未設定=ON, 0/false=OFF」のヘルパーに統一済み。
|
||||
- .hako:
|
||||
- `ControlFormBox`(kind_name + loop_* / if_* + entry/exits)の箱だけ定義(まだ未使用)。
|
||||
- テスト/スモーク:
|
||||
- `mir_stage1_using_resolver_*` / `mir_loopform_exit_phi.rs` / `mir_stageb_loop_break_continue.rs` / `tools/test_stageb_min.sh` を回しつつ、
|
||||
ControlForm トレースが panic せずに構造ログだけを出すことを確認する。
|
||||
|
||||
2. **Option B: Conservative PHI ↔ ControlForm の統合(別フェーズ: 25.1g 想定)**
|
||||
- ねらい:
|
||||
- いま `merge_modified_at_merge_with`(If)や LoopForm v2 Exit PHI が直接 BlockId/ValueId を持っている部分を、
|
||||
`ControlForm` 経由で読めるように段階的に寄せていく。
|
||||
- 方針:
|
||||
- まず If から(`ControlKind::If`)→ ループ Exit PHI(`ControlKind::Loop`)の順に小さく進める。
|
||||
- 各ステップごとに:
|
||||
- 対応するテスト(Stage‑1 / Program v0 / Stage‑B 風)+ `test_stageb_min.sh` を回し、
|
||||
SSA/PHI の赤ログが増えていないことを確認しながら移行する。
|
||||
- 注意:
|
||||
- これは 25.1f のスコープ外。Phase 25.1g(仮)として小さな差分に分割しながら実装する。
|
||||
|
||||
3. **Option C: LoopBuilder/If 降下の ControlForm ベース正規化(さらに後ろのフェーズ)**
|
||||
- ねらい:
|
||||
- 最終的には「LoopBuilder/If 降下がまず ControlForm を組み立てる」スタイルに寄せ、
|
||||
Conservative PHI / LoopSSA / .hako 側 LoopSSA が同じ ControlForm を揃って見る状態にする。
|
||||
- 規模:
|
||||
- 数百行単位のリファクタになるため、Phase 25.2 以降(Selfhost/Stage‑B の安定を見ながら)に分割してやる。
|
||||
- ガード:
|
||||
- 各ステップで代表スモーク(Stage‑1 / Stage‑B / Program v0 / `test_stageb_min.sh`)を流しながら進める。
|
||||
- `NYASH_CONTROL_FORM_TRACE` と `.hako ControlFormBox` を使って、常に「形」が崩れていないかを観測できるようにしておく。
|
||||
|
||||
このフェーズ(25.1f)は「Option A をやり切って足場を固める」ことに専念し、その上で 25.1g 以降で Option B/C に進む、というロードマップで進めるよ。
|
||||
|
||||
## このフェーズで「やらないこと」
|
||||
|
||||
- if を loop に書き換えるような、意味論レベルの正規化は **行わない**:
|
||||
- If は IfShape、Loop は LoopShape として、それぞれの形を尊重する。
|
||||
- Stage‑B ハーネス本体の導入・既存パイプラインの切り替え:
|
||||
- `test_stageb_min.sh` の経路変更や `.hako` コンパイラ側の LoopSSA 実装変更は、25.1f では触らず、
|
||||
ControlForm の設計と観測レイヤ整備が完了してから別フェーズで扱う。
|
||||
- Conservative PHI ロジックの大規模リライト:
|
||||
- 25.1e で安定させた If/Loop PHI 実装を、いきなり ControlForm ベースに書き換えることはしない。
|
||||
- まずは「どう書き換えるのが構造的に美しいか」を、このフェーズの README に設計として落とす。
|
||||
118
docs/development/roadmap/phases/phase-25.1g/README.md
Normal file
118
docs/development/roadmap/phases/phase-25.1g/README.md
Normal file
@ -0,0 +1,118 @@
|
||||
# Phase 25.1g — Conservative PHI ↔ ControlForm 統合(設計+小さな導線)
|
||||
|
||||
Status: planning(設計・導線追加/既存挙動は変えない)
|
||||
|
||||
## ゴール
|
||||
|
||||
- 25.1f で整えた **ControlForm 観測レイヤー**(LoopShape / IfShape + ControlForm)を、
|
||||
Conservative PHI Box(If/Loop 用 SSA/PHI ロジック)の入口として使えるようにする。
|
||||
- いきなりすべてを書き換えるのではなく、
|
||||
1. If 用 Conservative PHI → ControlForm 対応
|
||||
2. LoopForm v2 Exit PHI → ControlForm 対応
|
||||
の順に、小さく段階的に寄せる。
|
||||
- 各ステップごとに Rust テストと `tools/test_stageb_min.sh` を流しつつ、
|
||||
SSA/PHI の赤ログが増えていないことを確認して進む。
|
||||
|
||||
## 前提(25.1f までで揃ったもの)
|
||||
|
||||
- Rust:
|
||||
- `src/mir/control_form.rs`:
|
||||
- `LoopShape` / `IfShape` / `ControlKind` / `ControlForm` / `CfgLike` / `is_control_form_trace_on()` が定義済み。
|
||||
- `src/mir/loop_builder.rs`:
|
||||
- LoopForm v2 経路(`build_loop_with_loopform`)の出口で `LoopShape` → `ControlForm::Loop` を生成・トレース。
|
||||
- `lower_if_in_loop` の merge 部分で `IfShape` → `ControlForm::If` を生成・トレース。
|
||||
- `NYASH_CONTROL_FORM_TRACE`(未設定=ON, 0/false=OFF)でトレース ON/OFF 切り替え可能。
|
||||
- Conservative PHI:
|
||||
- If 用: `src/mir/builder/phi.rs` / `src/mir/phi_core/if_phi.rs`(Conservative PHI Box 実装)。
|
||||
- Loop 用: `src/mir/phi_core/loopform_builder.rs`(LoopForm v2 の Exit PHI / Carrier/Pinned 対応)。
|
||||
- .hako:
|
||||
- `lang/src/shared/mir/control_form_box.hako`:
|
||||
- `static box ControlFormBox`(kind_name + loop_* / if_* + entry/exits)だけ実装済み(まだ未使用)。
|
||||
|
||||
## 方針(Option B の範囲)
|
||||
|
||||
- **このフェーズでは**:
|
||||
- Conservative PHI の「インターフェースと呼び出し位置」に ControlForm の導線を用意する。
|
||||
- ただし既存のロジック(引数で BlockId を受け取る形)は残し、ControlForm 導線は **観測+補助的な入口** として扱う。
|
||||
- 影響範囲は If / Loop の PHI 部分に限定し、他の MIR 降下には触れない。
|
||||
- 実装方針:
|
||||
- 新しい API を「足す」→ 既存コードから段階的に使い始める、という形で進める。
|
||||
- 例:
|
||||
- If 用: `merge_modified_at_merge_with_control(form: &ControlForm, ...)` を追加。
|
||||
- Loop 用: `build_exit_phis_for_control(form: &ControlForm, ...)` を追加。
|
||||
- 最初のステップでは「ControlForm から必要な BlockId を取り出して、既存の関数に委譲するだけ」の薄いラッパにする。
|
||||
|
||||
## タスク粒度
|
||||
|
||||
### G‑1: If 用 Conservative PHI への ControlForm 導線
|
||||
|
||||
- 目的:
|
||||
- `loop_builder.rs::lower_if_in_loop` から、`ControlForm::If` を PHI ロジックに渡せるようにする。
|
||||
- ステップ:
|
||||
1. `src/mir/phi_core/if_phi.rs` に薄いラッパ関数を追加:
|
||||
```rust
|
||||
pub fn merge_modified_with_control<O: PhiMergeOps>(
|
||||
ops: &mut O,
|
||||
form: &crate::mir::control_form::ControlForm,
|
||||
pre_if_snapshot: &HashMap<String, ValueId>,
|
||||
then_map_end: &HashMap<String, ValueId>,
|
||||
else_map_end_opt: &Option<HashMap<String, ValueId>>,
|
||||
skip_var: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
// ControlForm::If から cond/then/else/merge を取り出し、
|
||||
// 既存の merge_modified_at_merge_with に橋渡しするだけ(ロジックは変えない)。
|
||||
}
|
||||
```
|
||||
2. `lower_if_in_loop` 側で:
|
||||
- 既存の `merge_modified_at_merge_with` 呼び出しの直前/直後にコメントを付け、
|
||||
- 将来的に `merge_modified_with_control` に置き換えられるように位置を明示する(25.1g ではまだ呼び替えない or dev フラグで限定)。
|
||||
3. テスト:
|
||||
- `cargo test -q mir_stage1_using_resolver_min_fragment_verifies -- --nocapture`
|
||||
- `cargo test -q mir_stage1_using_resolver_full_collect_entries_verifies -- --nocapture`
|
||||
- 必要なら `NYASH_CONTROL_FORM_TRACE=1` で IfShape ログを確認し、BlockId 対応が設計どおりかを確認。
|
||||
|
||||
### G‑2: LoopForm v2 Exit PHI への ControlForm 導線
|
||||
|
||||
- 目的:
|
||||
- LoopForm v2 Exit PHI(`loopform_builder.rs::build_exit_phis`)にも ControlForm ベースの入口を用意する。
|
||||
- ステップ:
|
||||
1. `src/mir/phi_core/loopform_builder.rs` にラッパ関数追加:
|
||||
```rust
|
||||
pub fn build_exit_phis_for_control<O: LoopFormOps>(
|
||||
loopform: &LoopFormBuilder,
|
||||
ops: &mut O,
|
||||
form: &crate::mir::control_form::ControlForm,
|
||||
exit_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
|
||||
) -> Result<(), String> {
|
||||
// form.kind が Loop の場合に限り、
|
||||
// shape.preheader/header/body/latch/exit から exit_id/branch_source_block を決めて
|
||||
// 既存の build_exit_phis に委譲するだけ。
|
||||
}
|
||||
```
|
||||
2. `LoopBuilder::build_loop_with_loopform` からは:
|
||||
- 現在どおり `loopform.build_exit_phis(self, exit_id, branch_source_block, &exit_snaps)` を呼び続ける。
|
||||
- コメントで「ControlForm 経由の入口」があることを明示し、将来の切り替えポイントを固定する。
|
||||
3. テスト:
|
||||
- `cargo test -q mir_loopform_exit_phi -- --nocapture`
|
||||
- `cargo test -q mir_stageb_loop_break_continue -- --nocapture`
|
||||
- `./tools/test_stageb_min.sh` を流して、Exit PHI 誘発系の赤ログが増えていないことを確認。
|
||||
|
||||
### G‑3: ControlForm ↔ Conservative PHI の設計メモ更新
|
||||
|
||||
- 目的:
|
||||
- 25.1d/e/f/g の成果をまとめ、「If/Loop の PHI が最終的にどのレイヤで SSOT を持つか」を文書で固定する。
|
||||
- ステップ:
|
||||
- `phase-25.1d/README.md` と `phase-25.1f/README.md` をリンクしつつ、
|
||||
- Conservative PHI Box の責務(If/Loop 両方)
|
||||
- ControlForm レイヤの責務
|
||||
- .hako 側 `ControlFormBox` / `LoopSSA` の予定
|
||||
を Phase 25 全体の流れの中に整理して追記する。
|
||||
- `CURRENT_TASK.md` に 25.1g の短いサマリを追加して、今どこまで進めたかをいつでも追えるようにする。
|
||||
|
||||
## このフェーズで「しない」こと
|
||||
|
||||
- 既存の Conservative PHI 実装を一気に ControlForm 専用に書き換えること:
|
||||
- 25.1g は「導線追加+ラッパ」「設計固め」まで。
|
||||
- 実際の置き換え(Option B の本体)は、さらに小さなステップに分けて後続フェーズで行う。
|
||||
- LoopBuilder/If 降下の大規模リファクタ(Option C 相当):
|
||||
- これは Phase 25.2 以降の仕事として残しておく。
|
||||
42
lang/src/shared/mir/control_form_box.hako
Normal file
42
lang/src/shared/mir/control_form_box.hako
Normal file
@ -0,0 +1,42 @@
|
||||
// selfhost/shared/mir/control_form_box.hako
|
||||
// ControlFormBox — Loop / If 共通ビューの Hakorune 実装版(Layer 2)
|
||||
//
|
||||
// 目的:
|
||||
// - Rust 側の LoopShape / IfShape / ControlForm に対応する箱を .hako 側に用意して、
|
||||
// Stage‑B / LoopSSA からも同じモデルで制御構造を扱えるようにすることだよ。
|
||||
// - 現時点では「構造定義のみ」の箱で、コンパイルを通すための最小限の実装になっているよ。
|
||||
|
||||
static box ControlFormBox {
|
||||
// 共通フィールド
|
||||
kind_name: StringBox // "loop" or "if"
|
||||
entry: IntegerBox // BasicBlockId 相当(整数ID)
|
||||
exits: ArrayBox // of IntegerBox (BlockId)
|
||||
|
||||
// Loop 用フィールド(kind_name == "loop" の時のみ意味を持つ)
|
||||
loop_preheader: IntegerBox
|
||||
loop_header: IntegerBox
|
||||
loop_body: IntegerBox
|
||||
loop_latch: IntegerBox
|
||||
loop_exit: IntegerBox
|
||||
|
||||
// If 用フィールド(kind_name == "if" の時のみ意味を持つ)
|
||||
if_cond: IntegerBox
|
||||
if_then: IntegerBox
|
||||
if_else: IntegerBox
|
||||
if_merge: IntegerBox
|
||||
|
||||
// コンストラクタ相当: kind_name を設定し、exits 配列を初期化するよ。
|
||||
birth(kind) {
|
||||
me.kind_name = kind
|
||||
me.exits = new ArrayBox()
|
||||
}
|
||||
|
||||
is_loop() {
|
||||
return me.kind_name == "loop"
|
||||
}
|
||||
|
||||
is_if() {
|
||||
return me.kind_name == "if"
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,9 +36,6 @@ impl MirInterpreter {
|
||||
let saved_fn = self.cur_fn.clone();
|
||||
self.cur_fn = Some(func.signature.name.clone());
|
||||
|
||||
// Check if this is a static box method call
|
||||
let static_box_name = self.is_static_box_method(&func.signature.name);
|
||||
|
||||
match arg_vals {
|
||||
Some(args) => {
|
||||
// Regular parameter binding: params and args are 1:1
|
||||
|
||||
@ -278,6 +278,7 @@ impl MirInterpreter {
|
||||
}
|
||||
|
||||
// moved: try_handle_map_box → handlers/boxes_map.rs
|
||||
#[allow(dead_code)]
|
||||
fn try_handle_map_box(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
@ -289,6 +290,7 @@ impl MirInterpreter {
|
||||
}
|
||||
|
||||
// moved: try_handle_string_box → handlers/boxes_string.rs
|
||||
#[allow(dead_code)]
|
||||
fn try_handle_string_box(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
@ -300,6 +302,7 @@ impl MirInterpreter {
|
||||
}
|
||||
|
||||
// moved: try_handle_array_box → handlers/boxes_array.rs
|
||||
#[allow(dead_code)]
|
||||
fn try_handle_array_box(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
|
||||
@ -102,7 +102,7 @@ pub(super) fn try_handle_instance_box(
|
||||
} else {
|
||||
// Conservative fallback: search unique function by name tail ".method/arity"
|
||||
let tail = format!(".{}{}", method, format!("/{}", args.len()));
|
||||
let mut cands: Vec<String> = this
|
||||
let cands: Vec<String> = this
|
||||
.functions
|
||||
.keys()
|
||||
.filter(|k| k.ends_with(&tail))
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use super::*;
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
pub(super) fn try_handle_object_fields(
|
||||
this: &mut MirInterpreter,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use super::*;
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
pub(super) fn try_handle_string_box(
|
||||
this: &mut MirInterpreter,
|
||||
|
||||
@ -3,6 +3,7 @@ use serde_json::Value as JsonValue;
|
||||
|
||||
impl MirInterpreter {
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn ensure_mir_json_version_field(s: &str) -> String {
|
||||
match serde_json::from_str::<JsonValue>(s) {
|
||||
Ok(mut v) => {
|
||||
@ -28,6 +29,12 @@ impl MirInterpreter {
|
||||
let mbase = super::super::utils::normalize_arity_suffix(method);
|
||||
match (iface, mbase) {
|
||||
("env", "get") => {
|
||||
// Prefer provider-based resolution when available, fall back to process env.
|
||||
if let Some(provider_res) = self.extern_provider_dispatch("env.get", args) {
|
||||
let result = provider_res?;
|
||||
self.write_result(dst, result);
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(a0) = args.get(0) {
|
||||
let key = self.reg_load(*a0)?.to_string();
|
||||
let val = std::env::var(&key).ok();
|
||||
@ -135,14 +142,6 @@ impl MirInterpreter {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
("env", "get") => {
|
||||
// Delegate to provider
|
||||
let ret = self
|
||||
.extern_provider_dispatch("env.get", args)
|
||||
.unwrap_or(Ok(VMValue::Void))?;
|
||||
self.write_result(dst, ret);
|
||||
Ok(())
|
||||
}
|
||||
("env", "set") => {
|
||||
// Delegate to provider
|
||||
let ret = self
|
||||
|
||||
@ -110,6 +110,7 @@ impl MirInterpreter {
|
||||
|
||||
/// Check if a function name represents a static box method
|
||||
/// Format: "BoxName.method/Arity"
|
||||
#[allow(dead_code)]
|
||||
fn is_static_box_method(&self, func_name: &str) -> Option<String> {
|
||||
if let Some((box_name, _rest)) = func_name.split_once('.') {
|
||||
if self.static_box_decls.contains_key(box_name) {
|
||||
|
||||
@ -44,6 +44,7 @@ impl MirInterpreter {
|
||||
/// # Returns
|
||||
/// 引数数が範囲内の場合はOk(())、そうでない場合はエラー
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn validate_args_range(
|
||||
&self,
|
||||
method: &str,
|
||||
@ -71,6 +72,7 @@ impl MirInterpreter {
|
||||
/// # Returns
|
||||
/// 引数数が最小値以上の場合はOk(())、そうでない場合はエラー
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn validate_args_min(
|
||||
&self,
|
||||
method: &str,
|
||||
|
||||
@ -42,6 +42,7 @@ impl MirInterpreter {
|
||||
/// # Errors
|
||||
/// * 値が整数でない場合はエラー
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn load_as_int(&mut self, vid: ValueId) -> Result<i64, VMError> {
|
||||
match self.reg_load(vid)? {
|
||||
VMValue::Integer(i) => Ok(i),
|
||||
@ -71,6 +72,7 @@ impl MirInterpreter {
|
||||
/// # Errors
|
||||
/// * 値がboolでない場合はエラー
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn load_as_bool(&mut self, vid: ValueId) -> Result<bool, VMError> {
|
||||
match self.reg_load(vid)? {
|
||||
VMValue::Bool(b) => Ok(b),
|
||||
@ -112,6 +114,7 @@ impl MirInterpreter {
|
||||
/// # Returns
|
||||
/// * `Result<Vec<VMValue>, VMError>` - 読み込んだVMValueのVec
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn load_args_as_values(
|
||||
&mut self,
|
||||
vids: &[ValueId],
|
||||
|
||||
@ -13,6 +13,7 @@ impl MirInterpreter {
|
||||
/// * `dst` - 書き込み先のValueId (Noneの場合は何もしない)
|
||||
/// * `result` - 書き込むBox
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn write_box_result(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
|
||||
@ -40,6 +40,7 @@ impl ErrorBuilder {
|
||||
/// // => "get expects Integer type, got String"
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn type_mismatch(method: &str, expected: &str, actual: &str) -> VMError {
|
||||
VMError::InvalidInstruction(format!("{} expects {} type, got {}", method, expected, actual))
|
||||
}
|
||||
@ -57,6 +58,7 @@ impl ErrorBuilder {
|
||||
/// // => "get index out of bounds: 5 >= 3"
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn out_of_bounds(method: &str, index: usize, len: usize) -> VMError {
|
||||
VMError::InvalidInstruction(format!("{} index out of bounds: {} >= {}", method, index, len))
|
||||
}
|
||||
@ -93,6 +95,7 @@ impl ErrorBuilder {
|
||||
/// // => "receiver must be ArrayBox"
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn receiver_type_error(expected: &str) -> VMError {
|
||||
VMError::InvalidInstruction(format!("receiver must be {}", expected))
|
||||
}
|
||||
@ -123,6 +126,7 @@ impl ErrorBuilder {
|
||||
/// // => "link_object expects at least 1 arg, got 0"
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn arg_count_min(method: &str, min: usize, actual: usize) -> VMError {
|
||||
VMError::InvalidInstruction(format!(
|
||||
"{} expects at least {} arg{}, got {}",
|
||||
@ -153,6 +157,7 @@ impl ErrorBuilder {
|
||||
/// // => "link_object: <error message>"
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn from_error(operation: &str, error: &dyn std::error::Error) -> VMError {
|
||||
VMError::InvalidInstruction(format!("{}: {}", operation, error))
|
||||
}
|
||||
@ -178,6 +183,7 @@ impl super::super::MirInterpreter {
|
||||
/// return Err(self.err_type_mismatch("get", "Integer", actual_type));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn err_type_mismatch(&self, method: &str, expected: &str, actual: &str) -> VMError {
|
||||
ErrorBuilder::type_mismatch(method, expected, actual)
|
||||
}
|
||||
@ -189,6 +195,7 @@ impl super::super::MirInterpreter {
|
||||
/// return Err(self.err_out_of_bounds("get", idx, len));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn err_out_of_bounds(&self, method: &str, index: usize, len: usize) -> VMError {
|
||||
ErrorBuilder::out_of_bounds(method, index, len)
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ impl MirInterpreter {
|
||||
/// # Returns
|
||||
/// 変換成功時はBox、失敗時はエラー
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn convert_to_box(
|
||||
&mut self,
|
||||
receiver: ValueId,
|
||||
|
||||
@ -33,7 +33,10 @@ impl BenchmarkSuite {
|
||||
|
||||
/// Run comprehensive benchmark across all backends
|
||||
pub fn run_all(&self) -> Vec<BenchmarkResult> {
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
let mut results = Vec::new();
|
||||
#[cfg(not(feature = "wasm-backend"))]
|
||||
let results = Vec::new();
|
||||
|
||||
let benchmarks = [
|
||||
("bench_light", "benchmarks/bench_light.hako"),
|
||||
@ -45,14 +48,14 @@ impl BenchmarkSuite {
|
||||
println!("🚀 Running benchmark: {}", name);
|
||||
|
||||
// Test if file exists and is readable
|
||||
if let Ok(source) = fs::read_to_string(file_path) {
|
||||
if let Ok(_source) = fs::read_to_string(file_path) {
|
||||
// Run on all backends
|
||||
// Interpreter benchmark removed with legacy interpreter
|
||||
|
||||
// VM benchmark removed with vm-legacy
|
||||
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
if let Ok(wasm_result) = self.run_wasm_benchmark(name, &source) {
|
||||
if let Ok(wasm_result) = self.run_wasm_benchmark(name, &_source) {
|
||||
results.push(wasm_result);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -27,7 +27,6 @@ mod static_ops;
|
||||
|
||||
pub use helpers::{concat_result, can_repeat};
|
||||
pub use macros::impl_static_numeric_ops;
|
||||
use crate::operator_traits::{NyashAdd, NyashMul};
|
||||
|
||||
// Phase 2: Static implementations are now in static_ops.rs
|
||||
|
||||
@ -514,6 +513,7 @@ impl OperatorResolver {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::operator_traits::{NyashAdd, NyashMul};
|
||||
|
||||
#[test]
|
||||
fn test_integer_addition() {
|
||||
|
||||
@ -11,14 +11,14 @@ use crate::impl_static_numeric_ops;
|
||||
|
||||
// ===== Macro-generated static implementations =====
|
||||
|
||||
/// Static numeric operations for IntegerBox
|
||||
///
|
||||
/// Generates implementations for: Add, Sub, Mul, Div with zero-division error handling
|
||||
// Static numeric operations for IntegerBox
|
||||
//
|
||||
// Generates implementations for: Add, Sub, Mul, Div with zero-division error handling
|
||||
impl_static_numeric_ops!(IntegerBox, 0);
|
||||
|
||||
/// Static numeric operations for FloatBox
|
||||
///
|
||||
/// Generates implementations for: Add, Sub, Mul, Div with zero-division error handling
|
||||
// Static numeric operations for FloatBox
|
||||
//
|
||||
// Generates implementations for: Add, Sub, Mul, Div with zero-division error handling
|
||||
impl_static_numeric_ops!(FloatBox, 0.0);
|
||||
|
||||
// ===== Manual static implementations for special cases =====
|
||||
@ -57,4 +57,4 @@ impl NyashAdd<BoolBox> for BoolBox {
|
||||
}
|
||||
|
||||
// Note: Additional static implementations can be added here as needed
|
||||
// for cross-type operations or special Box types
|
||||
// for cross-type operations or special Box types
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
//! Implements modulo operations between integer types with error handling.
|
||||
|
||||
use crate::box_trait::{BoolBox, BoxBase, BoxCore, IntegerBox, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
/// Modulo operations between boxes
|
||||
@ -124,4 +123,4 @@ impl Display for ModuloBox {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.fmt_box(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::runner;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
// use std::io::Write; // kept for future pretty-print extensions
|
||||
|
||||
/// Convert Program(JSON v0) to MIR(JSON v0) and return it as a String.
|
||||
/// Fail-Fast: prints stable tags on stderr and returns Err with the same tag text.
|
||||
@ -52,7 +52,7 @@ pub fn program_json_to_mir_json_with_imports(program_json: &str, imports: HashMa
|
||||
let _ = fs::remove_file(&tmp_path);
|
||||
// Ensure v1 schema: if output lacks schema_version but has functions, wrap minimally
|
||||
let s = match serde_json::from_str::<JsonValue>(&s0) {
|
||||
Ok(JsonValue::Object(mut m)) => {
|
||||
Ok(JsonValue::Object(m)) => {
|
||||
if m.get("schema_version").is_none() {
|
||||
if let Some(funcs) = m.get("functions").cloned() {
|
||||
let v1 = serde_json::json!({"schema_version":"1.0","functions": funcs});
|
||||
|
||||
@ -510,11 +510,11 @@ impl MirBuilder {
|
||||
let mut instruction = instruction;
|
||||
|
||||
// Precompute debug metadata to avoid borrow conflicts later
|
||||
let dbg_fn_name = self
|
||||
let _dbg_fn_name = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.map(|f| f.signature.name.clone());
|
||||
let dbg_region_id = self.debug_current_region_id();
|
||||
let _dbg_region_id = self.debug_current_region_id();
|
||||
// P0: PHI の軽量補強と観測は、関数ブロック取得前に実施して借用競合を避ける
|
||||
if let MirInstruction::Phi { dst, inputs } = &instruction {
|
||||
origin::phi::propagate_phi_meta(self, *dst, inputs);
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
//! 4. ✅ 巨大関数は分割: 100行超える関数を30-50行目標で分割
|
||||
|
||||
// Import from new modules (refactored with Box Theory)
|
||||
use super::calls::*;
|
||||
pub use super::calls::call_target::CallTarget;
|
||||
|
||||
// ========================================
|
||||
|
||||
@ -46,6 +46,7 @@ pub fn suggest_resolution(name: &str) -> String {
|
||||
|
||||
/// Check if a method name is commonly shadowed by global functions
|
||||
/// Used for generating warnings about potential self-recursion
|
||||
#[allow(dead_code)]
|
||||
pub fn is_commonly_shadowed_method(method: &str) -> bool {
|
||||
matches!(
|
||||
method,
|
||||
@ -57,6 +58,7 @@ pub fn is_commonly_shadowed_method(method: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Generate warning message for potential self-recursion
|
||||
#[allow(dead_code)]
|
||||
pub fn generate_self_recursion_warning(box_name: &str, method: &str) -> String {
|
||||
format!(
|
||||
"Warning: Potential self-recursion detected in {}.{}(). \
|
||||
@ -102,4 +104,4 @@ mod tests {
|
||||
assert!(warning.contains("::print()"));
|
||||
assert!(warning.contains("self-recursion"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,11 +5,8 @@
|
||||
* Replaces 6 different call instructions with a single unified system
|
||||
*/
|
||||
|
||||
use crate::mir::{Callee, Effect, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall, TypeCertainty};
|
||||
use super::call_target::CallTarget;
|
||||
use super::method_resolution;
|
||||
use super::extern_calls;
|
||||
use crate::mir::{Callee, EffectMask, ValueId};
|
||||
use crate::mir::definitions::call_unified::{CallFlags, MirCall};
|
||||
|
||||
/// Check if unified call system is enabled
|
||||
pub fn is_unified_call_enabled() -> bool {
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
//! - emit_legacy_call: レガシーCall発行(既存互換)
|
||||
//! - emit_global_call/emit_method_call/emit_constructor_call: 便利ラッパー
|
||||
|
||||
use super::super::{Effect, EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use super::super::{EffectMask, MirBuilder, MirInstruction, ValueId};
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use super::{CallTarget, call_unified};
|
||||
use super::CallTarget;
|
||||
|
||||
impl MirBuilder {
|
||||
/// Unified call emission - delegates to UnifiedCallEmitterBox
|
||||
@ -139,6 +139,7 @@ impl MirBuilder {
|
||||
// ========================================
|
||||
|
||||
/// Try fallback handlers for global functions (delegates to CallMaterializerBox)
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn try_global_fallback_handlers(
|
||||
&mut self,
|
||||
dst: Option<ValueId>,
|
||||
@ -149,6 +150,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Ensure receiver is materialized in Callee::Method (delegates to CallMaterializerBox)
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn materialize_receiver_in_callee(
|
||||
&mut self,
|
||||
callee: Callee,
|
||||
|
||||
@ -155,6 +155,7 @@ pub fn parse_extern_name(name: &str) -> (String, String) {
|
||||
}
|
||||
|
||||
/// Check if a name refers to an environment interface
|
||||
#[allow(dead_code)]
|
||||
pub fn is_env_interface(name: &str) -> bool {
|
||||
matches!(name,
|
||||
"env" | "env.console" | "env.fs" | "env.net" |
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
/*!
|
||||
* Function Lowering Utilities
|
||||
*
|
||||
@ -147,4 +149,4 @@ pub fn method_likely_returns_value(method_name: &str) -> bool {
|
||||
"add" | "sub" | "mul" | "div" |
|
||||
"min" | "max" | "abs"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +96,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
/// receiver型の検証(ヘルパー)
|
||||
///
|
||||
/// 指定されたreceiverがBox型を持っているか確認
|
||||
#[allow(dead_code)]
|
||||
pub fn has_box_type(&self, receiver: ValueId) -> bool {
|
||||
matches!(self.value_types.get(&receiver), Some(MirType::Box(_)))
|
||||
}
|
||||
@ -103,6 +104,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
/// receiver型の取得(ヘルパー)
|
||||
///
|
||||
/// 指定されたreceiverのBox型名を返す(存在しない場合はNone)
|
||||
#[allow(dead_code)]
|
||||
pub fn get_box_type(&self, receiver: ValueId) -> Option<&String> {
|
||||
match self.value_types.get(&receiver) {
|
||||
Some(MirType::Box(box_name)) => Some(box_name),
|
||||
@ -114,6 +116,7 @@ impl<'a> CalleeGuardBox<'a> {
|
||||
///
|
||||
/// box_name と receiver型が一致するか判定
|
||||
/// (静的メソッド呼び出しの検出用)
|
||||
#[allow(dead_code)]
|
||||
pub fn is_me_call(&self, box_name: &str, receiver: ValueId) -> bool {
|
||||
match self.get_box_type(receiver) {
|
||||
Some(recv_box) => recv_box == box_name,
|
||||
|
||||
@ -28,8 +28,13 @@ pub mod effects_analyzer; // Phase 3-B: Effects analyzer (エフェクト解析
|
||||
pub mod materializer; // Phase 3-C: Call materializer (Call前処理・準備専用箱)
|
||||
|
||||
// Re-export public interfaces
|
||||
#[allow(unused_imports)]
|
||||
pub use call_target::CallTarget;
|
||||
#[allow(unused_imports)]
|
||||
pub use lowering::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use utils::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use emit::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use build::*;
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
/*!
|
||||
* Special Call Handlers
|
||||
*
|
||||
@ -137,4 +139,4 @@ pub fn suggest_alternative_for_reserved(name: &str) -> String {
|
||||
"from" => "Use 'from Parent.method()' syntax for delegation".to_string(),
|
||||
_ => format!("'{}' is a reserved keyword", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,16 +23,19 @@ use std::collections::HashMap;
|
||||
pub struct BoxCompilationContext {
|
||||
/// 変数名 → ValueId マッピング
|
||||
/// 例: "args" → ValueId(0), "result" → ValueId(42)
|
||||
#[allow(dead_code)]
|
||||
pub variable_map: HashMap<String, ValueId>,
|
||||
|
||||
/// ValueId → 起源Box名 マッピング
|
||||
/// NewBox命令で生成されたValueIdがどのBox型から来たかを追跡
|
||||
/// 例: ValueId(10) → "ParserBox"
|
||||
#[allow(dead_code)]
|
||||
pub value_origin_newbox: HashMap<ValueId, String>,
|
||||
|
||||
/// ValueId → MIR型 マッピング
|
||||
/// 各ValueIdの型情報を保持
|
||||
/// 例: ValueId(5) → MirType::Integer
|
||||
#[allow(dead_code)]
|
||||
pub value_types: HashMap<ValueId, MirType>,
|
||||
}
|
||||
|
||||
@ -43,6 +46,7 @@ impl BoxCompilationContext {
|
||||
}
|
||||
|
||||
/// コンテキストが空(未使用)かどうかを判定
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.variable_map.is_empty()
|
||||
&& self.value_origin_newbox.is_empty()
|
||||
@ -50,6 +54,7 @@ impl BoxCompilationContext {
|
||||
}
|
||||
|
||||
/// デバッグ用:コンテキストのサイズ情報を取得
|
||||
#[allow(dead_code)]
|
||||
pub fn size_info(&self) -> (usize, usize, usize) {
|
||||
(
|
||||
self.variable_map.len(),
|
||||
|
||||
@ -38,11 +38,6 @@ impl super::MirBuilder {
|
||||
let _ = self.lower_static_method_as_function(func_name, params.clone(), body.clone());
|
||||
eprintln!("[DEBUG] build_static_main_box: After lower_static_method_as_function");
|
||||
eprintln!("[DEBUG] variable_map = {:?}", self.variable_map);
|
||||
// Convert the method body to a Program AST node and lower it
|
||||
let program_ast = ASTNode::Program {
|
||||
statements: body.clone(),
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
// Initialize local variables for Main.main() parameters
|
||||
// Note: These are local variables in the wrapper main() function, NOT parameters
|
||||
let saved_var_map = std::mem::take(&mut self.variable_map);
|
||||
@ -117,7 +112,7 @@ impl super::MirBuilder {
|
||||
weak_fields: Vec<String>,
|
||||
) -> Result<(), String> {
|
||||
// Create a type registration constant (marker)
|
||||
let type_id = crate::mir::builder::emission::constant::emit_string(self, format!("__box_type_{}", name));
|
||||
crate::mir::builder::emission::constant::emit_string(self, format!("__box_type_{}", name));
|
||||
|
||||
// Emit field metadata markers
|
||||
for field in fields {
|
||||
|
||||
@ -17,11 +17,13 @@ pub fn emit_to(b: &mut MirBuilder, dst: ValueId, op: CompareOp, lhs: ValueId, rh
|
||||
|
||||
// Convenience wrappers (明示関数名が読みやすい箇所用)
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_eq_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Eq, lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_ne_to(b: &mut MirBuilder, dst: ValueId, lhs: ValueId, rhs: ValueId) -> Result<(), String> {
|
||||
emit_to(b, dst, CompareOp::Ne, lhs, rhs)
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ impl super::MirBuilder {
|
||||
MirInstruction::Const { value, .. } => {
|
||||
if let super::ConstValue::String(s) = value { last_const_name = Some(s.clone()); }
|
||||
}
|
||||
MirInstruction::Call { func, .. } => {
|
||||
MirInstruction::Call { func: _, .. } => {
|
||||
// If immediately preceded by matching Const String, accept
|
||||
if let Some(prev) = last_const_name.as_ref() {
|
||||
if prev == &expect_tail { ok = true; break; }
|
||||
@ -318,7 +318,7 @@ impl super::MirBuilder {
|
||||
// Dev stub: provide condition_fn when missing to satisfy predicate calls in JSON lexers
|
||||
// Returns integer 1 (truthy) and accepts one argument (unused).
|
||||
if module.functions.get("condition_fn").is_none() {
|
||||
let mut sig = FunctionSignature {
|
||||
let sig = FunctionSignature {
|
||||
name: "condition_fn".to_string(),
|
||||
params: vec![MirType::Integer], // accept one i64-like arg
|
||||
return_type: MirType::Integer,
|
||||
|
||||
@ -32,6 +32,7 @@ pub fn propagate(builder: &mut MirBuilder, src: ValueId, dst: ValueId) {
|
||||
/// dst に型注釈を明示的に設定し、必要ならば起源情報を消去/維持する。
|
||||
/// 🎯 TypeRegistry 経由モード対応(NYASH_USE_TYPE_REGISTRY=1)
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
let use_registry = std::env::var("NYASH_USE_TYPE_REGISTRY")
|
||||
.ok()
|
||||
@ -45,4 +46,3 @@ pub fn propagate_with_override(builder: &mut MirBuilder, dst: ValueId, ty: MirTy
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ impl MirBuilder {
|
||||
}
|
||||
|
||||
/// Check if this is a TypeOp method call
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn is_typeop_method(method: &str, arguments: &[ASTNode]) -> Option<String> {
|
||||
if (method == "is" || method == "as") && arguments.len() == 1 {
|
||||
Self::extract_string_literal(&arguments[0])
|
||||
|
||||
@ -12,7 +12,7 @@ impl MirBuilder {
|
||||
pub(super) fn merge_modified_vars(
|
||||
&mut self,
|
||||
_then_block: super::BasicBlockId,
|
||||
else_block: super::BasicBlockId,
|
||||
_else_block: super::BasicBlockId,
|
||||
then_exit_block_opt: Option<super::BasicBlockId>,
|
||||
else_exit_block_opt: Option<super::BasicBlockId>,
|
||||
pre_if_snapshot: &std::collections::HashMap<String, super::ValueId>,
|
||||
@ -179,8 +179,8 @@ impl MirBuilder {
|
||||
/// This handles variable reassignment patterns and ensures a single exit value.
|
||||
pub(super) fn normalize_if_else_phi(
|
||||
&mut self,
|
||||
then_block: BasicBlockId,
|
||||
else_block: BasicBlockId,
|
||||
_then_block: BasicBlockId,
|
||||
_else_block: BasicBlockId,
|
||||
then_exit_block_opt: Option<BasicBlockId>,
|
||||
else_exit_block_opt: Option<BasicBlockId>,
|
||||
then_value_raw: ValueId,
|
||||
|
||||
@ -19,6 +19,7 @@ fn rewrite_enabled() -> bool {
|
||||
|
||||
/// Try Known‑route instance→function rewrite.
|
||||
/// 既存の安全ガード(user_defined/存在確認/ENV)を尊重して関数化する。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_known_rewrite(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: ValueId,
|
||||
@ -119,6 +120,7 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
|
||||
/// Fallback: when exactly one user-defined method matches by name/arity across the module,
|
||||
/// resolve to that even if class inference failed. Deterministic via uniqueness and user-box prefix.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_unique_suffix_rewrite(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: ValueId,
|
||||
@ -183,7 +185,7 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
if cands.len() != 1 { return None; }
|
||||
let fname = cands.remove(0);
|
||||
if let Some((bx, _)) = fname.split_once('.') { if !builder.user_defined_boxes.contains(bx) { return None; } } else { return None; }
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -212,6 +214,7 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
}
|
||||
|
||||
/// Unified entry: try Known rewrite first, then unique-suffix fallback.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_known_or_unique(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: ValueId,
|
||||
|
||||
@ -2,6 +2,7 @@ use super::super::MirBuilder;
|
||||
|
||||
/// Early special-case: toString/stringify → str(互換)を処理。
|
||||
/// 戻り値: Some(result_id) なら処理済み。None なら通常経路へ委譲。
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_early_str_like(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: super::super::ValueId,
|
||||
@ -104,6 +105,7 @@ pub(crate) fn try_early_str_like(
|
||||
|
||||
/// Special-case for equals/1: prefer Known rewrite; otherwise allow unique-suffix fallback
|
||||
/// when it is deterministic (single candidate). This centralizes equals handling.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn try_special_equals(
|
||||
builder: &mut MirBuilder,
|
||||
object_value: super::super::ValueId,
|
||||
@ -151,7 +153,7 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Known",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &chosen) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -182,7 +184,7 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
@ -209,7 +211,7 @@ pub(crate) fn try_early_str_like_to_dst(
|
||||
"certainty": "Heuristic",
|
||||
});
|
||||
super::super::observe::resolve::emit_choose(builder, meta);
|
||||
let name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ pub struct BlockScheduleBox;
|
||||
|
||||
impl BlockScheduleBox {
|
||||
/// Insert a Copy immediately after PHI nodes. Returns the local value id.
|
||||
#[allow(dead_code)]
|
||||
pub fn ensure_after_phis_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
if let Some(bb) = builder.current_block {
|
||||
if let Some(&cached) = builder.schedule_mat_map.get(&(bb, src)) {
|
||||
@ -29,6 +30,7 @@ impl BlockScheduleBox {
|
||||
|
||||
/// Emit a Copy right before the next emitted instruction (best-effort):
|
||||
/// place it at the tail of the current block. Returns the local value id.
|
||||
#[allow(dead_code)]
|
||||
pub fn emit_before_call_copy(builder: &mut MirBuilder, src: ValueId) -> Result<ValueId, String> {
|
||||
// Prefer to reuse the after-phis materialized id for this src in this block
|
||||
let base = Self::ensure_after_phis_copy(builder, src)?;
|
||||
|
||||
@ -13,6 +13,7 @@ use std::collections::HashMap;
|
||||
pub struct TraceEntry {
|
||||
pub vid: ValueId,
|
||||
pub source: String, // "newbox:MapBox", "param:args", "propagate:from_%123"
|
||||
#[allow(dead_code)]
|
||||
pub timestamp: usize,
|
||||
}
|
||||
|
||||
@ -52,6 +53,7 @@ impl TypeRegistry {
|
||||
// ============================================================
|
||||
|
||||
/// NewBox起源を記録
|
||||
#[allow(dead_code)]
|
||||
pub fn record_newbox(&mut self, vid: ValueId, class: String) {
|
||||
self.origins.insert(vid, class.clone());
|
||||
|
||||
@ -67,6 +69,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// パラメータ型を記録
|
||||
#[allow(dead_code)]
|
||||
pub fn record_param(&mut self, vid: ValueId, param_name: &str, param_type: Option<MirType>) {
|
||||
if let Some(ty) = param_type.clone() {
|
||||
self.types.insert(vid, ty.clone());
|
||||
@ -107,6 +110,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 起源を明示的に設定(推論結果など)
|
||||
#[allow(dead_code)]
|
||||
pub fn record_origin(&mut self, vid: ValueId, origin: String, reason: &str) {
|
||||
self.origins.insert(vid, origin.clone());
|
||||
|
||||
@ -164,11 +168,13 @@ impl TypeRegistry {
|
||||
// ============================================================
|
||||
|
||||
/// 起源クラス名を取得
|
||||
#[allow(dead_code)]
|
||||
pub fn get_origin(&self, vid: ValueId) -> Option<&String> {
|
||||
self.origins.get(&vid)
|
||||
}
|
||||
|
||||
/// 型情報を取得
|
||||
#[allow(dead_code)]
|
||||
pub fn get_type(&self, vid: ValueId) -> Option<&MirType> {
|
||||
self.types.get(&vid)
|
||||
}
|
||||
@ -214,6 +220,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 全トレースログを表示
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_trace(&self) {
|
||||
eprintln!("[type-registry] === Trace Log ({} entries) ===", self.trace_log.len());
|
||||
for entry in &self.trace_log {
|
||||
@ -222,6 +229,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 統計情報を表示
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_stats(&self) {
|
||||
eprintln!("[type-registry] === Statistics ===");
|
||||
eprintln!("[type-registry] Origins: {} entries", self.origins.len());
|
||||
@ -234,6 +242,7 @@ impl TypeRegistry {
|
||||
// ============================================================
|
||||
|
||||
/// 起源情報のみクリア(型情報は保持)
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_origins(&mut self) {
|
||||
self.origins.clear();
|
||||
if self.trace_enabled {
|
||||
@ -242,6 +251,7 @@ impl TypeRegistry {
|
||||
}
|
||||
|
||||
/// 全情報クリア
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_all(&mut self) {
|
||||
self.origins.clear();
|
||||
self.types.clear();
|
||||
|
||||
@ -5,6 +5,7 @@ use crate::mir::builder::MirBuilder;
|
||||
|
||||
/// 直接的に MirType を設定する(仕様不変)。
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_type(builder: &mut MirBuilder, dst: ValueId, ty: MirType) {
|
||||
builder.value_types.insert(dst, ty);
|
||||
}
|
||||
|
||||
@ -334,6 +334,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
/// Ensure a value has a local definition in the current block by inserting a Copy.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
|
||||
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
|
||||
let dst = if let Some(ref mut f) = self.current_function {
|
||||
@ -348,6 +349,7 @@ impl super::MirBuilder {
|
||||
}
|
||||
|
||||
/// Insert a Copy immediately after PHI nodes in the current block (position-stable).
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn insert_copy_after_phis(&mut self, dst: super::ValueId, src: super::ValueId) -> Result<(), String> {
|
||||
if let (Some(ref mut function), Some(bb)) = (&mut self.current_function, self.current_block) {
|
||||
if std::env::var("NYASH_SCHEDULE_TRACE").ok().as_deref() == Some("1") {
|
||||
|
||||
222
src/mir/control_form.rs
Normal file
222
src/mir/control_form.rs
Normal file
@ -0,0 +1,222 @@
|
||||
/*!
|
||||
* ControlForm – 共通制御構造ビュー(Loop / If の箱化レイヤ)
|
||||
*
|
||||
* 目的:
|
||||
* - LoopForm v2(ループ)と If 降下を、1段上の「制御構造の形」として統一的に眺めるための薄いレイヤだよ。
|
||||
* - Conservative PHI Box や将来の可視化/検証ロジックが、Loop 専用 / If 専用に分かれず、
|
||||
* ControlForm という SSOT から情報を取れるようにするのがねらいだよ。
|
||||
*
|
||||
* このモジュール自体は構造定義とデバッグ用のユーティリティのみを提供し、
|
||||
* 既存の LoopBuilder / If 降下の挙動は変えないよ(Phase 25.1f では観測レイヤ専用)。
|
||||
*/
|
||||
|
||||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction};
|
||||
|
||||
/// ループ構造の形だけを表す箱だよ。
|
||||
///
|
||||
/// - `preheader` : ループ直前のブロック(キャリア/ピン変数のコピー元)
|
||||
/// - `header` : ループヘッダ(条件判定と header PHI が置かれる)
|
||||
/// - `body` : 代表的なループ本体ブロック(最初の body など)
|
||||
/// - `latch` : ヘッダへ戻るバックエッジを張るブロック
|
||||
/// - `exit` : ループを抜けた先のブロック
|
||||
/// - `continue_targets` : continue がジャンプするブロック群(通常は latch か header)
|
||||
/// - `break_targets` : break がジャンプするブロック群(通常は exit)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoopShape {
|
||||
pub preheader: BasicBlockId,
|
||||
pub header: BasicBlockId,
|
||||
pub body: BasicBlockId,
|
||||
pub latch: BasicBlockId,
|
||||
pub exit: BasicBlockId,
|
||||
pub continue_targets: Vec<BasicBlockId>,
|
||||
pub break_targets: Vec<BasicBlockId>,
|
||||
}
|
||||
|
||||
/// if/else 構造の形だけを表す箱だよ。
|
||||
///
|
||||
/// - `cond_block` : 条件式を評価するブロック
|
||||
/// - `then_block` : then ブランチの先頭ブロック
|
||||
/// - `else_block` : else ブランチの先頭ブロック(無ければ None)
|
||||
/// - `merge_block`: then/else の合流ブロック
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfShape {
|
||||
pub cond_block: BasicBlockId,
|
||||
pub then_block: BasicBlockId,
|
||||
pub else_block: Option<BasicBlockId>,
|
||||
pub merge_block: BasicBlockId,
|
||||
}
|
||||
|
||||
/// 制御構造の種別だよ。
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ControlKind {
|
||||
Loop(LoopShape),
|
||||
If(IfShape),
|
||||
}
|
||||
|
||||
/// ループ / if / 将来の switch などを、共通のビューとして扱う箱だよ。
|
||||
///
|
||||
/// - `entry` : 構造に入る入口ブロック
|
||||
/// - `exits` : 構造を抜けたあとのブロック群
|
||||
/// - `kind` : Loop / If などの種別ごとの Shape
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ControlForm {
|
||||
pub entry: BasicBlockId,
|
||||
pub exits: Vec<BasicBlockId>,
|
||||
pub kind: ControlKind,
|
||||
}
|
||||
|
||||
impl ControlForm {
|
||||
/// ループ用 Shape から ControlForm を生成するよ。
|
||||
///
|
||||
/// ループの entry は preheader、exit は exit ブロック 1 つとみなす。
|
||||
pub fn from_loop(shape: LoopShape) -> Self {
|
||||
ControlForm {
|
||||
entry: shape.preheader,
|
||||
exits: vec![shape.exit],
|
||||
kind: ControlKind::Loop(shape),
|
||||
}
|
||||
}
|
||||
|
||||
/// If 用 Shape から ControlForm を生成するよ。
|
||||
///
|
||||
/// If の entry は cond_block、exit は merge_block 1 つとみなす。
|
||||
pub fn from_if(shape: IfShape) -> Self {
|
||||
ControlForm {
|
||||
entry: shape.cond_block,
|
||||
exits: vec![shape.merge_block],
|
||||
kind: ControlKind::If(shape),
|
||||
}
|
||||
}
|
||||
|
||||
/// これはループかな?という軽い判定だよ。
|
||||
pub fn is_loop(&self) -> bool {
|
||||
matches!(self.kind, ControlKind::Loop(_))
|
||||
}
|
||||
|
||||
/// これは if 構造かな?という軽い判定だよ。
|
||||
pub fn is_if(&self) -> bool {
|
||||
matches!(self.kind, ControlKind::If(_))
|
||||
}
|
||||
|
||||
/// デバッグ用に構造をダンプするよ。
|
||||
///
|
||||
/// 呼び出し側で `NYASH_CONTROL_FORM_TRACE=1` を見る想定なので、
|
||||
/// ここでは単純に eprintln! するだけにしておく。
|
||||
pub fn debug_dump(&self) {
|
||||
match &self.kind {
|
||||
ControlKind::Loop(shape) => {
|
||||
eprintln!(
|
||||
"[ControlForm::Loop] entry={:?} preheader={:?} header={:?} body={:?} latch={:?} exit={:?} continue={:?} break={:?}",
|
||||
self.entry,
|
||||
shape.preheader,
|
||||
shape.header,
|
||||
shape.body,
|
||||
shape.latch,
|
||||
shape.exit,
|
||||
shape.continue_targets,
|
||||
shape.break_targets,
|
||||
);
|
||||
}
|
||||
ControlKind::If(shape) => {
|
||||
eprintln!(
|
||||
"[ControlForm::If] entry={:?} cond={:?} then={:?} else={:?} merge={:?} exits={:?}",
|
||||
self.entry,
|
||||
shape.cond_block,
|
||||
shape.then_block,
|
||||
shape.else_block,
|
||||
shape.merge_block,
|
||||
self.exits,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ControlForm の invariant を軽く検査するための CFG 抽象だよ。
|
||||
///
|
||||
/// 実装は MirFunction などに持たせて、`debug_validate` から使う想定。
|
||||
pub trait CfgLike {
|
||||
fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool;
|
||||
fn predecessors_len(&self, block: BasicBlockId) -> usize;
|
||||
}
|
||||
|
||||
impl CfgLike for MirFunction {
|
||||
fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool {
|
||||
self.blocks
|
||||
.get(&from)
|
||||
.map(|bb: &BasicBlock| bb.successors.contains(&to))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn predecessors_len(&self, block: BasicBlockId) -> usize {
|
||||
self.blocks
|
||||
.get(&block)
|
||||
.map(|bb: &BasicBlock| bb.predecessors.len())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// ControlForm トレース用の環境フラグを判定するヘルパーだよ。
|
||||
///
|
||||
/// - 未設定 → 既定で ON
|
||||
/// - "0" / "false" → OFF
|
||||
/// - それ以外 → ON
|
||||
pub fn is_control_form_trace_on() -> bool {
|
||||
std::env::var("NYASH_CONTROL_FORM_TRACE")
|
||||
.map(|v| v != "0" && v.to_lowercase() != "false")
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
impl LoopShape {
|
||||
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
|
||||
///
|
||||
/// - preheader → header にエッジがあること
|
||||
/// - latch → header にバックエッジがあること
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_validate<C: CfgLike>(&self, cfg: &C) {
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.preheader, self.header),
|
||||
"LoopShape invalid: preheader -> header edge missing: {:?} -> {:?}",
|
||||
self.preheader,
|
||||
self.header
|
||||
);
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.latch, self.header),
|
||||
"LoopShape invalid: latch -> header backedge missing: {:?} -> {:?}",
|
||||
self.latch,
|
||||
self.header
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl IfShape {
|
||||
/// Debug ビルドでだけ呼ぶ用の簡易 invariant チェックだよ。
|
||||
///
|
||||
/// - cond → then / else にエッジがあること
|
||||
/// - merge については、predecessor 情報がまだ配線途中のケースもあるので
|
||||
/// ここでは「0 ならログだけ出す(panic しない)」ことにするよ。
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn debug_validate<C: CfgLike>(&self, cfg: &C) {
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.cond_block, self.then_block),
|
||||
"IfShape invalid: cond -> then edge missing: {:?} -> {:?}",
|
||||
self.cond_block,
|
||||
self.then_block
|
||||
);
|
||||
if let Some(else_blk) = self.else_block {
|
||||
debug_assert!(
|
||||
cfg.has_edge(self.cond_block, else_blk),
|
||||
"IfShape invalid: cond -> else edge missing: {:?} -> {:?}",
|
||||
self.cond_block,
|
||||
else_blk
|
||||
);
|
||||
}
|
||||
let preds = cfg.predecessors_len(self.merge_block);
|
||||
if preds == 0 && std::env::var("NYASH_CONTROL_FORM_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[ControlForm::IfShape] WARN: merge block {:?} has no predecessors yet",
|
||||
self.merge_block
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
use super::{BasicBlockId, ConstValue, MirInstruction, ValueId};
|
||||
use crate::mir::control_form::{ControlForm, IfShape, LoopShape, is_control_form_trace_on};
|
||||
use crate::mir::phi_core::loop_phi::IncompletePhi;
|
||||
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
||||
use crate::ast::ASTNode;
|
||||
@ -342,7 +343,6 @@ impl<'a> LoopBuilder<'a> {
|
||||
|
||||
// Jump to latch if not already terminated
|
||||
let actual_latch_id = if !is_current_block_terminated(self.parent_builder)? {
|
||||
let cur_body_end = self.current_block()?;
|
||||
self.emit_jump(latch_id)?;
|
||||
latch_id
|
||||
} else {
|
||||
@ -375,6 +375,38 @@ impl<'a> LoopBuilder<'a> {
|
||||
// Pop loop context
|
||||
crate::mir::builder::loops::pop_loop_context(self.parent_builder);
|
||||
|
||||
// ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき LoopShape をダンプ
|
||||
if is_control_form_trace_on() {
|
||||
// continue / break のターゲットブロックをユニーク化して収集
|
||||
use std::collections::HashSet;
|
||||
let mut cont_set: HashSet<BasicBlockId> = HashSet::new();
|
||||
let mut break_set: HashSet<BasicBlockId> = HashSet::new();
|
||||
for (bb, _) in &self.continue_snapshots {
|
||||
cont_set.insert(*bb);
|
||||
}
|
||||
for (bb, _) in &self.exit_snapshots {
|
||||
break_set.insert(*bb);
|
||||
}
|
||||
let continue_targets: Vec<BasicBlockId> = cont_set.into_iter().collect();
|
||||
let break_targets: Vec<BasicBlockId> = break_set.into_iter().collect();
|
||||
|
||||
let loop_shape = LoopShape {
|
||||
preheader: preheader_id,
|
||||
header: header_id,
|
||||
body: body_id,
|
||||
latch: latch_id,
|
||||
exit: exit_id,
|
||||
continue_targets,
|
||||
break_targets,
|
||||
};
|
||||
let form = ControlForm::from_loop(loop_shape.clone());
|
||||
form.debug_dump();
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(ref func) = self.parent_builder.current_function {
|
||||
loop_shape.debug_validate(func);
|
||||
}
|
||||
}
|
||||
|
||||
// Return void value
|
||||
let void_dst = self.new_value();
|
||||
self.emit_const(void_dst, ConstValue::Void)?;
|
||||
@ -486,7 +518,7 @@ impl<'a> LoopBuilder<'a> {
|
||||
}
|
||||
|
||||
// Add PHI nodes for new pinned variables in header block
|
||||
for (name, value, preheader_value) in new_pinned_vars {
|
||||
for (name, _value, preheader_value) in new_pinned_vars {
|
||||
let phi_id = self.new_value();
|
||||
self.emit_phi_at_block_start(header_id, phi_id, vec![(preheader_id, preheader_value)])?;
|
||||
// Update variable map to use PHI value
|
||||
@ -1156,6 +1188,22 @@ impl<'a> LoopBuilder<'a> {
|
||||
&else_var_map_end_opt,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// ControlForm 観測: 環境フラグ(未設定時は既定ON)のとき IfShape をダンプ
|
||||
if is_control_form_trace_on() {
|
||||
let if_shape = IfShape {
|
||||
cond_block: pre_branch_bb,
|
||||
then_block: then_bb,
|
||||
else_block: Some(else_bb),
|
||||
merge_block: merge_bb,
|
||||
};
|
||||
let form = ControlForm::from_if(if_shape.clone());
|
||||
form.debug_dump();
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(ref func) = self.parent_builder.current_function {
|
||||
if_shape.debug_validate(func);
|
||||
}
|
||||
}
|
||||
let void_id = self.new_value();
|
||||
self.emit_const(void_id, ConstValue::Void)?;
|
||||
// Pop merge debug region
|
||||
|
||||
@ -34,6 +34,7 @@ pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
|
||||
pub mod value_id;
|
||||
pub mod verification;
|
||||
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints)
|
||||
pub mod control_form; // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
||||
|
||||
// Re-export main types for easy access
|
||||
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
||||
|
||||
@ -145,7 +145,7 @@ pub fn merge_modified_at_merge_with<O: PhiMergeOps>(
|
||||
ops: &mut O,
|
||||
merge_bb: crate::mir::BasicBlockId,
|
||||
_then_block: crate::mir::BasicBlockId,
|
||||
else_block: crate::mir::BasicBlockId,
|
||||
_else_block: crate::mir::BasicBlockId,
|
||||
then_pred_opt: Option<crate::mir::BasicBlockId>,
|
||||
else_pred_opt: Option<crate::mir::BasicBlockId>,
|
||||
pre_if_snapshot: &HashMap<String, ValueId>,
|
||||
|
||||
@ -193,7 +193,7 @@ pub fn prepare_loop_variables_with<O: LoopPhiOps>(
|
||||
ops.emit_copy_at_preheader(preheader_id, pre_copy, value_before)?;
|
||||
|
||||
let phi_id = ops.new_value();
|
||||
let mut inc = IncompletePhi {
|
||||
let inc = IncompletePhi {
|
||||
phi_id,
|
||||
var_name: var_name.clone(),
|
||||
known_inputs: vec![(preheader_id, pre_copy)], // ensure def at preheader
|
||||
|
||||
@ -67,6 +67,7 @@ pub fn compute_dominators(function: &MirFunction) -> HashMap<BasicBlockId, HashS
|
||||
dom
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn compute_reachable_blocks(function: &MirFunction) -> HashSet<BasicBlockId> {
|
||||
let mut reachable = HashSet::new();
|
||||
let mut worklist = vec![function.entry_block];
|
||||
|
||||
@ -43,6 +43,7 @@ impl<'a> TokenCursor<'a> {
|
||||
}
|
||||
|
||||
/// 次のトークンをピーク
|
||||
#[allow(dead_code)]
|
||||
pub fn peek(&self) -> &Token {
|
||||
self.tokens.get(self.idx + 1).unwrap_or(&Token {
|
||||
token_type: TokenType::EOF,
|
||||
@ -52,6 +53,7 @@ impl<'a> TokenCursor<'a> {
|
||||
}
|
||||
|
||||
/// N番目のトークンをピーク
|
||||
#[allow(dead_code)]
|
||||
pub fn peek_nth(&self, n: usize) -> &Token {
|
||||
self.tokens.get(self.idx + n).unwrap_or(&Token {
|
||||
token_type: TokenType::EOF,
|
||||
@ -220,11 +222,13 @@ impl<'a> TokenCursor<'a> {
|
||||
}
|
||||
|
||||
/// モードを取得
|
||||
#[allow(dead_code)]
|
||||
pub fn get_mode(&self) -> NewlineMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// モードを設定
|
||||
#[allow(dead_code)]
|
||||
pub fn set_mode(&mut self, mode: NewlineMode) {
|
||||
self.mode = mode;
|
||||
}
|
||||
@ -294,4 +298,4 @@ mod tests {
|
||||
assert!(c.match_token(&TokenType::PLUS));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ impl NyashParser {
|
||||
}
|
||||
|
||||
/// Small helper: build UnexpectedToken with current token and line
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn err_unexpected<S: Into<String>>(&self, expected: S) -> ParseError {
|
||||
ParseError::UnexpectedToken {
|
||||
found: self.current_token().token_type.clone(),
|
||||
@ -68,6 +69,7 @@ impl NyashParser {
|
||||
}
|
||||
|
||||
/// Expect an identifier and advance. Returns its string or an UnexpectedToken error
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn expect_identifier(&mut self, what: &str) -> Result<String, ParseError> {
|
||||
if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
|
||||
let out = name.clone();
|
||||
@ -77,4 +79,4 @@ impl NyashParser {
|
||||
Err(self.err_unexpected(what))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ impl NyashParser {
|
||||
let mut statements = Vec::new();
|
||||
|
||||
// Helper: lookahead for `ident '(' ... ')' [NEWLINE*] '{'`
|
||||
let mut looks_like_method_head = |this: &Self| -> bool {
|
||||
let looks_like_method_head = |this: &Self| -> bool {
|
||||
// Only meaningful when starting at a new statement head
|
||||
match &this.current_token().token_type {
|
||||
TokenType::IDENTIFIER(_) => {
|
||||
|
||||
@ -8,6 +8,7 @@ pub fn pre_run_reset_oob_if_strict() {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn post_run_exit_if_oob_strict_triggered() -> ! {
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
|
||||
@ -90,6 +90,7 @@ pub(super) fn demo_parser_system() {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn demo_interpreter_system() {
|
||||
println!("\n🎭 7. Interpreter System:");
|
||||
println!(" ⚠️ Legacy interpreter removed - use VM or LLVM backends instead");
|
||||
|
||||
@ -129,18 +129,17 @@ pub fn run_json_v1_inline(json: &str) -> i32 {
|
||||
|
||||
let sval = regs.get(&src).cloned();
|
||||
let is_integer = sval.is_some(); // hv1 inline stores i64 only → integer
|
||||
let mut out = 0i64;
|
||||
if operation == "check" || operation == "is" {
|
||||
if target == "i64" || target == "int" || target == "integer" {
|
||||
out = if is_integer { 1 } else { 0 };
|
||||
let out: i64 = if target == "i64" || target == "int" || target == "integer" {
|
||||
if is_integer { 1 } else { 0 }
|
||||
} else if target == "bool" {
|
||||
// Inline model uses integer registers; treat 0/1 as bool when present
|
||||
out = if let Some(v) = sval { if v == 0 || v == 1 { 1 } else { 0 } } else { 0 };
|
||||
if let Some(v) = sval { if v == 0 || v == 1 { 1 } else { 0 } } else { 0 }
|
||||
} else if target == "string" {
|
||||
out = 0; // no string registers in inline model
|
||||
0 // no string registers in inline model
|
||||
} else {
|
||||
out = 0;
|
||||
}
|
||||
0
|
||||
};
|
||||
regs.insert(dst, out);
|
||||
} else {
|
||||
// cast/as: pass-through (MVP)
|
||||
|
||||
@ -2,7 +2,7 @@ use super::ast::{ProgramV0, StmtV0, ExprV0};
|
||||
use crate::mir::Callee;
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule,
|
||||
MirPrinter, MirType, ValueId, BinaryOp,
|
||||
MirPrinter, MirType, ValueId,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
@ -103,6 +103,7 @@ pub(super) struct BridgeEnv {
|
||||
}
|
||||
|
||||
impl BridgeEnv {
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn load() -> Self {
|
||||
Self::with_imports(HashMap::new())
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use super::BridgeEnv;
|
||||
use super::ternary;
|
||||
use super::match_expr;
|
||||
use crate::mir::{
|
||||
BasicBlockId, BinaryOp, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId,
|
||||
BasicBlockId, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use super::{lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv, LoopContext};
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use crate::mir::{BasicBlockId, MirFunction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
use super::super::ast::StmtV0;
|
||||
use super::super::ast::ExprV0;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
use super::merge::new_block;
|
||||
use super::BridgeEnv;
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use crate::mir::{BasicBlockId, MirFunction, ValueId};
|
||||
use super::super::ast::ExprV0;
|
||||
|
||||
use super::expr::{lower_expr_with_scope, VarScope};
|
||||
@ -35,7 +35,7 @@ pub(super) fn lower_ternary_expr_with_scope<S: VarScope>(
|
||||
}
|
||||
let out = f.next_value_id();
|
||||
// フェーズM.2: PHI統一処理(no_phi分岐削除)
|
||||
let mut inputs = vec![(tend, tval), (eend, eval)];
|
||||
let inputs = vec![(tend, tval), (eend, eval)];
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, merge_bb, out, inputs);
|
||||
Ok((out, merge_bb))
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use crate::mir::{BasicBlockId, MirFunction, ValueId};
|
||||
use std::cell::RefCell;
|
||||
|
||||
thread_local! {
|
||||
|
||||
@ -511,27 +511,6 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
if let Some(d) = dst_opt { max_value_id = max_value_id.max(d.as_u32() + 1); }
|
||||
}
|
||||
}
|
||||
"Constructor" => {
|
||||
// box_type: string, dst: required
|
||||
let dst = dst_opt.ok_or_else(|| format!(
|
||||
"mir_call Constructor requires dst in function '{}'",
|
||||
func_name
|
||||
))?;
|
||||
let bt = callee_obj
|
||||
.get("box_type")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| format!(
|
||||
"mir_call Constructor missing box_type in function '{}'",
|
||||
func_name
|
||||
))?
|
||||
.to_string();
|
||||
block_ref.add_instruction(MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type: bt,
|
||||
args: argv.clone(),
|
||||
});
|
||||
max_value_id = max_value_id.max(dst.as_u32() + 1);
|
||||
}
|
||||
"Extern" => {
|
||||
let name = callee_obj
|
||||
.get("name")
|
||||
|
||||
@ -31,6 +31,7 @@ fn create_json_v1_root(functions: serde_json::Value) -> serde_json::Value {
|
||||
/// Helper: detect residual numeric-core boxcalls that should have been lowered by AotPrepNumericCoreBox.
|
||||
/// Currently we only check for `boxcall` with `method:"mul_naive"` which should become
|
||||
/// `call("NyNumericMatI64.mul_naive", ...)` when NYASH_AOT_NUMERIC_CORE=1 is effective.
|
||||
#[allow(dead_code)]
|
||||
fn has_numeric_core_boxcall(root: &serde_json::Value) -> bool {
|
||||
let funs = match root.get("functions") {
|
||||
Some(v) => v.as_array().cloned().unwrap_or_default(),
|
||||
@ -61,6 +62,7 @@ fn has_numeric_core_boxcall(root: &serde_json::Value) -> bool {
|
||||
/// Helper: enforce numeric_core invariants when NYASH_AOT_NUMERIC_CORE=1 is set.
|
||||
/// - Default: emit a warning if mul_naive boxcalls are still present.
|
||||
/// - Strict: if NYASH_AOT_NUMERIC_CORE_STRICT=1, return Err to fail fast.
|
||||
#[allow(dead_code)]
|
||||
fn check_numeric_core_invariants(root: &serde_json::Value) -> Result<(), String> {
|
||||
let numeric_on = matches!(std::env::var("NYASH_AOT_NUMERIC_CORE").ok().as_deref(), Some("1"));
|
||||
if !numeric_on {
|
||||
|
||||
@ -22,7 +22,7 @@ pub fn gather_required_providers() -> Vec<String> {
|
||||
return v;
|
||||
}
|
||||
// Default conservative set
|
||||
let mut v = vec![
|
||||
let v = vec![
|
||||
"FileBox".to_string(),
|
||||
"ConsoleBox".to_string(),
|
||||
"ArrayBox".to_string(),
|
||||
@ -86,4 +86,3 @@ pub fn check_and_report(strict: bool, quiet_pipe: bool, label: &str) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
fn build_text_merged(
|
||||
&self,
|
||||
source: &str,
|
||||
filename: &str,
|
||||
_filename: &str,
|
||||
prelude_paths: &[String],
|
||||
trace: bool,
|
||||
) -> Result<String, String> {
|
||||
@ -123,7 +123,7 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
|
||||
|
||||
// using行を除去して正規化
|
||||
let using_resolver = UsingResolutionBox::new(&self.runner, path)?;
|
||||
let _using_resolver = UsingResolutionBox::new(&self.runner, path)?;
|
||||
let (cleaned_raw, _nested) = self.collect_using_and_strip_internal(&content, path)?;
|
||||
let cleaned = self.normalize_text_for_inline(&cleaned_raw);
|
||||
|
||||
|
||||
@ -129,7 +129,7 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
&self,
|
||||
error: &str,
|
||||
original_code: &str,
|
||||
filename: &str,
|
||||
_filename: &str,
|
||||
) -> CompilationResult {
|
||||
eprintln!("[selfhost-pipeline] ⚠️ Error: {}", error);
|
||||
eprintln!("[selfhost-pipeline] 🔄 Falling back to original code");
|
||||
@ -179,8 +179,8 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
/// 📊 パフォーマンスプロファイリングするにゃ!
|
||||
pub fn profile_pipeline(
|
||||
&mut self,
|
||||
code: &str,
|
||||
filename: &str,
|
||||
_code: &str,
|
||||
_filename: &str,
|
||||
) -> Result<String, String> {
|
||||
// プロファイル機能を実装(別途)
|
||||
// TODO: プロファイル機能を追加
|
||||
|
||||
@ -448,7 +448,7 @@ pub fn resolve_prelude_paths_profiled(
|
||||
// must be discovered so that their definitions are present at runtime
|
||||
// (e.g., runner_min -> lower_* boxes). Previously this only ran when
|
||||
// NYASH_USING_AST=1, which caused unresolved calls in inline flows.
|
||||
let ast_on = crate::config::env::env_bool("NYASH_USING_AST");
|
||||
let _ast_on = crate::config::env::env_bool("NYASH_USING_AST");
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
fn normalize_path(path: &str) -> (String, String) {
|
||||
|
||||
@ -14,6 +14,7 @@ pub struct UsingResolutionBox<'a> {
|
||||
runner: &'a NyashRunner,
|
||||
config: UsingConfig,
|
||||
ctx_dir: Option<PathBuf>,
|
||||
#[allow(dead_code)]
|
||||
filename_canon: Option<PathBuf>,
|
||||
inside_pkg: bool,
|
||||
seen_paths: HashMap<String, (String, usize)>, // canon_path -> (alias/label, first_line)
|
||||
|
||||
@ -476,7 +476,7 @@ impl NyashRunner {
|
||||
|
||||
match vm.execute_module(&module_vm) {
|
||||
Ok(ret) => {
|
||||
use crate::box_trait::{NyashBox, IntegerBox, BoolBox};
|
||||
use crate::box_trait::{IntegerBox, BoolBox};
|
||||
|
||||
// Extract exit code from return value
|
||||
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
||||
|
||||
@ -321,7 +321,7 @@ impl NyashRunner {
|
||||
}
|
||||
match vm.execute_module(&module_vm) {
|
||||
Ok(ret) => {
|
||||
use crate::box_trait::{NyashBox, IntegerBox, BoolBox};
|
||||
use crate::box_trait::{IntegerBox, BoolBox};
|
||||
|
||||
// Extract exit code from return value
|
||||
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
||||
@ -346,7 +346,8 @@ impl NyashRunner {
|
||||
|
||||
impl NyashRunner {
|
||||
/// Small helper to continue fallback execution once AST is prepared
|
||||
fn execute_vm_fallback_from_ast(&self, filename: &str, ast: nyash_rust::ast::ASTNode) {
|
||||
#[allow(dead_code)]
|
||||
fn execute_vm_fallback_from_ast(&self, _filename: &str, ast: nyash_rust::ast::ASTNode) {
|
||||
use crate::{
|
||||
backend::MirInterpreter,
|
||||
box_factory::{BoxFactory, RuntimeError},
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::runner::child_env;
|
||||
|
||||
impl NyashRunner {
|
||||
/// Try to handle `--ny-parser-pipe` / `--json-file` flow.
|
||||
@ -20,7 +19,7 @@ impl NyashRunner {
|
||||
if !(groups.parser.ny_parser_pipe || groups.parser.json_file.is_some()) {
|
||||
return false;
|
||||
}
|
||||
let mut json = if let Some(path) = &groups.parser.json_file {
|
||||
let json = if let Some(path) = &groups.parser.json_file {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
|
||||
@ -84,6 +84,7 @@ impl NyashRunner {
|
||||
}
|
||||
|
||||
/// Suggest candidate files by leaf name within limited bases (apps/lib/.)
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn suggest_in_base(base: &str, leaf: &str, out: &mut Vec<String>) {
|
||||
use std::fs;
|
||||
fn walk(dir: &std::path::Path, leaf: &str, out: &mut Vec<String>, depth: usize) {
|
||||
|
||||
@ -58,7 +58,7 @@ impl NyashRunner {
|
||||
for p in list {
|
||||
if list_only { println!(" • {}", p); continue; }
|
||||
match std::fs::read_to_string(&p) {
|
||||
Ok(code) => {
|
||||
Ok(_code) => {
|
||||
// Legacy interpreter removed - ny_plugins execution disabled
|
||||
println!("[ny_plugins] {}: SKIP (legacy interpreter removed)", p);
|
||||
}
|
||||
|
||||
@ -403,21 +403,22 @@ impl NyashRunner {
|
||||
// パイプライン(tools/ny_selfhost_inline.sh など)を使う想定とし、ここでは常に Rust 既定
|
||||
// パスへフォールバックする。
|
||||
crate::cli_v!("[ny-compiler] inline selfhost pipeline disabled (Phase 25.1b); falling back to default path");
|
||||
return false;
|
||||
|
||||
match super::json_v0_bridge::parse_json_v0_to_module("") {
|
||||
Ok(module) => {
|
||||
if crate::config::env::cli_verbose() {
|
||||
// Dev-only escape hatch: allow forcing the old inline path when explicitly requested.
|
||||
if std::env::var("NYASH_SELFHOST_INLINE_FORCE").ok().as_deref() == Some("1") {
|
||||
match super::json_v0_bridge::parse_json_v0_to_module("") {
|
||||
Ok(module) => {
|
||||
if crate::config::env::cli_verbose() {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
if crate::config::env::cli_verbose() {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
}
|
||||
}
|
||||
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY")
|
||||
.unwrap_or_else(|_| "1".to_string())
|
||||
== "1";
|
||||
if emit_only {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY")
|
||||
.unwrap_or_else(|_| "1".to_string())
|
||||
== "1";
|
||||
if emit_only {
|
||||
return false;
|
||||
}
|
||||
// Phase-15 policy: when NYASH_VM_USE_PY=1, prefer PyVM as reference executor
|
||||
// regardless of BoxCall presence to ensure semantics parity (e.g., PHI merges).
|
||||
let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1");
|
||||
@ -430,25 +431,29 @@ impl NyashRunner {
|
||||
})
|
||||
})
|
||||
});
|
||||
if prefer_pyvm || needs_pyvm {
|
||||
let label = if prefer_pyvm { "selfhost" } else { "selfhost-fallback" };
|
||||
if let Some(code) = crate::runner::modes::common_util::selfhost::json::run_pyvm_module(&module, label) {
|
||||
println!("Result: {}", code);
|
||||
std::process::exit(code);
|
||||
if prefer_pyvm || needs_pyvm {
|
||||
let label = if prefer_pyvm { "selfhost" } else { "selfhost-fallback" };
|
||||
if let Some(code) = crate::runner::modes::common_util::selfhost::json::run_pyvm_module(&module, label) {
|
||||
println!("Result: {}", code);
|
||||
std::process::exit(code);
|
||||
}
|
||||
}
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
self.execute_mir_module(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
eprintln!("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
self.execute_mir_module(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
eprintln!("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
std::process::exit(1);
|
||||
Err(e) => {
|
||||
eprintln!("❌ JSON v0 bridge error: {}", e);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ JSON v0 bridge error: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Default path: always fall back to existing Rust runner.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use super::library;
|
||||
use super::PluginLoaderV2;
|
||||
use crate::bid::{BidError, BidResult};
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ mod specs;
|
||||
mod util;
|
||||
|
||||
use super::host_bridge::BoxInvokeFn;
|
||||
use super::types::{LoadedPluginV2, PluginBoxMetadata, PluginBoxV2, PluginHandleInner};
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use super::types::{LoadedPluginV2, PluginBoxMetadata, PluginHandleInner};
|
||||
use crate::bid::BidResult;
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::config::nyash_toml_v2::{LibraryDefinition, NyashConfigV2};
|
||||
use specs::LoadedBoxSpec;
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use super::specs;
|
||||
use super::PluginLoaderV2;
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::runtime::plugin_loader_v2::enabled::{errors, host_bridge, types};
|
||||
|
||||
@ -18,6 +18,7 @@ pub(crate) struct LoadedBoxSpec {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct MethodSpec {
|
||||
pub(crate) method_id: u32,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) returns_result: bool,
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::runtime::plugin_loader_v2::enabled::PluginLoaderV2;
|
||||
use std::collections::HashMap;
|
||||
|
||||
impl PluginLoaderV2 {
|
||||
/// Resolve a method ID for a given box type and method name
|
||||
@ -37,7 +36,7 @@ impl PluginLoaderV2 {
|
||||
// Fallback to TypeBox FFI spec
|
||||
if let Ok(map) = self.box_specs.read() {
|
||||
// Try direct lookup first
|
||||
for ((lib, bt), spec) in map.iter() {
|
||||
for ((_lib, bt), spec) in map.iter() {
|
||||
if bt == box_type {
|
||||
// Check methods map
|
||||
if let Some(ms) = spec.methods.get(method_name) {
|
||||
@ -47,7 +46,7 @@ impl PluginLoaderV2 {
|
||||
// Try resolve function
|
||||
if let Some(res_fn) = spec.resolve_fn {
|
||||
if let Ok(cstr) = std::ffi::CString::new(method_name) {
|
||||
let mid = unsafe { res_fn(cstr.as_ptr()) };
|
||||
let mid = res_fn(cstr.as_ptr());
|
||||
if mid != 0 {
|
||||
return Ok(mid);
|
||||
}
|
||||
@ -130,11 +129,13 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
|
||||
/// Helper functions for method resolution
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn is_special_method(method_name: &str) -> bool {
|
||||
matches!(method_name, "birth" | "fini" | "toString")
|
||||
}
|
||||
|
||||
/// Get default method IDs for special methods
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn get_special_method_id(method_name: &str) -> Option<u32> {
|
||||
match method_name {
|
||||
"birth" => Some(1),
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
use crate::box_factory::builtin::BuiltinBoxFactory;
|
||||
#[cfg(feature = "plugins")]
|
||||
use crate::box_factory::plugin::PluginBoxFactory;
|
||||
use crate::box_factory::{UnifiedBoxRegistry, FactoryPolicy};
|
||||
use crate::box_factory::UnifiedBoxRegistry;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
/// Global registry instance
|
||||
|
||||
Reference in New Issue
Block a user