fix(mir/builder): use function-local ValueId throughout MIR builder
Phase 25.1b: Complete SSA fix - eliminate all global ValueId usage in function contexts. Root cause: ~75 locations throughout MIR builder were using global value generator (self.value_gen.next()) instead of function-local allocator (f.next_value_id()), causing SSA verification failures and runtime "use of undefined value" errors. Solution: - Added next_value_id() helper that automatically chooses correct allocator - Fixed 19 files with ~75 occurrences of ValueId allocation - All function-context allocations now use function-local IDs Files modified: - src/mir/builder/utils.rs: Added next_value_id() helper, fixed 8 locations - src/mir/builder/builder_calls.rs: 17 fixes - src/mir/builder/ops.rs: 8 fixes - src/mir/builder/stmts.rs: 7 fixes - src/mir/builder/emission/constant.rs: 6 fixes - src/mir/builder/rewrite/*.rs: 10 fixes - + 13 other files Verification: - cargo build --release: SUCCESS - Simple tests with NYASH_VM_VERIFY_MIR=1: Zero undefined errors - Multi-parameter static methods: All working Known remaining: ValueId(22) in Stage-B (separate issue to investigate) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -17,17 +17,43 @@ Status: Step0〜3 実装済み・Step4(Method/Extern)実装フェーズ
|
||||
- `FuncScannerBox` + `HAKO_STAGEB_FUNC_SCAN=1` により、`static box` メソッド(暗黙の `me` 引数付き)も defs に載る。
|
||||
- using 解決:
|
||||
- `Stage1UsingResolverBox`(`lang/src/compiler/entry/using_resolver_box.hako`)+ `HAKO_STAGEB_MODULES_LIST` で `nyash.toml` の `[modules]` を参照し、`using lang.mir.builder.MirBuilderBox` 等をファイル結合前に解決。
|
||||
- Stage‑B entry 側は string literal using を廃止し、`using lang.compiler.entry.using_resolver as Stage1UsingResolverBox` のように module alias を使用する。
|
||||
- Stage‑B entry 側は string literal using を廃止し、`using lang.compiler.entry.using_resolver as Stage1UsingResolverBox` のように module alias を使用する。
|
||||
|
||||
#### Stage‑B func_scan トグルのデフォルト(HAKO_STAGEB_FUNC_SCAN)
|
||||
|
||||
- 目的:
|
||||
- Stage‑B を直接叩いたときに `HAKO_STAGEB_FUNC_SCAN` を立て忘れても、`HakoCli.run` や `TestBox.fib` のようなメソッド定義が `Program.defs` にきちんと入るようにする(selfhost builder / FuncLowering 側の前提を崩さない)。
|
||||
- 実装(compiler_stageb.hako 内):
|
||||
- 以前: `HAKO_STAGEB_FUNC_SCAN=1` のときだけ `FuncScannerBox.scan_all_boxes` を呼び出し、それ以外は defs を生成しなかった。
|
||||
- 現在: `HAKO_STAGEB_FUNC_SCAN` が未設定 (`null`) のときは既定で ON とみなし、**明示的に `"0"` が入っているときだけ OFF** として扱う。
|
||||
- これにより、`tools/hakorune_emit_mir.sh` や v2 スモーク以外から Stage‑B を直接呼び出しても、defs が常に生成される。
|
||||
- 既存のテストで func_scan を無効化したいケースでは、`HAKO_STAGEB_FUNC_SCAN=0` を明示すれば従来どおり defs をスキップできる。
|
||||
|
||||
#### Stage‑B の安定度と使用上の注意
|
||||
|
||||
- 正規経路:
|
||||
- Stage‑B は `tools/hakorune_emit_mir.sh` / `tools/selfhost/selfhost_build.sh` 経由で呼び出すことを前提としており、これらのラッパが Stage‑3 用 ENV(`NYASH_PARSER_STAGE3=1` / `HAKO_PARSER_STAGE3=1` / `NYASH_PARSER_ALLOW_SEMICOLON=1` など)を一括でセットする。
|
||||
- Phase 25.1b では「このラッパ経由で呼ぶ限り Stage‑B 自体は安定」とみなし、主な改善対象を Program→MIR の selfhost builder 側に置く。
|
||||
- Phase 25.1b では「multi-carrier fib などの core 小ケースについては、このラッパ経由で呼ぶ限り Stage‑B 自体は十分に安定」とみなし、主な改善対象を Program→MIR の selfhost builder 側に置く。
|
||||
- 手動実行時の注意:
|
||||
- Stage‑3 ENV を立てずに Stage‑B / VM を直接叩くと、`Undefined variable: local` のようなエラーが発生するが、これは構文/実装バグではなく「Stage‑3 キーワード(local など)を Stage‑1 と同じルールでパースしてしまっている」ため。
|
||||
- 詳細な原因と対処は `docs/development/troubleshooting/stage3-local-keyword-guide.md` にまとめてあり、selfhost 開発では「まずラッパスクリプトを使う → 必要な場合のみ ENV を明示して直叩きする」方針とする。
|
||||
|
||||
#### 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 に対して:
|
||||
- `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)」節を参照)。
|
||||
- 対応方針(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` への導線を表示するようにした。
|
||||
|
||||
### Rust provider (`env.mirbuilder.emit`)
|
||||
|
||||
- `program_json_to_mir_json_with_imports`:
|
||||
@ -122,16 +148,18 @@ Status: Step0〜3 実装済み・Step4(Method/Extern)実装フェーズ
|
||||
|
||||
- `lang/src/mir/builder/internal/lower_loop_multi_carrier_box.hako`
|
||||
- 目的:
|
||||
- Fibonacci 風の「multi-carrier」ループ(`i,a,b,t` など複数のキャリアを持つループ)を selfhost builder 側で検出し、将来的に LoopFormBox に委譲できるようにする足場。
|
||||
- 現段階の挙動:
|
||||
- Program(JSON v0) 内に
|
||||
- `type:"Loop"` と `type:"Compare"` が存在し、
|
||||
- ループ本体に `type:"Local"` が複数存在する(=キャリア候補が複数ある)
|
||||
ことを確認したら、`HAKO_MIR_BUILDER_TRACE=1` 相当(builder_config.trace_enabled)有効時に
|
||||
`[mirbuilder/internal/loop:multi_carrier:detected]` タグを出す。
|
||||
- まだ LoopFormBox への `build2` 委譲は行わず、`null` を返して Rust provider 経路にフォールバックする(Fail-Fast ポリシー維持)。
|
||||
- Fibonacci 風の「multi-carrier」ループ(`i,a,b,t` など複数のキャリアを持つループ)を selfhost builder 側で検出・LoopFormBox (`mode="multi_count"`) に委譲する。
|
||||
- 現段階の挙動(2025-11-16 時点):
|
||||
- Program(JSON v0) 内で `Loop` + `Compare` を検出し、キャリア初期値(`local a = 0; local b = 1; ...`)を 2 個以上抽出。
|
||||
- `i < N` の `N` については、`Int` リテラルだけでなく `n` のようなパラメータ `Var` もサポートし、`limit_kind=const|param` として `LoopFormBox.build2` に伝達。
|
||||
- 成功時は `[mirbuilder/internal/loop:multi_carrier:detected:limit_kind=param,value=...|param_reg=...]` タグを確実に出力し、そのまま LoopForm から返ってきた MIR を `_rebind` する。
|
||||
- ループ limit が `local` 参照など selfhost で扱えない場合は `[mirbuilder/internal/loop:multi_carrier:limit:unsupported]` を出して `null` を返し、Rust provider 経路へ退避。
|
||||
- スモーク:
|
||||
- `tools/smokes/v2/profiles/quick/core/phase251/selfhost_mir_loopform_multi_carrier_vm.sh`
|
||||
- Stage‑B で `TestBox.fib(n)` を emit し、selfhost builder が `[funcs/basic:loop.multi_carrier]` を出したうえで `TestBox.fib/1` を含む MIR(JSON) を生成するかをチェック。
|
||||
- `carriers` 長や `limit_kind=param` ログを条件に PASS/FAIL を分岐(provider fallback 時は SKIP)。
|
||||
- 今後の拡張:
|
||||
- `LoopScanBox` を使ってキャリア変数群・limit などを抽出し、`LoopOptsBox.build2(opts)` に渡すロジックを追加する。
|
||||
- `LoopFormBox.build_loop_multi_carrier` の `limit_kind=param` 実装を一般化し(現在は param register コピー → `reg_limit` 初期化まで対応済み)、break/continue 付き multi-carrier も下ろせるようにする。
|
||||
- 代表ケースとして `tools/smokes/v2/profiles/quick/core/vm_loop_phi_multi_carriers.sh` と同型の .hako を selfhost-first で通す canary を追加し、VM/EXE rc を Rust オラクルと比較する。
|
||||
|
||||
## Next Steps(実装フェーズに入るときの TODO)
|
||||
@ -196,9 +224,23 @@ Status: Step0〜3 実装済み・Step4(Method/Extern)実装フェーズ
|
||||
- どの関数名で `_lower_func_body` が `null` を返したかを `HAKO_SELFHOST_TRACE=1` でログ出力。
|
||||
- `tools/hakorune_emit_mir.sh`
|
||||
- 既存の head/tail ログ出力で `[builder/selfhost-first:*]` タグがそのまま表示されることを確認済み(追加改修なし)。
|
||||
- Phase 25.1b 以降、`HAKO_SELFHOST_BUILDER_FIRST=1` で呼び出された場合は「Stage‑B → selfhost builder(+Stage1 CLI)」経路のみを試行し、selfhost builder が失敗した場合は即座に非0で終了する。selfhost-first モードでは `env.mirbuilder.emit` / provider delegate へのフォールバックは行わず、MirBuilder の未整備を隠さない方針とする(provider 経路を使うときは `HAKO_SELFHOST_BUILDER_FIRST=0` を明示)。
|
||||
- 成果物:
|
||||
- selfhost-first で Stage1 CLI を通したときに、どの関数/構造がまだ未サポートなのかがログで推測できる状態。
|
||||
|
||||
#### 補足: Ny selfhost パイプラインとの関係(Phase 25.1b 時点)
|
||||
- `.hako → Program(JSON v0) → MIR(JSON)` のメイン経路は、Stage‑B(`compiler_stageb.hako`)と MirBuilder/selfhost builder で完結させる。Ny selfhost(`src/runner/selfhost.rs`)は `.ny` 用の補助ルートとして扱い、Phase 25.1b のスコープからは外す。
|
||||
- `NYASH_USE_NY_COMPILER`:
|
||||
- Phase 25.1b で「明示 opt-in」(既定=0)に変更。Runner は `NYASH_USE_NY_COMPILER=1` が立っているときだけ selfhost パイプライン(Ny→JSON v0)を試行し、それ以外では従来どおり Rust parser/JSON v0 bridge を使う。
|
||||
- `.hako` 実行や `tools/hakorune_emit_mir.sh` 経由の Stage‑B/MirBuilder/selfhost builder には影響しない(これらは `NYASH_USE_NY_COMPILER=0` / `NYASH_DISABLE_NY_COMPILER=1` で起動)。
|
||||
- Python MVP:
|
||||
- `tools/ny_parser_mvp.py` は Phase 15 時点の Ny→JSON v0 実験用ハーネスであり、Phase 25.1b では `NYASH_NY_COMPILER_USE_PY=1` のときだけ有効にする。
|
||||
- 既定では Ny selfhost パイプラインから Python には落ちない(脱 Python 方針に合わせて dev 専用の補助線に格下げ)。
|
||||
- inline selfhost compiler(`inline_selfhost_emit.hako`):
|
||||
- `try_run_selfhost_pipeline` の最終手段として、`using lang.compiler.parser.box as ParserBox` / `using lang.compiler.stage1.emitter_box as EmitterBox` を含む小さな Hako を生成し、`ParserBox.parse_program2`→`EmitterBox.emit_program` で JSON v0 を得る経路が残っている。
|
||||
- 現状、この inline 経路は `.ny` の大きなソースに対して 60s タイムアウト+ `Undefined variable: local` を伴うことがあり、ParserBox/Stage‑3/using 周りに無限ループ相当のバグが残っている疑いがある。
|
||||
- Phase 25.1b では `.hako` selfhost builder から Ny selfhost 経路を切り離すことを優先し、inline 経路のバグは `[ny-compiler] inline timeout ...` + `[ny-inline:hint]`(stdout/stderr の head を添える)で可視化したうえで、後続フェーズ(25.1c 以降)の構造タスクとして扱う。
|
||||
|
||||
### Step 1 — defs injection の再設計
|
||||
- Status: initial-implemented(main 必須チェックはトグル付き; multi-function への完全移行は後続 Step)。
|
||||
- 目的: `FuncLoweringBox.inject_funcs` で main 関数の有無を意識し、multi-function モジュールの土台を整える。
|
||||
@ -232,8 +274,24 @@ Status: Step0〜3 実装済み・Step4(Method/Extern)実装フェーズ
|
||||
- LoopForm制約外は必ずタグ付きでFail-Fast(Rust providerに退避可能)。
|
||||
- 成果物:
|
||||
- `cmd_build_exe`の`loop(i < argc)`等、Stage1 CLIの代表的なwhile/forパターンをselfhost builderで通せる基礎が整った。
|
||||
- 追加アップデート(2025-11-16): multi-carrier ループ(`TestBox.fib(n)` など)も `LowerLoopMultiCarrierBox` → `LoopFormBox.build_loop_multi_carrier` 経由で selfhost lowering できるようになり、limit が `Int` でなく `Var(n)` でも `[mirbuilder/internal/loop:multi_carrier:detected:limit_kind=param,...]` を出して処理できる。
|
||||
- 次のステップ: LoopForm対応の動作確認スモークテスト追加、Step4(MethodCall/ExternCall)へ進む。
|
||||
|
||||
#### Step 3.1 — Box 型情報 API(Rust Parity)★New
|
||||
- 背景:
|
||||
- Stage‑3 VM では `"" + MapBox` のような「Box を文字列に暗黙変換する演算」が禁止されており、既存の `JsonEmitBox` / `BoxHelpers`(`LoopOptsBox.build2` から呼び出される)が `repr` 判定に依存しているため multi-carrier の JSON 生成が `Type error` で停止した。
|
||||
- Rust 側の MirBuilder は enum で型が決まっており `match` で分岐できる。Hakorune 側でも同等の「Box の種別を問い合わせる API」を用意して文字列ハックを撤廃する必要がある。
|
||||
- 設計方針:
|
||||
1. `lang/src/shared/common/box_type_inspector_box.hako` を追加し、`BoxTypeInspectorBox.kind(value)` / `is_map(value)` / `is_array(value)` 等の API を提供する。
|
||||
2. 実装は Stage0 Rust 側に `env.box_introspect(kind, value)` 的な extern を追加し、`hostbridge.extern_invoke("env.box_introspect","kind",[value])` で種別名(例: `"MapBox"`, `"ArrayBox"`, `"Int"`)を返す。
|
||||
3. `BoxHelpers` / `JsonEmitBox` / `LoopOptsBox` など、Box 種別チェックが必要な箇所はすべてこの API に置き換え、`"" + value` を一切使わない。
|
||||
4. 返り値は最小で `Null/Bool/Int/String` と `MapBox/ArrayBox/HostHandleBox`(Stage1 で使用する型)をカバーし、将来的に `type_id` などを拡張する。
|
||||
- 追加で行うこと:
|
||||
- `CURRENT_TASK.md` に Box 型 API 実装タスクを追加し、LoopForm multi-carrier の JSON 出力がこの API 依存であることを明示。
|
||||
- Stage0 側での対応(`env.box_introspect` 新規 extern)の設計も合わせて `phase-25.1b/README.md` に記述しておく(Selfhost 側で API 追加→Rust 側 stub→VM 反映の順)。
|
||||
- 現状(2025-11-16 時点): Stage‑3 VM 経路で `BoxTypeInspectorBox.kind` / `is_map` / `is_array` が MapBox / ArrayBox を正しく認識し、小さな Hako テストで `hostbridge.extern_invoke("env.box_introspect","kind",[value])` → `env.box_introspect.kind` provider → plugin loader v2 の BoxIntrospect 実装までが end‑to‑end で動作することを確認済み。
|
||||
- fib multi‑carrier 経路と selfhost multi‑carrier smoke 用の canary ケース(`tools/smokes/v2/profiles/quick/core/phase251/selfhost_mir_loopform_multi_carrier_vm.sh`)は、2025‑11‑16 時点で `env.box_introspect.kind` provider 経路+BoxTypeInspector 経由の multi-carrier LoopForm で PASS 済み。ログに `[mirbuilder/internal/loop:multi_carrier:detected:limit_kind=param,...]` と `[funcs/basic:loop.multi_carrier] -> TestBox.fib/1` が現れ、出力 MIR(JSON) に `"name":"TestBox.fib/1"` が含まれることを確認したため、「env.box_introspect.kind provider 経路完了 / multi‑carrier selfhost-first canary PASS」とみなす。
|
||||
|
||||
### Step 4 — MethodCall / ExternCall パリティ(設計メモ・Rust層読解込み)
|
||||
- Status: design-only(Rust 層の挙動を踏まえた設計まで)
|
||||
- 目的: `hostbridge.extern_invoke` / `FileBox` / `ArrayBox` など Stage1 CLI で多用される呼び出しを selfhost builder でも再現し、Rust 側の `build_method_call` / extern handler と意味論を揃える(ただしスコープは Stage1 必要最小限に限定)。
|
||||
@ -319,6 +377,80 @@ Status: Step0〜3 実装済み・Step4(Method/Extern)実装フェーズ
|
||||
が見つかった場合は、`[builder/funcs:unsupported:call]` タグを出して `null` で戻る。
|
||||
- これにより、「知らない形をなんとなく MIR にする」ことを避け、Rust provider や legacy CLI delegate に退避できるようにする。
|
||||
|
||||
#### Step 4.1 — Rust 層 Call/ExternCall 契約の整理(移植元 SSOT)
|
||||
|
||||
- 目的:
|
||||
- Stage1 側の MethodCall/ExternCall lowering を「Rust 実装の振る舞い」に正確に揃えるため、Rust 層の Call/ExternCall/hostbridge 経路を SSOT として整理しておく。
|
||||
- ここでの整理は構造レベルに留め、意味論の“拡張”は行わない(Hako 側はこの契約に従うだけ)。
|
||||
|
||||
- Rust 側のコア断面(ざっくり構造):
|
||||
- **MIR ビルダ(呼び出し生成)**:
|
||||
- `src/mir/builder/builder_calls.rs`
|
||||
- `emit_unified_call(dst, CallTarget, args)`:
|
||||
- `CallTarget::Method { box_type, method, receiver }` → `Callee::Method` を作り、`MirInstruction::Call { callee: Some(Callee::Method{..}), ... }` を emit。
|
||||
- `CallTarget::Extern(name)` → 文字列 `"env.codegen.emit_object"` などを `ExternCall` に変換(`iface_name="env.codegen"`, `method_name="emit_object"`)。
|
||||
- `CallTarget::Global(name)` → `Callee::Global(name)` 付き `Call` を emit(`execute_global_function` へ)。
|
||||
- **VM 側 Call ハンドラ**:
|
||||
- `src/backend/mir_interpreter/handlers/calls/global.rs`:
|
||||
- `execute_global_function(func_name, args)`:
|
||||
- まず `functions` テーブルにあれば module 内関数として実行。
|
||||
- そうでない場合、`normalize_arity_suffix("name/1")` した base 名に対して:
|
||||
- `"print"` → `execute_extern_function("print", args)`。
|
||||
- `"hostbridge.extern_invoke"` → `execute_extern_function("hostbridge.extern_invoke", args)`(SSOT: hostbridge 経由の extern は必ずここを通る)。
|
||||
- `"env.mirbuilder.emit"` / `"env.codegen.emit_object"` / `"env.codegen.link_object"`:
|
||||
- それぞれ `crate::host_providers::{mir_builder,llvm_codegen}` を直接呼ぶ「グローバル関数版」ルート。
|
||||
- `src/backend/mir_interpreter/handlers/calls/externs.rs`:
|
||||
- `execute_extern_function(iface, method, args)`:
|
||||
- `("env.mirbuilder","emit")` / `("env.codegen","emit_object")` / `("env.codegen","link_object")` などを `extern_provider_dispatch` に委譲。
|
||||
- `"hostbridge.extern_invoke"` base 名もここから `extern_provider_dispatch("hostbridge.extern_invoke", args)` に流す。
|
||||
- **ExternCall / hostbridge.extern_invoke の provider**:
|
||||
- `src/backend/mir_interpreter/handlers/externals.rs`:
|
||||
- ExternCall 形(`MirInstruction::ExternCall`) を `iface_name`,`method_name` ごとに振り分け:
|
||||
- `("env.mirbuilder","emit")` → `extern_provider_dispatch("env.mirbuilder.emit", args)`。
|
||||
- `("env.codegen","emit_object")` → `extern_provider_dispatch("env.codegen.emit_object", args)`。
|
||||
- `("env.codegen","link_object")` → 第3引数 ArrayBox `[obj_path, exe_out?]` を取り出して C-API ルートへ。
|
||||
- `("hostbridge","extern_invoke")` → `extern_provider_dispatch("hostbridge.extern_invoke", args)`(なければ Invalid)。
|
||||
- `src/backend/mir_interpreter/handlers/extern_provider.rs`:
|
||||
- `extern_provider_dispatch(key, args)`:
|
||||
- `"env.mirbuilder.emit"`:
|
||||
- `args[0]` を `program_json` にし、`HAKO_MIRBUILDER_IMPORTS` から imports マップを読む。
|
||||
- `host_providers::mir_builder::program_json_to_mir_json_with_imports` を呼んで MIR(JSON) 文字列を返す。
|
||||
- `"env.codegen.emit_object"`:
|
||||
- `args[0]` を MIR(JSON) 文字列にして v1 へ normalize → `llvm_codegen::mir_json_to_object`。
|
||||
- `"env.codegen.link_object"`:
|
||||
- `args[0]`=obj_path, `args[1]`=exe_out を文字列化し、C-API ルート(`NYASH_LLVM_USE_CAPI=1` + `HAKO_V1_EXTERN_PROVIDER_C_ABI=1`)で `link_object_capi`。
|
||||
- `"env.get"` / `"env.box_introspect.kind"` / `"hostbridge.extern_invoke"` もここで扱う(BoxIntrospect は plugin_loader_v2 に委譲)。
|
||||
|
||||
- plugin_loader v2 側の env.*:
|
||||
- `src/runtime/plugin_loader_v2/enabled/extern_functions.rs`:
|
||||
- `extern_call(iface_name, method_name, args)` で `env.*` を一括処理。
|
||||
- `handle_mirbuilder("emit", args)`:
|
||||
- `args[0]` の Program(JSON v0) 文字列を受け取り、`host_providers::mir_builder::program_json_to_mir_json` で MIR(JSON v0) を返す。
|
||||
- `handle_codegen("emit_object", args)`:
|
||||
- `args[0]` の MIR(JSON v0) 文字列を受け取り、ny-llvmc ラッパ (`llvm_codegen::mir_json_to_object`) で object (.o) のパスを返す。
|
||||
|
||||
- Bridge(JSON v0 → MIR)の特別扱い:
|
||||
- `src/runner/json_v0_bridge/lowering/expr.rs`:
|
||||
- `MapVars::resolve`:
|
||||
- `hostbridge` / `env` を特殊変数として扱い、それぞれ Const(String) `"hostbridge"` / `"env"` を生成する(Method チェーンを降ろすためのプレースホルダ)。
|
||||
- `lower_expr_with_scope`:
|
||||
- `ExprV0::Extern { iface, method, args }` → `MirInstruction::ExternCall { iface_name, method_name, ... }`。
|
||||
- `ExprV0::Method` の特別ケース:
|
||||
- `ConsoleBox` の `print/println/log` → `ExternCall env.console.log`。
|
||||
- `env.box_introspect.kind(value)` パターン → `ExternCall env.box_introspect.kind` に正規化。
|
||||
|
||||
- Selfhost への移植指針(Rust SSOT に沿った箱設計):
|
||||
- `MethodCall`:
|
||||
- Hako 側では「どの Box のどのメソッドを MIR の `mir_call(Method)` に落とすか」を Box 単位の helper で管理する(`LoopOptsBox` や `Cli*Box` と同様に)。
|
||||
- Rust 側の `CallTarget::Method` → `Callee::Method` の変換ルール(receiver レジスタの扱い、box_name/method 名)を Step 4 の設計メモと揃える。
|
||||
- `ExternCall`:
|
||||
- `hostbridge.extern_invoke("env.codegen","emit_object"/"link_object", args)` や `env.mirbuilder.emit` などは、
|
||||
- Rust では最終的に `ExternCall` → `extern_provider_dispatch("env.*", args)` → `plugin_loader_v2::extern_call("env.*", method, args)` / `host_providers::*` という構造になっている。
|
||||
- Hako 側では「env 名+メソッド名の組(= key)」を列挙した薄い `*BridgeBox` でラップし、そのうえで `ExternCallLowerBox` が `externcall func="env.codegen.emit_object"` を emit する。
|
||||
- 未対応の name/method 組は必ず Fail-Fast(タグ付き)で provider に回す。
|
||||
|
||||
この Step 4.1 を「Rust 側の SSOT」として固定しておき、Phase 25.1c 以降ではこの契約に沿って Hako 側の MethodCall/ExternCall lowering 箱を実装・整理していく(Rust 側に新ルールは追加しない)方針とする。
|
||||
|
||||
- 実装イメージ(Phase 25.1b 中にやるときの TODO):
|
||||
1. `FuncLoweringBox` に小さな helper を追加:
|
||||
- `_lower_method_call(body_json, func_name, box_name, params_arr)` → MethodCall パターン検出+`mir_call Method` 生成。
|
||||
|
||||
48
docs/development/roadmap/phases/phase-25.1c/README.md
Normal file
48
docs/development/roadmap/phases/phase-25.1c/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Phase 25.1c — Env / Extern / BoxIntrospect Structural Cleanup
|
||||
|
||||
Status: planning(構造整理フェーズ・挙動は変えない)
|
||||
|
||||
## ゴール
|
||||
|
||||
- `env.*` / `hostbridge.*` / `env.box_introspect.*` の責務と経路を整理し、型システムまわりの「正しい入口」を 1 箇所に揃える。
|
||||
- Box 型情報 API(`env.box_introspect.kind` + `BoxTypeInspectorBox`)を **コア型システム**として扱えるようにする(plugins の有無に依存しない)。
|
||||
- i64 / MapBox / ArrayBox の unwrap ロジックを SSOT に寄せ、MirBuilder / JsonEmit / LoopOpts / BoxHelpers が同じ前提で動くようにする。
|
||||
|
||||
## スコープ(何をここで扱うか)
|
||||
|
||||
- 対象:
|
||||
- Rust 側: `extern_registry.rs` / `handlers/externals.rs` / `handlers/extern_provider.rs` / `runtime/plugin_loader_v2/*`
|
||||
- Hako 側: `BoxTypeInspectorBox` / `BoxHelpers` / `JsonEmitBox` / `MirSchemaBox` / `LoopOptsBox`
|
||||
- ドキュメント: `docs/specs`(env externs / box_introspect / numeric view の設計メモ)
|
||||
- 非対象:
|
||||
- 新しい言語機能や VM 命令の追加(Phase 25 ポリシーに従い、仕様拡張はしない)。
|
||||
- MirBuilder の意味論変更(multi‑carrier や LoopForm の設計は Phase 25.1b の範囲に留める)。
|
||||
|
||||
## やりたい整理(タスクリスト)
|
||||
|
||||
1. **env.* extern の SSOT を決める**
|
||||
- `env.get` / `env.mirbuilder.emit` / `env.codegen.emit_object` / `env.codegen.link_object` / `env.box_introspect.kind` を一覧化し、仕様(引数・戻り値・MIR 形)を `docs/specs/env_externs.md`(仮)に明文化する。
|
||||
- JSON v0 → MIR ブリッジ(`MapVars::resolve` / lowering)で、上記が必ず `ExternCall("env.*", ..)` に落ちることを確認・修正する。
|
||||
|
||||
2. **hostbridge.extern_invoke を「互換レイヤ」に押し込める**
|
||||
- 方針: 「`env.*` で表現できるものは ExternCall を正義とし、`hostbridge.extern_invoke` は互換用ラッパに限定する」。
|
||||
- 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`)に寄せる。
|
||||
- コア型(MapBox / ArrayBox / StringBox / IntegerBox / BoolBox / NullBox)は runtime 側で `build_box_info` を定義し、plugin loader は「ユーザー Box の拡張」だけを担当する。
|
||||
- `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 経路からはそれを使う。
|
||||
|
||||
## 進め方メモ
|
||||
|
||||
- 先にドキュメントを書く(env extern / BoxIntrospect / numeric view の仕様を `docs/specs` 配下に整理)→ そのあとで Bridge / VM / Hako を小さく揃える。
|
||||
- 既存フェーズとの関係:
|
||||
- Phase 25.1b: selfhost builder / multi‑carrier / BoxTypeInspector 実装フェーズ(機能側)。
|
||||
- Phase 25.1c: そのうち「env.* / hostbridge.* / BoxIntrospect の構造と責務」を整理するメタフェーズ(構造側)。
|
||||
- 挙動を変えないこと(Fail‑Fast / default path は現状維持)を前提に、小さな差分で進める。
|
||||
|
||||
Reference in New Issue
Block a user