Span trace utilities and runner source hint

This commit is contained in:
nyash-codex
2025-11-24 14:17:02 +09:00
parent 3154903121
commit 466e636af6
106 changed files with 4597 additions and 958 deletions

View File

@ -2,6 +2,14 @@
Status: design+partial implementationStage1 ビルド導線の初期版まで)
## Stage1 CLI 実験メモ2025-XX
- 2025-XX: `NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 ./target/release/hakorune apps/tests/minimal_ssa_skip_ws.hako` を実行。
- ブリッジ自体は発火し、`stage1-cli/debug` ログと `Stage1CliMain.main/0` が生成されることを確認。
- しかし VM 側で `vm step budget exceeded`max_steps=2000000で終了。プラグイン未ロード警告ありFileBox/ArrayBox など)。
- 対応メモ: ① NYASH_DISABLE_PLUGINS=1 + core-ro で FileBox は代替読込、② `HAKO_VM_MAX_STEPS` をさらに引き上げる or パーサ前処理を削る検討、③ StageB 自前ビルドは `Unknown method 'main' on InstanceBox` で失敗中StageBDriverBox 呼び出しが壊れている)。
- Rust VM 側で `vm step budget exceeded`**fn / bb / last_inst 情報** SpanMIR に付いていれば .hako 行番号)を付与。今回の Stage1 CLI 実行では `fn=ParserBox.parse_program2/1 ... (lang/src/runner/stage1_cli.hako:1:1)` まで出力されたSpan は通ったが行位置はまだ粗い)。
## 25.1 サブフェーズの整理a〜e 概要)
- **25.1a — Stage1 Build Hotfix配線**

View File

@ -0,0 +1,5 @@
# Phase 25.1 — Span Trace Mini Note
- 方針: MIR 命令に AST Span を持たせ、VMError (StepBudgetExceeded) で fn/bb/inst に加えて .hako 行番号を出す。
- 実装: MirInstruction 生成時に current_span を保存し、VM 側で last_inst_idx から Span を引いてエラーに埋め込む。Span が無い場合は従来どおり fn/bb/inst のみ。
- 状態: Stage1 CLI の MIR には Span 未付与なので行番号はまだ出ていないが、Span 付き MIR なら `... (file.hako:line:col)` まで表示できる。

View File

