Phase 25.1b: VM undefined-value diagnostics and builder SSA helpers
This commit is contained in:
@ -40,19 +40,24 @@ Status: Step0〜3 実装済み・Step4(Method/Extern)実装フェーズ
|
||||
|
||||
#### Stage‑B と selfhost CLI canary(HakoCli.run/2)の現状
|
||||
|
||||
- selfhost CLI の最小ケース(`tools/smokes/v2/profiles/quick/core/phase251/selfhost_cli_run_basic_vm.sh` が生成する HakoCli.run サンプル)に対しては、現状 Stage‑B 実行中に VM エラー:
|
||||
- `❌ VM error: Invalid value: use of undefined value ValueId(N)` が発生し、Program(JSON v0) が 1 行としては出力されない(`tools/hakorune_emit_mir.sh` が Program 抽出に失敗する)。
|
||||
- `NYASH_VM_VERIFY_MIR=1` を立てて `lang/src/compiler/entry/compiler_stageb.hako` を直接叩くと、Stage‑B が生成した MIR に対して:
|
||||
- selfhost CLI の最小ケース(`tools/smokes/v2/profiles/quick/core/phase251/selfhost_cli_run_basic_vm.sh` が生成する HakoCli.run サンプル)に対しては、修正前は Stage‑B 実行中に VM エラー:
|
||||
- `❌ VM error: Invalid value: use of undefined value ValueId(N)`(%0 / 97842 / 22 など)が発生し、Program(JSON v0) が 1 行としては出力されなかった(`tools/hakorune_emit_mir.sh` が Program 抽出に失敗する)。
|
||||
- `NYASH_VM_VERIFY_MIR=1` を立てて `lang/src/compiler/entry/compiler_stageb.hako` を直接叩くと、修正前は Stage‑B が生成した MIR に対して:
|
||||
- `Stage1UsingResolverBox._collect_using_entries/1`
|
||||
- `ParserStringUtilsBox.skip_ws/2`
|
||||
- `ParserIdentScanBox.scan_ident/2`
|
||||
- `ParserBox.parse_stmt2/2`
|
||||
- などに `Undefined value %0 used in block ...` が多数報告されることが確認できる(詳細は `docs/private/roadmap/phases/phase-20.33/DEBUG.md` の「Invalid value: use of undefined value ValueId(N)」節を参照)。
|
||||
- などに `Undefined value %0 used in block ...` が多数報告されていた(詳細は `docs/private/roadmap/phases/phase-20.33/DEBUG.md` の「Invalid value: use of undefined value ValueId(N)」節を参照)。
|
||||
- Task先生による Rust MIR builder 側の修正(ValueId 割り当て統一+loop PHI/pinned 変数の扱い修正)後は:
|
||||
- `%0` / 97842 / 22 に起因する Undefined value / Invalid value エラーは `NYASH_VM_VERIFY_MIR=1` / 実行時ともに解消済み。
|
||||
- pinned 変数(`__pin$*@recv` など)も loop header/exit で正しく PHI に乗るようになり、ループ後のメソッドレシーバーが未定義になる問題も再現しなくなった。
|
||||
- 現時点で selfhost CLI サンプルに対して残っている課題は:
|
||||
- 1) Rust VM 実行時に `❌ VM error: Invalid value: use of undefined value ValueId(17)` が発生しており、拡張済みエラーメッセージ(`fn=Main.main, last_block=Some(BasicBlockId(3419)), last_inst=Some(Call { ... ParserBox.length() [recv: %17] ... })`)から「Stage‑B Main.main 内の `ParserBox.length()` 呼び出しにおいて recv スロット(ValueId(17)) が定義されていない」ことが分かっている。これは verifier がまだチェックしていない「Callee.receiver 側の SSA 漏れ」であり、Phase 25.1c の Stage‑B / LoopBuilder / LocalSSA 整理タスクで修正する前提。
|
||||
- 2) `if args { ... }` まわりの truthy 判定(ArrayBox を boolean 条件に使っている部分)の扱いに起因する型/意味論の揺れが残っており、こちらも 25.1c の型システム整理タスクで `ArrayBox` の truthy 規約を明文化した上で揃える想定。
|
||||
- 3) `NewBox HakoCli` が plugin 前提で解決されてしまう問題は、VM 側の static box factory 統合(静的 Box を User factory にも広告する)により解消済みであり、`NYASH_DISABLE_PLUGINS=1` でも静的 Box として HakoCli を生成できるようになっている(selfhost CLI canary では NewBox 自体はもはやブロッカーではない)。
|
||||
- 対応方針(Phase 25.1b 時点):
|
||||
- BoxTypeInspector / multi‑carrier LoopForm 経路とは独立した **Stage‑B/MIR 側の構造バグ** として扱い、selfhost CLI canary(HakoCli.run/2 lowering)はこの問題が解消されるまで「Stage‑B 側の既知の未修正バグ」として保留する。
|
||||
- `tools/hakorune_emit_mir.sh` には `diagnose_stageb_failure()` を追加し、Stage‑B の標準出力に `Invalid value: use of undefined value` が含まれている場合に:
|
||||
- `[stageb/diagnose] VM reported 'use of undefined value' during Stage‑B execution.` などのタグを出しつつ、
|
||||
- `NYASH_VM_VERIFY_MIR=1`+`compiler_stageb.hako` 直叩き、および `docs/private/roadmap/phases/phase-20.33/DEBUG.md` への導線を表示するようにした。
|
||||
- BoxTypeInspector / multi‑carrier LoopForm 経路とは独立した **Stage‑B/MIR 側の SSA/型システム/Box 解決の構造問題** として扱い、selfhost CLI canary(HakoCli.run/2 lowering)はこれらが片付くまでは「25.1c の構造タスク待ち」として扱う。
|
||||
- `tools/hakorune_emit_mir.sh` の `diagnose_stageb_failure()` は維持し、Stage‑B の標準出力に `Invalid value: use of undefined value` が含まれている場合には `NYASH_VM_VERIFY_MIR=1`+`compiler_stageb.hako` 直叩き、および `docs/private/roadmap/phases/phase-20.33/DEBUG.md` への導線を表示する。加えて、VM 側の `MirInterpreter::reg_load` が `fn` / `last_block` / `last_inst` を含めてエラー文字列を出すようになったため、Stage‑B 由来の undefined value は「どの関数のどの Call(どの recv)」で発生しているかを 1 行で特定できる。
|
||||
|
||||
### Rust provider (`env.mirbuilder.emit`)
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@ Status: planning(構造整理フェーズ・挙動は変えない)
|
||||
|
||||
2. **hostbridge.extern_invoke を「互換レイヤ」に押し込める**
|
||||
- 方針: 「`env.*` で表現できるものは ExternCall を正義とし、`hostbridge.extern_invoke` は互換用ラッパに限定する」。
|
||||
- Hako 側: `hostbridge.extern_invoke("env.*", ..)` は内部で `env.*` を呼ぶだけにする(新規コードは直接 `env.*` を使う)。
|
||||
- Rust 側: `"hostbridge.extern_invoke"` の実装は、`extern_provider_dispatch("env.*", ..)` に委譲する薄いブリッジに整理する。
|
||||
- Hako 側: `hostbridge.extern_invoke("env.*", ..)` は内部で `env.*` を呼ぶだけにする(新規コードは直接 `env.*` を使う)。
|
||||
- Rust 側: `"hostbridge.extern_invoke"` の実装は、`extern_provider_dispatch("env.*", ..)` に委譲する薄いブリッジに整理する。
|
||||
|
||||
3. **BoxIntrospect をコア型システムに昇格させる**
|
||||
- `env.box_introspect.kind` の実装を plugin loader v2 直下ではなく、コア runtime(例: `runtime/box_introspect.rs`)に寄せる。
|
||||
@ -35,14 +35,53 @@ Status: planning(構造整理フェーズ・挙動は変えない)
|
||||
- `BoxTypeInspectorBox` は `env.box_introspect.kind(value)` を唯一の情報源として扱い、repr ベースの fallback は「plugins も env.* も使えないデバッグ環境のみ」で使うことをコメントで明示する。
|
||||
|
||||
4. **Numeric view(i64 unwrap)の SSOT 化**
|
||||
- Hako 側: `string_helpers` / `BoxHelpers` / `MirSchemaBox` / `JsonEmitBox` / `LoopOptsBox` に散っている i64 unwrap ロジックを、小さなユーティリティ(仮: `box_numeric_view.hako`)に寄せる。
|
||||
- Rust 側: `NyashBox` から i64 を取り出す `as_i64` 的な関数を 1 箇所に置き、extern / BoxIntrospect 経路からはそれを使う。
|
||||
- Hako 側: `string_helpers` / `BoxHelpers` / `MirSchemaBox` / `JsonEmitBox` / `LoopOptsBox` に散っている i64 unwrap ロジックを、小さなユーティリティ(仮: `box_numeric_view.hako`)に寄せる。
|
||||
- Rust 側: `NyashBox` から i64 を取り出す `as_i64` 的な関数を 1 箇所に置き、extern / BoxIntrospect 経路からはそれを使う。
|
||||
|
||||
5. **Stage‑B Main を箱に分割して SSA/デバッグを軽くする**
|
||||
- 現状の `compiler_stageb.hako: Main.main` は:
|
||||
- CLI 引数パース (`--source` / `--bundle-*` / `--require-mod`)
|
||||
- bundle/require 解決 (`BundleResolver`)
|
||||
- body 抽出 (`body_src` の抽出ロジック)
|
||||
- ParserBox 呼び出し (`parse_program2` → emit JSON)
|
||||
- defs スキャン (`FuncScannerBox.scan_all_boxes`)
|
||||
が 1 関数に詰め込まれており、MIR 上でも巨大な `Main.main` になっている。
|
||||
- 25.1c ではこれを「箱理論」に沿って分割する:
|
||||
- `StageBArgsBox`(CLI 引数と bundle/require の扱いだけを担当)
|
||||
- `StageBBodyExtractorBox`(`body_src` 抽出ロジックだけを担当)
|
||||
- `StageBDriverBox`(ParserBox/FuncScannerBox を呼んで Program(JSON v0) を emit)
|
||||
- Rust 側 MirBuilder には、この箱ごとの小さな関数をそのまま MIR に落とさせることで、`Main.main` の SSA/Loop の複雑さを減らし、今回のような ValueId 追跡をしやすくする。
|
||||
- 併せて、現状 selfhost CLI サンプルで観測されている `Main.main` 内の `ParserBox.length()` 呼び出しに対する recv 未定義エラー(`Invalid value: use of undefined value ValueId(17)`)を、Stage‑B Main 分割+LocalSSA/LoopBuilder 整理の一環として根本修正する(MethodCall の recv にも SSA/verify を適用する)。
|
||||
|
||||
6. **LoopBuilder / pin スロットの型付け・箱化**
|
||||
- いまの LoopBuilder は `__pin$*$@recv` のような文字列ベースの「内部変数名」を `variable_map` に直接突っ込んで、SSA/phi/pin を管理している。
|
||||
- 25.1c では、Loop 状態を「箱」として切り出して型付けする:
|
||||
- 例: `LoopStateBox`(Rust 側構造体)に
|
||||
- `recv_slots`(Method receiver 用)
|
||||
- `index_slots`(ループカウンタ用)
|
||||
- `limit_slots`(limit/上限 expr 用)
|
||||
を明示的に持たせる。
|
||||
- `LoopBuilder::emit_phi_at_block_start` / `update_variable` は、この LoopStateBox を通じてのみ pin/phi を操作し、「recv に Null/未定義が混ざらない」ことを構造レベルで保証する。
|
||||
|
||||
7. **ビルダー観測用の専用レイヤ(デバッグ箱)**
|
||||
- すでに `NYASH_BUILDER_TRACE_RECV` / `NYASH_BUILDER_DEBUG` などで ad-hoc に eprintln を入れているが、出力箇所が複数ファイルに散っていて再利用しにくい。
|
||||
- 25.1c ではこれを `builder.observe` 的なモジュール(箱)に集約する:
|
||||
- 例: `observe::recv::log(fn, bb, name, src, dst)`、`observe::phi::log(fn, bb, dst, inputs)` など。
|
||||
- ポリシー:
|
||||
- すべて dev トグル(NYASH_BUILDER_TRACE_*)越しに呼ぶ。
|
||||
- 本番挙動は変えず、「どこをどうトレースできるか」を構造として明示する。
|
||||
|
||||
8. **Stage‑B 向けの極小 MIR 再現ハーネス**
|
||||
- `docs/private/roadmap/phases/phase-20.33/DEBUG.md` にあるような Stage‑B 向けメモを踏まえ、Stage‑B/MirBuilder 用の「極小 Hako → MIR テスト」を 1 つ用意する。
|
||||
- 例:
|
||||
- 100〜200 行程度の `.hako` を `lang/src/compiler/tests/stageb_min_sample.hako` のようなファイルに固定。
|
||||
- Rust 側で MirBuilder に直接その AST を食わせて MIR を生成し、`NYASH_VM_VERIFY_MIR=1` で「Undefined value」が出ないことを確認するユニット/スモークを足す(構造バグ検知用)。
|
||||
- これにより、Stage‑B/LoopBuilder に関する修正が `.hako` 本番コード全体に依存せず、小さな再現ケースで検証できるようにする。
|
||||
|
||||
## 進め方メモ
|
||||
|
||||
- 先にドキュメントを書く(env extern / BoxIntrospect / numeric view の仕様を `docs/specs` 配下に整理)→ そのあとで Bridge / VM / Hako を小さく揃える。
|
||||
- 既存フェーズとの関係:
|
||||
- Phase 25.1b: selfhost builder / multi‑carrier / BoxTypeInspector 実装フェーズ(機能側)。
|
||||
- Phase 25.1c: そのうち「env.* / hostbridge.* / BoxIntrospect の構造と責務」を整理するメタフェーズ(構造側)。
|
||||
- Phase 25.1c: そのうち「env.* / hostbridge.* / BoxIntrospect」に加えて、Stage‑B Main / LoopBuilder / builder 観測レイヤの構造と責務も整理するメタフェーズ(構造側)。
|
||||
- 挙動を変えないこと(Fail‑Fast / default path は現状維持)を前提に、小さな差分で進める。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user