refactor(funcscanner): Region+next_i パターン統一 & SSA テスト追加
**FuncScanner .hako 側改善**: - scan_all_boxes を Region + next_i 形式に統一(continue 多発による SSA/PHI 複雑さ削減) - インデント修正(タブ→スペース統一) - デバッグ print 削除 **SSA テスト追加**: - lang/src/compiler/tests/funcscanner_scan_methods_min.hako - src/tests/mir_funcscanner_ssa.rs (scan_methods & fib_min SSA デバッグテスト) **Phase 25.3 ドキュメント**: - docs/development/roadmap/phases/phase-25.3-funcscanner/ 追加 **関連**: Phase 25.3 FuncScanner 箱化準備作業 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -268,12 +268,13 @@
|
||||
次にどの箱から攻めるか決めたら、ここに箇条書きで足していこうね。
|
||||
|
||||
|
||||
## 2-? Stage‑B FuncScanner / LoopSnapshotMergeBox(in progress)
|
||||
## 2-? Stage‑B FuncScanner / LoopSnapshotMergeBox / MIR ロガー(in progress)
|
||||
|
||||
- StageBFuncScannerBox を `compiler_stageb.hako` 内に追加し、StageBDriverBox.main からは FuncScannerBox ではなくこちらを呼ぶ構成にしている。
|
||||
- VM 側の Undefined value(BreakFinderBox._find_loops/2)は LoopForm v2 / continue + PHI 修正で解消済み。
|
||||
- FuncScannerBox.scan_all_boxes/1 については、Phase 25.2 で `LoopSnapshotMergeBox` を導入し、continue/break/exit スナップショットのマージを一元化したことで
|
||||
13 個の continue を含む複雑ループでも Undefined Value が出ないところまで到達した。
|
||||
13 個の continue を含む複雑ループでも Undefined Value が出ないところまで到達しており、
|
||||
さらに `lang/src/compiler/tests/funcscanner_fib_min.hako` を使った最小ハーネスでも `NYASH_VM_VERIFY_MIR=1` で Undefined Value が再現しないところまで確認済み。
|
||||
- LoopSnapshotMergeBox 実装: `src/mir/phi_core/loop_snapshot_merge.rs`
|
||||
- `merge_continue_for_header` / `merge_exit` / `optimize_same_value` / `sanitize_inputs` など、ヘッダ/exit PHI 用の入力統合ロジックを提供。
|
||||
- LoopBuilder / LoopFormBuilder は、この箱を経由して continue_merge / exit PHI を構成するようにリファクタ済み。
|
||||
@ -285,7 +286,18 @@
|
||||
- body-local 変数を含むループ: result=6, i=3 ✅(exit PHI で body-local を正しく統合)
|
||||
- StageBFuncScannerBox.scan_all_boxes(src) は依然として fib サンプルに対して defs=[](Program(JSON) に `TestBox.fib` が載っていない)という問題が残っているため、
|
||||
次の担当者は StageBFuncScannerBox.scan_all_boxes/1 と FuncScannerBox.scan_all_boxes/1 の MIR を比較しつつ、
|
||||
Stage‑B body 抽出 → FuncScanner → defs 注入の導線自体を見直してほしい(LoopForm/PHI/スナップショット側は LoopSnapshotMergeBox でほぼ安定化済み)。
|
||||
Stage‑B body 抽出 → StageBFuncScannerBox.scan_all_boxes → Program(JSON v0).defs 注入の導線自体を見直してほしい(LoopForm/PHI/スナップショット側は LoopSnapshotMergeBox で安定化済み)。
|
||||
- Phase 25.3 では、FuncScanner / Stage‑B ラインの観測専用として `.hako` から直接 MIR ログを埋め込める `__mir__` 構文を導入した。
|
||||
- 構文: `__mir__.log("label", v1, v2, ...)` / `__mir__.mark("label")`
|
||||
- lowering: `MirInstruction::DebugLog { message: "label", values: [v1, v2, ...] }` に変換される(LoopForm/PHI には影響しない)。
|
||||
- 実行: `NYASH_MIR_DEBUG_LOG=1` のときだけ `[MIR-LOG] label: %id=value ...` を VM 側で出力し、それ以外は no-op。
|
||||
- 実装ポイント: `src/mir/builder/calls/build.rs` の `build_method_call_impl` で `__mir__` receiver を検出し、`try_build_mir_debug_call` で `DebugLog` 命令に directly lowering している。
|
||||
- これにより、FuncScannerBox.scan_all_boxes や StageBFuncScannerBox.scan_all_boxes のループ頭や `box` 検出位置にラベルを打ち、
|
||||
VM 実行ログと合わせて「どの経路で未初期化の値が残っているか」を .hako 側から追いやすくなった。
|
||||
- 併せて `MirBuilder::handle_me_method_call` を `MeCallPolicyBox` で箱化し、me-call のフォールバックを静的メソッド降下に統一した。
|
||||
- `Box.method/Arity` lowered 関数が存在する場合は従来どおり `me` を先頭引数にした Global call。
|
||||
- 存在しない場合は `handle_static_method_call(cls, method, arguments)` に委譲し、static helper(FuncScannerBox._parse_params / _strip_comments など)呼び出しとして扱う。
|
||||
- これにより static box 文脈で「実体のない me を receiver にした Method call」が生成される経路が閉じられ、FuncScannerBox._scan_methods/4 系の UndefinedValue は Rust 側で構造的に防止されている。
|
||||
|
||||
|
||||
## 3. Static Box フィールド仕様ドキュメント(完了)
|
||||
|
||||
151
docs/development/roadmap/phases/phase-25.3-funcscanner/README.md
Normal file
151
docs/development/roadmap/phases/phase-25.3-funcscanner/README.md
Normal file
@ -0,0 +1,151 @@
|
||||
# Phase 25.3 — FuncScanner / Stage‑B defs 安定化
|
||||
|
||||
## スコープ / ゴール
|
||||
|
||||
- 対象レイヤ
|
||||
- `.hako` 側:
|
||||
- `lang/src/compiler/entry/func_scanner.hako` (`FuncScannerBox`)
|
||||
- `lang/src/compiler/entry/compiler_stageb.hako` (`StageBFuncScannerBox`, `StageBDriverBox`)
|
||||
- 必要に応じて `lang/src/compiler/tests/funcscanner_fib_min.hako` などのテスト用ハーネス
|
||||
- Rust 側:
|
||||
- 既存の LoopForm v2 / LoopSnapshotMergeBox / JSON front は「完成済みの土台」として利用するだけで、原則ここでは触らない。
|
||||
|
||||
- ゴール
|
||||
- Rust VM 検証付きで FuncScanner を安定させる:
|
||||
- `NYASH_VM_VERIFY_MIR=1` で `lang/src/compiler/tests/funcscanner_fib_min.hako` を実行したときに、
|
||||
- `FuncScannerBox.scan_all_boxes/1` で Undefined value が発生しないこと。
|
||||
- fib 風ソースに対して `defs` に `TestBox.fib` / `Main.main` が含まれること(箱レベルの振る舞いが安定)。
|
||||
- Stage‑B 側からも同じ結果が得られる:
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh` を実行すると、
|
||||
- `defs` に `TestBox.fib` が存在し、
|
||||
- その `body` に `Loop` ノードが含まれていること。
|
||||
- Loop/PHI の意味論は **LoopFormBuilder + LoopSnapshotMergeBox** に完全委譲したまま、
|
||||
- FuncScanner / Stage‑B は「テキストスキャン+JSON 組み立て」の箱として綺麗に分離された状態にする。
|
||||
|
||||
## すでに前提としている状態
|
||||
|
||||
- LoopForm v2 / PHI / snapshot:
|
||||
- ループ構造・PHI・break/continue スナップショットの意味論は、
|
||||
- `src/mir/loop_builder.rs`
|
||||
- `src/mir/phi_core/loopform_builder.rs`
|
||||
- `src/mir/phi_core/loop_snapshot_merge.rs`
|
||||
に集約されており、AST ルート / JSON ルートともに同じ実装を使っている。
|
||||
- canonical `continue_merge` ブロック(ループごとに 1 つ)が導入済みで、backedge は
|
||||
- `latch` → `header`
|
||||
- `continue_merge` → `header`
|
||||
の 2 本に限定されている。
|
||||
|
||||
- JSON v0 front:
|
||||
- `src/runner/json_v0_bridge/lowering/loop_.rs` は LoopForm v2 の **薄いアダプタ**になっている。
|
||||
- ブロック ID の確保
|
||||
- `vars` / snapshot の受け渡し
|
||||
- `LoopFormOps` 実装 (`LoopFormJsonOps`)
|
||||
だけを担い、PHI 生成は LoopForm v2 に一元化された。
|
||||
|
||||
- 25.1e / 25.1q / 25.2:
|
||||
- 変数スコープ(Carrier / Pinned / Invariant / BodyLocalInOut)と Env_in/Env_out モデルは文書と実装が一致している。
|
||||
- JSON ループ用のスモーク(`tests/json_program_loop.rs`)はすでに緑で、ループ+break/continue+body‑local exit に対して MIR 検証が通っている。
|
||||
|
||||
この Phase 25.3 では、「LoopForm / JSON front は触らずに、FuncScanner / Stage‑B ラインをその上に綺麗に載せる」ことが目的になる。
|
||||
|
||||
## タスク一覧
|
||||
|
||||
### 1. FuncScanner fib 最小ハーネス(SSA バグの確認完了)
|
||||
|
||||
- 対象:
|
||||
- `lang/src/compiler/tests/funcscanner_fib_min.hako`
|
||||
- `FuncScannerBox.scan_all_boxes/1`(Rust lowering 時の MIR)
|
||||
|
||||
- 現状:
|
||||
- `NYASH_VM_VERIFY_MIR=1` 付きで `funcscanner_fib_min.hako` を実行しても、
|
||||
Undefined value / `ssa-undef-debug` は発生していない(LoopForm v2 / LoopSnapshotMergeBox 修正で解消済み)。
|
||||
- `cargo test mir_funcscanner_fib_min_ssa_debug` も緑で、FuncScanner 単体の SSA 破綻は再現しない。
|
||||
- このフェーズで導入した `__mir__` ロガーは、今後の再現時に経路観測用フックとして利用できる状態になっている。
|
||||
|
||||
- 位置づけ:
|
||||
- 「FuncScanner + LoopForm v2 での Undefined Value バグの根治」は完了とみなし、
|
||||
以降は Stage‑B 側の defs 欠落(`defs=[]`)を主ターゲットとする。
|
||||
|
||||
- 追加の SSA ガード(me-call 周り):
|
||||
- Rust 側では `MirBuilder::handle_me_method_call` を `MeCallPolicyBox` によって箱化し、
|
||||
- `Box.method/Arity` lowered 関数が存在する場合は、従来どおり `me` を先頭引数とする Global call(インスタンス文脈の me-call)。
|
||||
- 存在しない場合は、`handle_static_method_call(cls, method, arguments)` にフォールバックし、
|
||||
static helper(`FuncScannerBox._parse_params` / `_strip_comments` など)への呼び出しとして扱うようにした。
|
||||
- これにより、static box 文脈で「実体のない me を receiver とする Method call」が生成される経路が閉じられ、
|
||||
FuncScannerBox._scan_methods/4 + `_parse_params` + `_strip_comments` のラインは SSA 的に安全になっている。
|
||||
|
||||
### 2.5. MIR ロガー (__mir__) による観測(Dev 専用)
|
||||
|
||||
- 位置づけ:
|
||||
- Phase 25.3 では、FuncScanner / Stage‑B のような `.hako` 側ロジックを LoopForm v2 上でデバッグしやすくするため、
|
||||
専用の MIR ログ構文 `__mir__` を導入する(実行意味論には影響しない dev 専用 Hook)。
|
||||
- 構文:
|
||||
- `__mir__.log("label", v1, v2, ...)`
|
||||
- lowering 時に `MirInstruction::DebugLog { message: "label", values: [v1, v2, ...] }` へ変換される。
|
||||
- `__mir__.mark("label")`
|
||||
- 値無し版の `DebugLog` として `debug_log "label"` だけを差し込む。
|
||||
- 振る舞い:
|
||||
- VM 実行時は `NYASH_MIR_DEBUG_LOG=1` のときだけ
|
||||
- `[MIR-LOG] label: %id=value ...`
|
||||
の形式でログ出力され、それ以外のときは完全に無視される(Effect::Debug のみ)。
|
||||
- LoopForm / PHI / snapshot には関与せず、単に MIR に 1 命令追加するだけの観測レイヤ。
|
||||
- 実装ポイント:
|
||||
- `src/mir/builder/calls/build.rs` 内の `build_method_call_impl` で、
|
||||
- receiver が `__mir__` の `__mir__.log/mark` を検出し、
|
||||
- `try_build_mir_debug_call` で `DebugLog` 命令に直接 lowering している。
|
||||
|
||||
FuncScanner / Stage‑B のデバッグ時には、`scan_all_boxes` のループ頭や `box` 検出直後に
|
||||
`__mir__.log("funcscan/head", i, in_str, in_block)` などを埋め込み、VM 実行ログと合わせて
|
||||
「どの経路で環境スナップショットやステートが崩れているか」を観測する想定だよ。
|
||||
|
||||
### 3. Stage‑B FuncScanner との整合性確保(主ターゲット)
|
||||
|
||||
- 対象:
|
||||
- `lang/src/compiler/entry/compiler_stageb.hako`
|
||||
- `StageBFuncScannerBox.scan_all_boxes`
|
||||
- `StageBFuncScannerBox._find_matching_brace` 他
|
||||
- `StageBDriverBox.main` 内の defs 組み立てロジック
|
||||
|
||||
- やること:
|
||||
- `StageBFuncScannerBox` のロジックを、可能な範囲で `FuncScannerBox` と共有・委譲する方向に寄せる。
|
||||
- `_find_matching_brace` などはすでに FuncScannerBox に委譲済みなので、
|
||||
残りのスキャンロジックも「同じアルゴリズム / 同じ境界条件」で動くように整理する。
|
||||
- `HAKO_STAGEB_FUNCSCAN_TEST=1` で `StageBFuncScannerBox.test_fib_scan()` を走らせ、
|
||||
- `brace1/brace2` の close_idx が正しく取れること。
|
||||
- `defs_len > 0` となり、`TestBox.fib` / `Main.main` がログに出ること。
|
||||
- そのうえで `StageBDriverBox.main` が `StageBFuncScannerBox.scan_all_boxes(src)` の結果を
|
||||
- Program(JSON v0).defs に正しく注入できているかを確認する。
|
||||
- ここでの主なバグは「SSA ではなく、Stage‑B が fib ソースから defs を組み立てきれていないこと」なので、
|
||||
ループ構造や LoopForm には手を入れず、Stage‑B 側のテキスト処理と defs 生成パスを中心に見る。
|
||||
|
||||
### 4. fib defs canary & ドキュメント更新
|
||||
|
||||
- 対象:
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh`
|
||||
- `docs/development/roadmap/phases/phase-25.1q/README.md`
|
||||
- `CURRENT_TASK.md`
|
||||
|
||||
- やること:
|
||||
- canary スクリプトで:
|
||||
- `Program.kind == "Program"`
|
||||
- `defs` に `TestBox.fib` が存在すること。
|
||||
- `TestBox.fib.body` に `Loop` ノードが含まれること。
|
||||
を満たした状態で `rc=0` になるまで確認する。
|
||||
- Phase 25.1q / 25.2 の README には、
|
||||
- 「loop/PHI/スナップショットの SSOT は LoopForm v2 + LoopSnapshotMergeBox」
|
||||
- 「Phase 25.3 で FuncScanner / Stage‑B defs もこの土台の上に載せた」
|
||||
というつながりを 1〜2 行で追記する。
|
||||
- `CURRENT_TASK.md` には:
|
||||
- Phase 25.3 のタスク完了状況(FuncScanner fib / Stage‑B fib canary 緑)と、
|
||||
- その後に繋がる Stage‑1 UsingResolver / Stage‑1 CLI self‑host ラインへのブリッジ
|
||||
を短く整理しておく。
|
||||
|
||||
---
|
||||
|
||||
この Phase 25.3 は、
|
||||
|
||||
- LoopForm v2 / JSON front を「箱として完成済み」とみなし、
|
||||
- その上で FuncScanner / Stage‑B defs ラインを **構造的に**安定させる
|
||||
|
||||
ための仕上げフェーズだよ。
|
||||
ループや PHI の意味論は触らず、テキストスキャンとスコープ設計を整えることで、self‑hosting ライン全体の足場を固めるのが狙い。+
|
||||
@ -12,90 +12,91 @@ static box FuncScannerBox {
|
||||
return me._scan_methods(source, box_name, 1, 0)
|
||||
}
|
||||
|
||||
// Scan all static box definitions and collect their methods.
|
||||
method scan_all_boxes(source) {
|
||||
// source が null の場合はスキャン対象がないので空配列を返すよ。
|
||||
if source == null { return new ArrayBox() }
|
||||
local defs = new ArrayBox()
|
||||
local s = "" + source
|
||||
// Scan all static box definitions and collect their methods.
|
||||
method scan_all_boxes(source) {
|
||||
// source が null の場合はスキャン対象がないので空配列を返すよ。
|
||||
if source == null { return new ArrayBox() }
|
||||
local defs = new ArrayBox()
|
||||
local s = "" + source
|
||||
local n = s.length()
|
||||
// DEBUG: 一時的に常時トレースを有効化(Stage‑B fib 用の挙動観測)
|
||||
print("[funcscan/scan_all_boxes] source_len=" + ("" + n))
|
||||
local i = 0
|
||||
local in_str = 0
|
||||
local esc = 0
|
||||
local in_line = 0
|
||||
local in_block = 0
|
||||
|
||||
// Region + next_i 形式に統一して、ループ本体での多重 continue による
|
||||
// SSA/PHI の複雑さを下げる。
|
||||
loop(i < n) {
|
||||
local next_i = i + 1
|
||||
local ch = s.substring(i, i + 1)
|
||||
|
||||
if in_line == 1 {
|
||||
if ch == "\n" { in_line = 0 }
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
if in_block == 1 {
|
||||
} else if in_block == 1 {
|
||||
if ch == "*" && i + 1 < n && s.substring(i + 1, i + 2) == "/" {
|
||||
in_block = 0
|
||||
i = i + 2
|
||||
continue
|
||||
next_i = i + 2
|
||||
}
|
||||
} else if in_str == 1 {
|
||||
if esc == 1 {
|
||||
esc = 0
|
||||
} else if ch == "\\" {
|
||||
esc = 1
|
||||
} else if ch == "\"" {
|
||||
in_str = 0
|
||||
}
|
||||
} else {
|
||||
if ch == "\"" {
|
||||
in_str = 1
|
||||
} else if ch == "/" && i + 1 < n {
|
||||
local ch2 = s.substring(i + 1, i + 2)
|
||||
if ch2 == "/" {
|
||||
in_line = 1
|
||||
next_i = i + 2
|
||||
} else if ch2 == "*" {
|
||||
in_block = 1
|
||||
next_i = i + 2
|
||||
}
|
||||
} else {
|
||||
if i + 3 <= n && s.substring(i, i + 3) == "box" && me._kw_boundary_before(s, i) == 1 && me._kw_boundary_after(s, i + 3) == 1 {
|
||||
local cursor = i + 3
|
||||
cursor = me._skip_whitespace(s, cursor)
|
||||
local name_start = cursor
|
||||
loop(cursor < n) {
|
||||
local ch_name = s.substring(cursor, cursor + 1)
|
||||
if me._is_ident_char(ch_name) == 1 { cursor = cursor + 1 } else { break }
|
||||
}
|
||||
local box_name = s.substring(name_start, cursor)
|
||||
if box_name == "" {
|
||||
next_i = cursor
|
||||
} else {
|
||||
cursor = me._skip_whitespace(s, cursor)
|
||||
if cursor >= n || s.substring(cursor, cursor + 1) != "{" {
|
||||
next_i = cursor
|
||||
} else {
|
||||
local close_idx = me._find_matching_brace(s, cursor)
|
||||
if close_idx < 0 {
|
||||
// マッチしない場合はこれ以上スキャンしても意味がないのでループ終了。
|
||||
i = n
|
||||
next_i = n
|
||||
} else {
|
||||
local body = s.substring(cursor + 1, close_idx)
|
||||
local skip_main = 0
|
||||
if box_name == "Main" { skip_main = 1 }
|
||||
local include_me = 0
|
||||
if box_name != "Main" { include_me = 1 }
|
||||
local defs_box = me._scan_methods(body, box_name, skip_main, include_me)
|
||||
me._append_defs(defs, defs_box)
|
||||
next_i = close_idx + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
if in_str == 1 {
|
||||
if esc == 1 { esc = 0 i = i + 1 continue }
|
||||
if ch == "\\" { esc = 1 i = i + 1 continue }
|
||||
if ch == "\"" { in_str = 0 i = i + 1 continue }
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
if ch == "\"" { in_str = 1 i = i + 1 continue }
|
||||
if ch == "/" && i + 1 < n {
|
||||
local ch2 = s.substring(i + 1, i + 2)
|
||||
if ch2 == "/" { in_line = 1 i = i + 2 continue }
|
||||
if ch2 == "*" { in_block = 1 i = i + 2 continue }
|
||||
}
|
||||
|
||||
// DEBUG: "box" キーワード候補をチェック(Stage‑B fib 用)
|
||||
if i + 3 <= n {
|
||||
local candidate = s.substring(i, i + 3)
|
||||
if candidate == "box" {
|
||||
print("[funcscan/box_candidate] i=" + ("" + i) + " candidate=" + candidate)
|
||||
}
|
||||
}
|
||||
if s.substring(i, i + 3) == "box" && me._kw_boundary_before(s, i) == 1 && me._kw_boundary_after(s, i + 3) == 1 {
|
||||
local cursor = i + 3
|
||||
cursor = me._skip_whitespace(s, cursor)
|
||||
local name_start = cursor
|
||||
loop(cursor < n) {
|
||||
local ch_name = s.substring(cursor, cursor + 1)
|
||||
if me._is_ident_char(ch_name) == 1 { cursor = cursor + 1 } else { break }
|
||||
}
|
||||
local box_name = s.substring(name_start, cursor)
|
||||
if box_name == "" {
|
||||
i = cursor
|
||||
continue
|
||||
}
|
||||
cursor = me._skip_whitespace(s, cursor)
|
||||
if cursor >= n || s.substring(cursor, cursor + 1) != "{" {
|
||||
i = cursor
|
||||
continue
|
||||
}
|
||||
local close_idx = me._find_matching_brace(s, cursor)
|
||||
if close_idx < 0 { break }
|
||||
local body = s.substring(cursor + 1, close_idx)
|
||||
local skip_main = 0
|
||||
if box_name == "Main" { skip_main = 1 }
|
||||
local include_me = 0
|
||||
if box_name != "Main" { include_me = 1 }
|
||||
local defs_box = me._scan_methods(body, box_name, skip_main, include_me)
|
||||
me._append_defs(defs, defs_box)
|
||||
i = close_idx + 1
|
||||
continue
|
||||
}
|
||||
|
||||
i = i + 1
|
||||
i = next_i
|
||||
}
|
||||
return defs
|
||||
}
|
||||
@ -164,7 +165,8 @@ static box FuncScannerBox {
|
||||
|
||||
// Extract params (minimal: comma-separated names)
|
||||
local params_str = s.substring(lparen + 1, rparen)
|
||||
local params = me._parse_params(params_str)
|
||||
// static helper として扱う(me ではなく FuncScannerBox に直接委譲)。
|
||||
local params = FuncScannerBox._parse_params(params_str)
|
||||
if include_me == 1 {
|
||||
local first = ""
|
||||
if params.length() > 0 { first = "" + params.get(0) }
|
||||
@ -235,8 +237,9 @@ static box FuncScannerBox {
|
||||
// Extract method body (inside braces)
|
||||
local method_body = s.substring(lbrace + 1, rbrace)
|
||||
|
||||
method_body = me._strip_comments(method_body)
|
||||
method_body = me._trim(method_body)
|
||||
// コメント除去と trim も static helper として扱う。
|
||||
method_body = FuncScannerBox._strip_comments(method_body)
|
||||
method_body = FuncScannerBox._trim(method_body)
|
||||
|
||||
// Parse method body to JSON (statement list)
|
||||
local body_json = null
|
||||
@ -304,14 +307,18 @@ static box FuncScannerBox {
|
||||
|
||||
method _find_matching_brace(s, open_idx) {
|
||||
local n = s.length()
|
||||
local i = open_idx
|
||||
if open_idx < 0 || open_idx >= n { return -1 }
|
||||
// open_idx 以降だけを対象に走査することで、前方の構造に依存しないようにする。
|
||||
local subs = s.substring(open_idx, n)
|
||||
local m = subs.length()
|
||||
local i = 0
|
||||
local depth = 0
|
||||
local in_str = 0
|
||||
local esc = 0
|
||||
local in_line = 0
|
||||
local in_block = 0
|
||||
loop(i < n) {
|
||||
local ch = s.substring(i, i + 1)
|
||||
loop(i < m) {
|
||||
local ch = subs.substring(i, i + 1)
|
||||
if in_line == 1 {
|
||||
if ch == "\n" { in_line = 0 }
|
||||
i = i + 1
|
||||
@ -334,8 +341,8 @@ static box FuncScannerBox {
|
||||
continue
|
||||
}
|
||||
if ch == "\"" { in_str = 1 i = i + 1 continue }
|
||||
if ch == "/" && i + 1 < n {
|
||||
local ch2 = s.substring(i + 1, i + 2)
|
||||
if ch == "/" && i + 1 < m {
|
||||
local ch2 = subs.substring(i + 1, i + 2)
|
||||
if ch2 == "/" { in_line = 1 i = i + 2 continue }
|
||||
if ch2 == "*" { in_block = 1 i = i + 2 continue }
|
||||
}
|
||||
@ -347,7 +354,7 @@ static box FuncScannerBox {
|
||||
if ch == "}" {
|
||||
depth = depth - 1
|
||||
i = i + 1
|
||||
if depth == 0 { return i - 1 }
|
||||
if depth == 0 { return open_idx + i - 1 }
|
||||
continue
|
||||
}
|
||||
i = i + 1
|
||||
|
||||
43
lang/src/compiler/tests/funcscanner_scan_methods_min.hako
Normal file
43
lang/src/compiler/tests/funcscanner_scan_methods_min.hako
Normal file
@ -0,0 +1,43 @@
|
||||
// funcscanner_scan_methods_min.hako — FuncScannerBox._scan_methods minimal harness
|
||||
//
|
||||
// 目的:
|
||||
// - static box TestBox/Main を含むソースから TestBox.body 部分だけを切り出し、
|
||||
// FuncScannerBox._scan_methods(source, "TestBox", 0, 1) を直接呼び出す。
|
||||
// - params 部分のパースと _parse_params/_trim の呼び出しが SSA 的に安全であることを確認する。
|
||||
//
|
||||
// 振る舞い:
|
||||
// - fib と同等の TestBox/Main を src に埋め込む。
|
||||
// - FuncScannerBox._find_matching_brace で TestBox の '{'〜'}' 範囲を取得。
|
||||
// - 本文だけを body1 として抜き出し、FuncScannerBox._scan_methods に渡す。
|
||||
// - defs.len と各定義の box/name をログに出力する(Stage‑B 側と比較しやすくする)。
|
||||
|
||||
using lang.compiler.entry.func_scanner as FuncScannerBox
|
||||
|
||||
static box Main {
|
||||
method main(args) {
|
||||
local src = "static box TestBox {\n method fib(n) {\n local i = 0\n local a = 0\n local b = 1\n loop(i < n) {\n local t = a + b\n a = b\n b = t\n i = i + 1\n }\n return b\n }\n}\n\nstatic box Main {\n method main(args) {\n local t = new TestBox()\n return t.fib(6)\n }\n}\n"
|
||||
|
||||
// TestBox 本体の brace 範囲を FuncScannerBox の実装で検出
|
||||
local open1 = 19
|
||||
local close1 = FuncScannerBox._find_matching_brace(src, open1)
|
||||
print("[scan_methods] brace1 open=" + ("" + open1) + " close=" + ("" + close1))
|
||||
if close1 < 0 { return 1 }
|
||||
|
||||
// "box TestBox {" の直後から close1 直前までを body1 として切り出す
|
||||
local body1 = src.substring(open1 + 1, close1)
|
||||
print("[scan_methods] body1_len=" + ("" + body1.length()))
|
||||
|
||||
// FuncScannerBox._scan_methods を直接呼び出してメソッド定義を抽出
|
||||
local defs1 = FuncScannerBox._scan_methods(body1, "TestBox", 0, 1)
|
||||
local n1 = defs1.length()
|
||||
print("[scan_methods] defs1.len=" + ("" + n1))
|
||||
local i = 0
|
||||
loop(i < n1) {
|
||||
local d = defs1.get(i)
|
||||
print("[scan_methods] def1[" + ("" + i) + "] box=" + ("" + d.get("box")) + " name=" + ("" + d.get("name")))
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -533,7 +533,7 @@ impl MirBuilder {
|
||||
};
|
||||
instruction = MirInstruction::Call {
|
||||
dst: *dst,
|
||||
func: crate::mir::ValueId::new(0), // Legacy compatibility
|
||||
func: crate::mir::ValueId::INVALID, // Legacy dummy (not a real SSA use)
|
||||
callee: Some(new_callee),
|
||||
args: args.clone(),
|
||||
effects: *effects,
|
||||
|
||||
@ -99,6 +99,15 @@ impl MirBuilder {
|
||||
eprintln!("[builder] method-call object kind={} method={}", kind, method);
|
||||
}
|
||||
|
||||
// 0. Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog へ直接 lowering
|
||||
if let ASTNode::Variable { name: obj_name, .. } = &object {
|
||||
if obj_name == "__mir__" {
|
||||
if let Some(result) = self.try_build_mir_debug_call(&method, &arguments)? {
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Static box method call: BoxName.method(args)
|
||||
if let ASTNode::Variable { name: obj_name, .. } = &object {
|
||||
if let Some(result) = self.try_build_static_method_call(obj_name, &method, &arguments)? {
|
||||
@ -407,6 +416,58 @@ impl MirBuilder {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Dev-only: __mir__.log / __mir__.mark → MirInstruction::DebugLog への変換
|
||||
///
|
||||
/// 構文:
|
||||
/// __mir__.log("label", v1, v2, ...)
|
||||
/// __mir__.mark("label")
|
||||
///
|
||||
/// - 第一引数は String リテラル想定(それ以外はこのハンドラをスキップして通常の解決に回す)。
|
||||
/// - 戻り値は Void 定数の ValueId(式コンテキストでも型破綻しないようにするため)。
|
||||
fn try_build_mir_debug_call(
|
||||
&mut self,
|
||||
method: &str,
|
||||
arguments: &[ASTNode],
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
if method != "log" && method != "mark" {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if arguments.is_empty() {
|
||||
return Err("__mir__.log/__mir__.mark requires at least a label argument".to_string());
|
||||
}
|
||||
|
||||
// 第一引数は String リテラルのみ対応(それ以外は通常経路にフォールバック)
|
||||
let label = match &arguments[0] {
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::String(s),
|
||||
..
|
||||
} => s.clone(),
|
||||
_ => {
|
||||
// ラベルがリテラルでない場合はこのハンドラをスキップし、通常の static box 解決に任せる
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
// 残りの引数を評価して ValueId を集める
|
||||
let mut values: Vec<ValueId> = Vec::new();
|
||||
if method == "log" {
|
||||
for arg in &arguments[1..] {
|
||||
values.push(self.build_expression(arg.clone())?);
|
||||
}
|
||||
}
|
||||
|
||||
// MIR に DebugLog 命令を 1 つ挿入(意味論は NYASH_MIR_DEBUG_LOG=1 のときにだけ効く)
|
||||
self.emit_instruction(MirInstruction::DebugLog {
|
||||
message: label,
|
||||
values,
|
||||
})?;
|
||||
|
||||
// 式コンテキスト用に Void 定数を返す(呼び出し元では通常使われない)
|
||||
let void_id = crate::mir::builder::emission::constant::emit_void(self);
|
||||
Ok(Some(void_id))
|
||||
}
|
||||
|
||||
/// Try static method fallback (name+arity)
|
||||
fn try_static_method_fallback(
|
||||
&mut self,
|
||||
|
||||
@ -201,7 +201,7 @@ impl UnifiedCallEmitterBox {
|
||||
// For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands)
|
||||
let legacy_call = MirInstruction::Call {
|
||||
dst: mir_call.dst,
|
||||
func: ValueId::new(0), // Dummy value for legacy compatibility
|
||||
func: ValueId::INVALID, // Dummy value for legacy compatibility (not a real SSA use)
|
||||
callee: Some(callee),
|
||||
args: args_local,
|
||||
effects: mir_call.effects,
|
||||
|
||||
@ -325,6 +325,12 @@ 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).
|
||||
//
|
||||
// NOTE:
|
||||
// - MirFunction::new() はシグネチャの params に応じて
|
||||
// [ValueId(0)..ValueId(param_count-1)] を事前に予約する。
|
||||
// - ここでは追加の next_value_id()/params.push() は行わず、
|
||||
// 予約済みのパラメータ集合をそのまま使う。
|
||||
if module.functions.get("condition_fn").is_none() {
|
||||
let sig = FunctionSignature {
|
||||
name: "condition_fn".to_string(),
|
||||
@ -334,9 +340,6 @@ impl super::MirBuilder {
|
||||
};
|
||||
let entry = BasicBlockId::new(0);
|
||||
let mut f = MirFunction::new(sig, entry);
|
||||
// parameter slot (unused in body)
|
||||
let _param = f.next_value_id();
|
||||
f.params.push(_param);
|
||||
// body: const 1; return it(FunctionEmissionBox を使用)
|
||||
let one = crate::mir::function_emission::emit_const_integer(&mut f, entry, 1);
|
||||
crate::mir::function_emission::emit_return_value(&mut f, entry, one);
|
||||
|
||||
@ -9,6 +9,69 @@ use crate::mir::builder::builder_calls::CallTarget;
|
||||
use crate::mir::{MirInstruction, TypeOpKind};
|
||||
use crate::mir::builder::calls::function_lowering;
|
||||
|
||||
/// Me-call 専用のポリシー箱。
|
||||
///
|
||||
/// - 責務:
|
||||
/// - me.method(...) を「インスタンス呼び出し」か「static メソッド呼び出し」か判定する。
|
||||
/// - static box 文脈で実体のない receiver を生まないように、静的メソッド降下にフォールバックする。
|
||||
struct MeCallPolicyBox;
|
||||
|
||||
impl MeCallPolicyBox {
|
||||
fn resolve_me_call(
|
||||
builder: &mut MirBuilder,
|
||||
method: &str,
|
||||
arguments: &[ASTNode],
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Instance box: prefer enclosing box method (lowered function) if存在
|
||||
let enclosing_cls: Option<String> = builder
|
||||
.current_function
|
||||
.as_ref()
|
||||
.and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string()));
|
||||
|
||||
if let Some(cls) = enclosing_cls.as_ref() {
|
||||
let mut arg_values = Vec::with_capacity(arguments.len());
|
||||
for a in arguments {
|
||||
arg_values.push(builder.build_expression(a.clone())?);
|
||||
}
|
||||
let arity = arg_values.len();
|
||||
let fname =
|
||||
function_lowering::generate_method_function_name(cls, method, arity);
|
||||
let exists = if let Some(ref module) = builder.current_module {
|
||||
module.functions.contains_key(&fname)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if exists {
|
||||
// Pass 'me' as first arg
|
||||
let me_id = builder.build_me_expression()?;
|
||||
let mut call_args = Vec::with_capacity(arity + 1);
|
||||
call_args.push(me_id);
|
||||
call_args.extend(arg_values.into_iter());
|
||||
let dst = builder.next_value_id();
|
||||
// Emit as unified global call to lowered function
|
||||
builder.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
)?;
|
||||
builder.annotate_call_result_from_func_name(dst, &fname);
|
||||
return Ok(Some(dst));
|
||||
}
|
||||
|
||||
// Fallback: treat me.method(...) as a static method on the enclosing box.
|
||||
// - 旧挙動では me を Box 値として Method 呼び出ししようとしていたが、
|
||||
// static box 文脈では実体インスタンスが存在しないため UndefinedValue を生みやすい。
|
||||
// - ここでは receiver を持たない Global call に揃え、FuncScannerBox などの
|
||||
// static ヘルパー呼び出しを安全に扱う。
|
||||
let static_dst =
|
||||
builder.handle_static_method_call(cls, method, arguments)?;
|
||||
return Ok(Some(static_dst));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
/// Handle static method calls: BoxName.method(args)
|
||||
pub(super) fn handle_static_method_call(
|
||||
@ -77,43 +140,7 @@ impl MirBuilder {
|
||||
method: &str,
|
||||
arguments: &[ASTNode],
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Instance box: prefer enclosing box method (lowered function) if存在
|
||||
let enclosing_cls: Option<String> = self
|
||||
.current_function
|
||||
.as_ref()
|
||||
.and_then(|f| f.signature.name.split('.').next().map(|s| s.to_string()));
|
||||
|
||||
if let Some(cls) = enclosing_cls.as_ref() {
|
||||
let mut arg_values = Vec::with_capacity(arguments.len());
|
||||
for a in arguments {
|
||||
arg_values.push(self.build_expression(a.clone())?);
|
||||
}
|
||||
let arity = arg_values.len();
|
||||
let fname =
|
||||
function_lowering::generate_method_function_name(cls, method, arity);
|
||||
let exists = if let Some(ref module) = self.current_module {
|
||||
module.functions.contains_key(&fname)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if exists {
|
||||
// Pass 'me' as first arg
|
||||
let me_id = self.build_me_expression()?;
|
||||
let mut call_args = Vec::with_capacity(arity + 1);
|
||||
call_args.push(me_id);
|
||||
call_args.extend(arg_values.into_iter());
|
||||
let dst = self.next_value_id();
|
||||
// Emit as unified global call to lowered function
|
||||
self.emit_unified_call(
|
||||
Some(dst),
|
||||
CallTarget::Global(fname.clone()),
|
||||
call_args,
|
||||
)?;
|
||||
self.annotate_call_result_from_func_name(dst, &fname);
|
||||
return Ok(Some(dst));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
MeCallPolicyBox::resolve_me_call(self, method, arguments)
|
||||
}
|
||||
|
||||
/// Handle standard Box/Plugin method calls (fallback)
|
||||
|
||||
@ -185,8 +185,15 @@ impl MirInstruction {
|
||||
|
||||
MirInstruction::Return { value } => value.map(|v| vec![v]).unwrap_or_default(),
|
||||
|
||||
MirInstruction::Call { func, args, .. } => {
|
||||
let mut used = vec![*func];
|
||||
MirInstruction::Call { func, callee, args, .. } => {
|
||||
// func は legacy 経路では「関数値」を指すが、現在の unified 経路では
|
||||
// callee にメタ情報が入り、func はダミー (0) になることがある。
|
||||
// callee が None のときだけ func を SSA 値として扱い、それ以外
|
||||
// (callee=Some(..))では args のみを使用値とみなす。
|
||||
let mut used: Vec<ValueId> = Vec::new();
|
||||
if callee.is_none() {
|
||||
used.push(*func);
|
||||
}
|
||||
used.extend(args);
|
||||
used
|
||||
}
|
||||
|
||||
@ -581,11 +581,21 @@ impl CallLikeInst {
|
||||
pub fn used(&self) -> Vec<ValueId> {
|
||||
match self {
|
||||
CallLikeInst::Call { func, args, .. } => {
|
||||
let mut v = vec![*func]; v.extend(args.iter().copied()); v
|
||||
let mut v = Vec::new();
|
||||
// func は legacy 経路では「関数値」を指すが、unified 経路では
|
||||
// ValueId::INVALID がダミーとして入る。INVALID の場合は SSA の
|
||||
// 使用値としては数えず、args のみを使用値とみなす。
|
||||
if *func != ValueId::INVALID {
|
||||
v.push(*func);
|
||||
}
|
||||
v.extend(args.iter().copied());
|
||||
v
|
||||
}
|
||||
CallLikeInst::BoxCall { box_val, args, .. }
|
||||
| CallLikeInst::PluginInvoke { box_val, args, .. } => {
|
||||
let mut v = vec![*box_val]; v.extend(args.iter().copied()); v
|
||||
let mut v = vec![*box_val];
|
||||
v.extend(args.iter().copied());
|
||||
v
|
||||
}
|
||||
CallLikeInst::ExternCall { args, .. } => args.clone(),
|
||||
}
|
||||
|
||||
@ -96,6 +96,13 @@ impl MirVerifier {
|
||||
block,
|
||||
instruction_index
|
||||
);
|
||||
if let Some(bb) = function.blocks.get(block) {
|
||||
let inst_opt =
|
||||
bb.all_instructions().nth(*instruction_index);
|
||||
if let Some(inst) = inst_opt {
|
||||
eprintln!("[mir-ssa-debug-inst] inst={:?}", inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
VerificationError::DominatorViolation {
|
||||
value,
|
||||
|
||||
@ -31,6 +31,10 @@ pub fn check_ssa_form(function: &MirFunction) -> Result<(), Vec<VerificationErro
|
||||
for (inst_idx, instruction) in block.all_instructions().enumerate() {
|
||||
for used_value in instruction.used_values() {
|
||||
if !definitions.contains_key(&used_value) {
|
||||
eprintln!(
|
||||
"[ssa-undef-debug] fn={} bb={:?} inst_idx={} used={:?} inst={:?}",
|
||||
function.signature.name, block_id, inst_idx, used_value, instruction
|
||||
);
|
||||
errors.push(VerificationError::UndefinedValue {
|
||||
value: used_value,
|
||||
block: *block_id,
|
||||
|
||||
99
src/tests/mir_funcscanner_ssa.rs
Normal file
99
src/tests/mir_funcscanner_ssa.rs
Normal file
@ -0,0 +1,99 @@
|
||||
/*!
|
||||
* FuncScanner / Stage‑B 用 SSA デバッグ用テスト
|
||||
*
|
||||
* 目的:
|
||||
* - lang/src/compiler/tests/funcscanner_fib_min.hako と同等のソースを Rust 側から
|
||||
* 直接 MIR 化し、MirVerifier 経由で UndefinedValue / DominatorViolation を観測する。
|
||||
* - CLI 実行ログではノイズが多くなるため、ここでは純粋に MIR モジュール単体に対する
|
||||
* verify 結果と、ssa.rs 側の `[ssa-undef-debug]` ログだけにフォーカスする。
|
||||
*
|
||||
* 注意:
|
||||
* - 現時点では FuncScannerBox.scan_all_boxes/1 まわりに既知の UndefinedValue があるため
|
||||
* このテストは #[ignore] 付き(デバッグ専用)だよ。
|
||||
* - 修正が完了して UndefinedValue が出なくなったら、将来的に #[ignore] を外すか、
|
||||
* ベースラインフィルタを挟んで常時検証テストへ昇格させる想定。
|
||||
*/
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{MirCompiler, MirPrinter, MirVerifier};
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
fn ensure_stage3_env() {
|
||||
std::env::set_var("NYASH_PARSER_STAGE3", "1");
|
||||
std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1");
|
||||
std::env::set_var("NYASH_ENABLE_USING", "1");
|
||||
}
|
||||
|
||||
/// FuncScannerBox.scan_all_boxes/1 + LoopForm v2 最小ケース(fib)の SSA デバッグ
|
||||
///
|
||||
/// - 入力ソースは lang/src/compiler/tests/funcscanner_fib_min.hako と同一。
|
||||
/// - main 内で static box TestBox/Main を組み立てて FuncScannerBox.scan_all_boxes(src) を呼ぶ。
|
||||
/// - MirVerifier の UndefinedValue を拾いつつ、ssa.rs 側の `[ssa-undef-debug]` ログで
|
||||
/// どの命令が %0 や未定義 ValueId を使っているかを観測する。
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn mir_funcscanner_fib_min_ssa_debug() {
|
||||
ensure_stage3_env();
|
||||
|
||||
// funcscanner_fib_min.hako と同じソースをそのまま使う
|
||||
let src = include_str!("../../lang/src/compiler/tests/funcscanner_fib_min.hako");
|
||||
let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse funcscanner_fib_min.hako ok");
|
||||
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let cr = mc.compile(ast).expect("compile funcscanner_fib_min.hako ok");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
// デバッグ時に MIR 全体を見たい場合は NYASH_MIR_TEST_DUMP=1 で有効化
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (FuncScanner.fib_min) -----\n{}", dump);
|
||||
}
|
||||
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
}
|
||||
panic!("MIR verification failed for funcscanner_fib_min (debug harness)");
|
||||
}
|
||||
}
|
||||
|
||||
/// FuncScannerBox._scan_methods/4 + _parse_params/_trim の最小ケース(TestBox 本文だけ)の SSA デバッグ
|
||||
///
|
||||
/// - 入力ソースは lang/src/compiler/tests/funcscanner_scan_methods_min.hako と同一。
|
||||
/// - main 内で TestBox/Main 相当のソースを組み立て、FuncScannerBox._find_matching_brace で
|
||||
/// TestBox 本文だけを抜き出してから FuncScannerBox._scan_methods を直接呼ぶ。
|
||||
/// - UndefinedValue が出ないこと(特に _parse_params / _trim 呼び出しで未定義の me/receiver が使われないこと)
|
||||
/// を MirVerifier で確認する。
|
||||
#[test]
|
||||
fn mir_funcscanner_scan_methods_ssa_debug() {
|
||||
ensure_stage3_env();
|
||||
|
||||
let src = include_str!("../../lang/src/compiler/tests/funcscanner_scan_methods_min.hako");
|
||||
let ast: ASTNode =
|
||||
NyashParser::parse_from_string(src).expect("parse funcscanner_scan_methods_min.hako ok");
|
||||
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let cr = mc
|
||||
.compile(ast)
|
||||
.expect("compile funcscanner_scan_methods_min.hako ok");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (FuncScanner.scan_methods_min) -----\n{}", dump);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
}
|
||||
panic!("MIR verification failed for funcscanner_scan_methods_min (debug harness)");
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,7 @@ pub mod mir_stage1_using_resolver_verify;
|
||||
pub mod mir_locals_ssa;
|
||||
pub mod mir_loopform_exit_phi;
|
||||
pub mod mir_breakfinder_ssa;
|
||||
pub mod mir_funcscanner_ssa;
|
||||
pub mod mir_vm_poc;
|
||||
pub mod nyash_abi_basic;
|
||||
pub mod parser_static_box_members;
|
||||
|
||||
Reference in New Issue
Block a user