@ -0,0 +1,97 @@
# Phase 25.1 — Stage1 CLI / BuildBox ループ & Box 依存分析メモ
目的
- `Stage1 CLI → BuildBox.emit_program_json_v0` 実行時に VM が step budget を食い尽くしている原因を、
ループ構造と BoxCall 依存の観点から整理しておくためのメモだよ。
## 1. 観測された症状
- コマンド例:
```bash
NYASH_CLI_VERBOSE=2 \
NYASH_USE_STAGE1_CLI=1 \
STAGE1_EMIT_PROGRAM_JSON=1 \
./target/release/hakorune apps/tests/minimal_ssa_skip_ws.hako
```
- 状態:
- `[stage1-bridge/trace]` `[stage1-cli/debug] emit_program_json ENTRY` が出ており、
Stage1CliMain.main/0 〜 stage1_cli.hako 実行までは到達している。
- その後 `BuildBox.emit_program_json_v0` 実行中に VM が `vm step budget exceeded`max_steps=1_000_000→2_000_000 でも NG
- FileBox/ArrayBox などの plugin 未ロード警告も併発。
- 現状は **ステップ上限+プラグイン依存** が阻害要因となって、program-json 自体は得られていない。
## 2. emit_program_json_v0 周辺のループ構造(概観)
調査時点でのループ構造のざっくり分類(ファイルは概略)だよ。
| ループ | ファイル | 複雑度 | 危険度 |
|------------------------------|--------------------------|-------------|--------|
| `ParserBox.parse_program2` | stage1_cli.hako:84 あたり | exponential | 🔴 最有力 |
| alias_table パース (opts) | build_box.hako:60-73 | O(n²) | 🟡 中 |
| alias_table パース (env) | build_box.hako:94-107 | O(n²) | 🟡 中 |
| `FileBox._read_file` | stage1_cli.hako:67 | I/O bound | ⚠️ 注視 |
| bundle_names 重複チェック | build_box.hako:41-51 | O(n²) | 🟢 許容 |
直感:
- alias_table や bundle_names の O(n²) 系ループは、入力サイズが小さい間は budget を食い尽くすレベルではない。
- `ParserBox.parse_program2` は Stage3 parser の statement/expression を全部受け持つため、
実装によっては **指数的** にブロック数・ステップ数が増え得る。
- FileBox は I/O bound なので、budget ではなくタイムアウト側のリスクが大きい。
## 3. Box 依存度マトリックス
Stage1 CLI / BuildBox ラインで使われている Box の洗い出しと、
「core だけで足りるか/外部プラグインが要るか」のざっくり分類だよ。
| Box | 用途 | プラグイン依存 | 影響度 |
|-----------|-------------------------------|---------|-------------|
| FileBox | `_read_file` で source 読み込み | YES | 🔴 CRITICAL |
| ParserBox | `parse_program2` AST parse | NO | 🟡 MEDIUM |
| ArrayBox | bundles 配列格納 | NO | 🟢 LOW |
| StringBox | alias_table / bundle_names parse | NO | 🟢 LOW |
メモ:
- FileBox は core-ro だけでは厳しく、実際には plugin / env 設定に依存するケースが多い。
- ParserBox は Stage3 parser の仕様そのものなので、LoopForm/JoinIR と独立に「parser の loop 形」問題として見る必要がある。
## 4. 今後の観測テンプレ(提案)
ステップ budget 溶けの原因をもう少し切り分けるために、次のようなコマンドで実験できるよ。
```bash
# Test 1: FileBox なしで inline source を直書き
NYASH_STAGE1_MODE=emit-program \
STAGE1_SOURCE_TEXT='static box Main { main(args) { return 0 } }' \
./target/release/nyash lang/src/runner/stage1_cli.hako
# Test 2: プラグイン OFF で挙動を見る
NYASH_DISABLE_PLUGINS=1 \
NYASH_STAGE1_MODE=emit-program \
STAGE1_SOURCE_TEXT='static box Main { main(args) { return 0 } }' \
./target/release/nyash lang/src/runner/stage1_cli.hako
# Test 3: ステップトレースを有効化
NYASH_VM_STATS=1 \
NYASH_STAGE1_MODE=emit-program \
STAGE1_SOURCE_TEXT='static box Main { main(args) { return 0 } }' \
./target/release/nyash lang/src/runner/stage1_cli.hako 2>&1 | grep -i step
```
目的:
- FileBox を完全にバイパスできる inline source モードで試し、
それでも budget が溶けるかを見るParserBox 起因かどうかの切り分け)。
- プラグイン OFF/ON で挙動がどれだけ変わるか確認する。
## 5. 方針メモ
- Stage1 CLI の「ブリッジまでは動いているが、そのあとで止まる」という現象は、
- StageB/BuildBox 経由の Program JSON emit
- ParserBox / FileBox のループ
のどこに問題があるかを 1 ループずつ切り出していけば、局所化できる見込み。
- joinIR 側とは独立に、このファイルでは「Stage1 CLI 実行ルートのうち、どのループと Box が危ないか」の観測結果を積み上げていくよ。
## 6. VM エラー出力改善メモ2025-XX
- Rust MIR Interpreter の step budget エラーに **fn / bb / last_inst / steps** Span を付与したよ。
- 例Span 付き MIR の場合): `vm step budget exceeded (max_steps=2000000, steps=2000001) at bb=bb49 fn=ParserBox.parse_program2/1 last_inst_idx=12 last_inst_bb=bb48 last_inst=... (file.hako:312:7)`
- Stage1 CLI の現行 MIR には Span が載っていないため、いまは fn/bb/inst だけが出る(`apps/tests/minimal_ssa_skip_ws.hako` 実行で確認。Span 付与を通せば .hako 行まで出る想定。

View File

@ -0,0 +1,75 @@
# Phase 25.1 — StageB Selfhost main 解決メモInstanceBox / Static Box 呼び出し)
目的
- `tools/selfhost/build_stage1.sh` 実行時に発生している
`Unknown method 'main' on InstanceBox` の根本原因を整理し、
どの Box / メソッド名のズレかを明確にするための観測メモだよ。
## 1. 現状の観測結果
- エラー発生箇所:
- `src/backend/mir_interpreter/handlers/boxes.rs:105` 付近InstanceBox 用のメソッドディスパッチ)。
- 症状:
- VM が **InstanceBox** という generic 型に対して `"main"` を探している。
- 期待されるのは `StageBDriverBox.main(...)` のような「ユーザー定義 static box 上の main」。
- Rust の `type_name()` macro がユーザー定義 Box を generic 型名InstanceBoxとして返しているため、
ログ上は常に `box_type_name=InstanceBox` となり、「どの box の main を探しているか」が見えづらい。
## 2. StageBDriverBox 側の定義
- 定義ファイル:
- `lang/src/compiler/entry/compiler_stageb.hako:1141-1367`
- 構造:
- `static box StageBDriverBox { main(args) { ... } }` という形で main が定義されている。
- エントリポイント:
- `Main.main()` から `StageBDriverBox.main()` を呼ぶ導線が 1373-1377 行あたりに存在。
- 直感的には:
- **`.hako` 側は正しく main を定義している**が、
- VM 側の `InstanceBox` 呼び出し時に **メソッド名の解決 or Box 名の正規化** でズレている可能性が高い。
## 3. ログ追加の案(型名+メソッド名の観測)
次のようなログを handlers/boxes.rs の Unknown method 分岐直前に入れておくと、
どの Box / 関数名の組み合わせが原因かが見やすくなるよ。
```rust
eprintln!(
"[vm/method-dispatch] Unknown method on Box: box_type_name={}, method_name={}",
recv_box.type_name(), // InstanceBox 側の type_name
method, // 探しにいったメソッド名(例: \"main\" / \"StageBDriverBox.main/0\"
);
```
※ 実際には `type_name()` は常に `InstanceBox` を返すので、
 実 box 名は `BoxDeclaration` / `InstanceBox` 内のフィールドから取り出す必要があるかもしれない。
## 4. 次に見ると良い箇所
### 4-1. MIR 側の BoxCall 受け側
- 目的:
- StageB selfhost の MIR で、「どの global / boxcall 名で StageBDriverBox.main を呼んでいるか」を確認する。
- 手順の目安:
- StageB ランチャの MIR を `--dump-mir` 等でダンプ。
- `call_global "StageBDriverBox.main/..."` のような形になっているかを見る。
### 4-2. MirBuilder の receiver / callee 解決
- ファイル候補:
- `src/mir/builder/calls/unified_emitter.rs`
- `src/backend/mir_interpreter/handlers/global.rs`
- 見たいこと:
- StaticMethodId / NamingBox を通った後に、
`"StageBDriverBox.main/arity"` がどのように解決されているか。
- InstanceBox に対するメソッド呼び出しのときに、
メソッド名から Box 名が正しく分離されているか。
## 5. 方針メモ
- joinIR ラインとは独立して、この問題は「StageB driver / InstanceBox / naming」の層で解決する。
- まずは:
- どの InstanceBox関数名の組み合わせで Unknown が出ているかをログで観測。
- `.hako` 側の StageBDriverBox 定義と、Rust 側のメソッド名解決ロジックの差分を見る。
- 修正自体(命名・正規化の調整)は Phase 25.1 の別サブフェーズで扱う前提として、
このファイルは「現状の観測結果と差分の候補」を残しておくためのメモだよ。