27 KiB
27 KiB
Phase‑31 — Box Normalization(Static→Singleton 正規化)
最終更新: 2025‑10‑22(Verifier 移行表追加/quick profile の intern 監視を静音化)
サマリ
- ねらい: すべてのメソッド呼び出し形を「me + args」に統一し、static box を型ごとのシングルトンインスタンス(
Type.singleton)に正規化する。 - 効果: ルータ分岐削減、receiver 有無によるバグ根絶(今回の ArrayBox(1) 類)、Extern/HostBridge 経路の単純化、AOP/計測の注入点の一元化。
- 方式: Builder で
Static(Type.method(args)) → Instance(Type.singleton.method(args))に書換。ルータは常に receiver を渡す。Verifier で逸脱を Fail‑Fast。 - 導入: フラグ既定OFFで段階導入(A→B→C)。プラグイン側で
len/lengthエイリアスを正規化(トランポリン撤退済み)。
追加計画(Phase‑31 取り込み)— MIR 正規化とアーキ境界の固定
- MirCall 一本化(最小カノニカル形)
- 許可:
Method/ModuleFunction("Box.m/arity")/Constructor/Extern(補助:Value/Closure) - 禁止: Legacy
BoxCall/ExternCall/Print/生 NewBox、Box Eq/Ne(Enum は.equals()、その他はnyrt.ops.op_eq) - 参考:
docs/development/architecture/mir/mir-canonical-calls.md
- 許可:
- Three Rings(Core/Meta/Domain)で責務を分離
- Meta は「プラグイン様式で実装」だが既定は内蔵(LTO/inline)。Domain は外部プラグイン。Core は不可抜き。
- 参考:
docs/development/architecture/three-rings.md
- 二層エクスポート(Stable ABI + Inlinable API)
- 既知先は直呼び(Known/Rewrite)/未知先は rt_boxcall 集約。Intern(安定ID)で解決子を決定化。
- 参考:
docs/development/architecture/abi/two-layer-export.md
実施方針(段階導入・既定OFF)
- Phase A(Docs/型定義のみ): 文書の追加・MirCall::CallFlags の拡張(may_throw/tail_ok/inline_hint)
- Phase B(Builder/Verifierの小正規化): NewBox→Constructor、Print撤退、Box Eq/Ne 正規化(strictガードは既存を拡張)
- Phase C(Known/Rewrite 最適化・Metaのみ): Enum/Callable の純粋メソッドを直呼び(パリティスモーク追加)
- Phase D(恒久化): レガシー禁止を既定ON、Intern ID 既定ON(ダンプは name+id 併記)
Phase C.1(Selfhost Parity — MirCall/Static Box/Constructor)
- 目的: Rust 線(Builder/VM/LLVM)の MirCall 正規形と Selfhost(Hakorune Script)の Compiler/VM を同形に揃える(SSOT)。
- 範囲:
- Selfhost VM: ModuleFunction 名を受理時に
Box.method/arityへ正規化(将来は Emitter 側で常時/arity付与)。 - Selfhost VM:
Callee::Constructor{box_type}を処理し birth 呼び出しまでを担当。legacyNewBoxは不可。 - Selfhost Emitter: ModuleFunction 発行ヘルパを追加し、
/arityの付与を一元化。
- Selfhost VM: ModuleFunction 名を受理時に
- 非目標:
- Stage‑1 JSON の糖衣→正規形(RewriteKnown 本実装)は次フェーズで導入。
- 検証:
- quick/integration スモークのパリティ回帰ゼロ(ModuleFunction
/arity有無・Constructor 0/1/2/3)。
- quick/integration スモークのパリティ回帰ゼロ(ModuleFunction
今日の更新(P0仕上げ+P1 部分)
- host anchors:
nyash_array_new_hを既定ON(feature gate 撤去)。 - extern_adapter(Array/Map):
nyrt.array.size受領者の HostHandle unwrap/Map.keys/values/size の dev ログ追加。 - Router 再配線:
PluginBoxV2の Array/Map スロットを表テーブル前提で正規に通す(builtin/plugin 両経路)。 - env 読み統一: runtime 配下の直
std::env::varをenv_gate_boxに寄せ(進行中)。 - Router/Adapter 回帰テスト:
map_host_keys_values_return_arrays/map_remove_returns_removed_array_lenなどを追加し、HostSlot/Plugin 両経路で ArrayBox が返ることを固定。 - Type ID 単一起点: Router/extern/loader から直
builtin_type_id("MapBox")や固定値参照(11/12/13)を排除し、crate::types::ids::{map,array,string,by_name}へ統一。rg 監視で再発チェックを継続。 - Reentrant Guard(host slot 中の再入許可): Map.values() → Array.set の再入を slot 配下のみ許可し、連鎖を安定化。
- 実装:
host_api::nyrt_host_call_slot実行中に thread‑localIN_HOST_SLOT=trueを設定し、plugin_loader_unifiedのガードをrecursed && !in_slotに変更。 - 効果: values→Array.set→Array.size の連鎖が正しく成立。代表スモーク
map_values_array_element_vmは PASS。
- 実装:
Runtime meta 層(Callable/Future)の箱化(追加)
- ねらい: 言語機能の足場(Callable/Future)をホスト所有の薄い箱として分離し、プラグイン/外部I/Oの逆流を構造で防止。
- 実装:
src/runtime/meta/{callable,future}/を新設。README と LAYER_GUARD で責務を明記。 - 互換: 既存の
runtime::{callable_box,future_box}re-export は撤去済み。新規・既存ともにruntime::meta::{callable,future}を使用。 - 影響: plugin-only/legacy いずれのビルドでも Callable/Future が安定(router/scheduler への依存のみ)。
HostHandle -14 検知(境界テストの安定化・追加)
- ENV:
HAKO_HOSTHANDLE_TEST_RET_MISMATCH=1(互換NYASH_HOSTHANDLE_TEST_RET_MISMATCH=1)で String.len 経路を -14 として観測。 - 実装: VM の HostSlot/Extern で rc を stdout に
hosthandle-test rc=-14として出力(テスト専用)。 - スモーク:
tools/smokes/v2/profiles/plugins/hosthandle_boundary_suite_vm.shは一時ファイルに退避してから-14を grep(PIPE 経由の出力欠落を回避)。
P1 安定化(今回分)
- builtin ルータ: ARRAY/MAP の host route 判定を dev‑only ログで観測可能に(
HAKO_DEBUG_HOST_SLOT=1)。 - extern_adapter: Array/Map のレガシー分岐を撤去し、HostHandle unwrap をハブ化。
nyrt.array.size/nyrt.map.{keys,values,size}の戻り値を正規化。 - 回帰テスト追加:
- ParameterGuardBox(ENV ON/OFF)。
- ループヘッダ PHI 即時挿入(先頭固定・更新 in‑place)。
- DCE: Method 受け手/Closure captures の Copy 温存。
- Router/Adapter(今回追記):
map_host_keys_values_return_arrays— HostSlot 強制下で keys/values → ArrayBox → len が成立することを固定(ユニット)。map_remove_returns_removed_array_len— remove が削除値(ArrayBox)を返し、直後に len を呼べることを固定(ユニット)。
次の一手(B段)— プラグイン互換トランポリン撤退(完了)
- 旧互換ラッパ(
HAKO_PLUGIN_TRAMPOLINE)は 2025-10-18 時点で撤去。 - len/length エイリアスは各プラグイン(ArrayBox/MapBox/StringBox)が直接解決済み。
- 追加ガードは不要。以降はプラグイン本体と Router テーブルで整合を維持する。
スコープ / 非対象
- 対象
- 呼び出し形状の統一(MIR/Builder/Router/Verifier)
- プラグイン/Extern/HostBridge の呼び出し規約整合
- selfhost 側の静的メソッド(Hako)→ instance 形の糖衣
- 非対象(今回やらない)
- 言語仕様の新規拡張(デフォルト引数・可変長シンタックスなど)
- 既存最適化のチューニング(導入後に計測して別フェーズ)
ゴール(受け入れ基準)
- すべての static 呼び出しが MIR 上で
receiver=Type.singletonに正規化される。 - ルータは receiver を常に受ける前提で動作し、static/instance の分岐が無い。
- Verifier が「receiver 欠落」や「static 直呼び」を検出して Fail‑Fast(開発時)。
- プラグイン ABI は各 Box resolver が直接エイリアスを提供(トランポリンなし)。既存スモーク(quick→plugins→full)緑。
- HostBridge/Extern の呼び出しは
me + args規約に統一。引数正規化(プリミティブ化)で再発無し。
非機能(性能/安定)
- LLVM/VM:
me未使用は inlining + DCE で最終的に消える(同一バイナリ/LTO 前提)。 - 動的リンク(.so)越し: 追加 1 引数のコストは微小。必要箇所は旧 ABI のエイリアスを維持。
設計(構造)
1) 呼び出し正規化(Builder)
- 変換規則(疑似):
Call ModuleFunction("Type.method/N", args)かつmethod != birth→Callee::Method { box_name: "Type", receiver: Some(Type.singleton), method }- 既に instance の場合は変換無し。
- 影響ファイル(予定):
src/mir/builder/calls/method_resolution.rssrc/mir/builder/builder_calls/*(ModuleFunction 経路)
2) ルータ(VM/ランタイム)
- 常に
receiverを渡す前提に一本化(static/instanceの分岐を削除)。 - 不変:
birth()の取り扱い(自動/明示)は既存契約維持。 - 影響(例):
src/backend/mir_interpreter/handlers/calls/function.rs(ModuleFunction ブリッジ)src/backend/mir_interpreter/handlers/boxes/legacy/mod.rs(BoxCall ディスパッチ)src/runtime/method_router_box/*(外部/Plugin 経路の期待形)
3) Verifier(Fail‑Fast)
- ルール:
- ModuleFunction 直呼び(static 形)が残っていたらエラー。
- Method の
receiver=Noneは禁止。 - static 正規化の
meは観測不可(反射禁止)…観測を試みるパスを警告/エラー。
- 影響:
src/mir/verify/*
4) プラグイン ABI 互換(直接 alias)
- len/length などの互換は各プラグインの
resolve実装で提供。 - 追加トランポリンや自動生成スクリプトは不要。
5) HostBridge/Extern(橋渡しの一元化)
- 規約:
Extern(iface.method)のハンドラは常にme + args形に揃える(必要なら内部でsingletonを補う)。 - 引数正規化: BoxRef(ArrayBox 等)→プリミティブ化は既存の
normalize_extern_arg(VM)へ集約。
ガード/フラグ
HAKO_STATIC_AS_SINGLETON=1(NYASH_* alias 可) … 既定OFF、A/B/C 段階で切替。- CLI 既定は変更しない。ENV は短命(導入〜安定化まで)。
ドキュメント / LAYER_GUARD
docs/development/proposals/に本設計の背景と対処(この文書を索引)。- LAYER_GUARD(意図)
- Router 層: 「receiver 必須」。ModuleFunction の直参照禁止。
- Builder 層: static→singleton 正規化必須。抜けはテストで遮断。
- Extern 層: プリミティブ引数化と
me+args契約。
段階導入(A/B/C)
Phase A(実験・既定OFF)
- 実装
- Builder 正規化(static→singleton)
- ルータ分岐の掃除(receiver 常時)
- Verifier 追加(開発時のみ Fail)
- プラグイン resolver の alias 対応を確認
- スモーク
- static/instance 同名メソッドの一致
- HostBridge 経路(extern)での等価性
- 成果: quick 緑 + plugins 代表 PASS
Phase B(小正規化・互換検証)
- Builder 正規化
- ModuleFunction の
me注入を emit 段に集約。Builder 出力はユーザ引数のみ。 - Print を入口で
env.console.log経路へ統一(MirInstruction::Print撤退)。 NewBox発行を Constructor 呼び出しに統一(tests ではcrate::tests::support::constructor_callで固定)。- Eq/Ne の Box 比較は既定で
nyrt.ops.op_eq/Enum は.equals()正規化。プリミティブのみ Compare を維持。
- ModuleFunction の
- Verifier 強化
- Legacy 禁止リストに
NewBoxを追加(既定ON)。 - static ModuleFunction の
me欠落/型不一致を Fail-Fast する検査を追加。
- Legacy 禁止リストに
- Optimizer / Runtime
- Constructor callee を VM handler で取り扱い(既存
handle_new_boxに委譲)。 - Lifecycle ガード(NewBox→birth)の検査を Constructor 経路にも対応。
- Constructor callee を VM handler で取り扱い(既存
- テスト
- 既存 MIR ユニットを Constructor 経路に移行し、受領者/Alloc 効果を固定。
Phase C(完了: Meta Known + Intern 観測)
- Meta Known/Rewrite
EnumBox純粋メソッド(tag/arity/get/equals/toString)を ModuleFunction 経由で統一 → Optimizer がMethod(receiver=Type.singleton)に縮約。ArrayBox/StringBoxmethodRef/2を ModuleFunction 呼び出しへ統一し、CallableBox 経路の素材化を一本化。CallableBox.arity/0は READ 効果に固定(call/async pipeline と分離)。- Gate:
NYASH_REWRITE_META_KNOWN(既定ON/opt‑out:=0)。
- Intern 観測
NYASH_DUMP_INTERN=1で ModuleFunction/Extern 名 ↔ 64bit 安定ID を逐次ダンプ(デバッグ向け)。NYASH_DUMP_INTERN_TABLE=1で関数単位の集計を1回ダンプ(必要時に quick profile で opt-in)。- Selfhost(Hakorune Script)ラインも同じ MirCall 正規形で出力し、Rust VM/LLVM とパリティ確保済み。
Phase D(恒久化へ移行中)
- Verifier
- Legacy 禁止(Print / 生 NewBox / Box Eq・Ne / ModuleFunction
me注入違反)を既定ONに固定。strict モードは警告専用へ移行。
- Legacy 禁止(Print / 生 NewBox / Box Eq・Ne / ModuleFunction
- Optimizer
- Builder で完了した Print/Constructor 正規化の重複Rewriteを撤去し、統一経路のみ残す。
- Known/Rewrite の safe whitelist をメタ箱全般に拡張(CallableBox など追加分を段階導入)。
- Intern
- Stable ID 採用(Callee payloadに name+id を併記)への布石として API/ドキュメントを整備。
- スモーク/プロファイル
- integration profile の自己ホスト parity 期待値を現行 resolver 診断に合わせて更新し、常緑化を完了。
- quick profile は既定で quiet(
NYASH_DUMP_INTERN_TABLE=0)。観測が必要なときのみ opt-in する。 - 2025‑10‑22 時点: quick profile で
userbox_birth_to_string_vmなど 9 件が未緑。setFieldが static singleton 正規化前の経路を通っており、Phase‑31 B/C の follow-up(UserBox/Enum マクロ)で修正予定。- HakoruneJsonCursorBox.* の
/arity付与、using strict 系の exit code/nyash.toml 整理も同バッチで扱う。
- Integration 既定(env/log ポリシー):
HAKO_QUIET=1,HAKO_DISCOVER_MODULES=0,NYASH_NYRT_SILENT_RESULT=1- Meta Known/Rewrite 既定ON(
NYASH_REWRITE_META_KNOWN=1)/Intern Table は既定OFF - Macro runner の子プロセスを無効化(argv超過/ノイズ抑止)
NYASH_MACRO_BOX_CHILD=0,NYASH_MACRO_BOX_CHILD_RUNNER=1,NYASH_NY_COMPILER_TIMEOUT_MS=4000
- ランナーの
filter_noiseで[macro]系/-- child stderr --を抑制(詳細は env ガイド参照)
P0(直近の仕上げ — 小さく強い箱の固定化)
- UsingResolver の SSOT 化(入口一本化)
UsingResolver.resolve(entry) -> ModuleSetを runner/CLI/tests の単一起点にする。- ENV の解釈は UsingResolver 内に集約。下流は
ModuleSetのみを受領。
- Verifier 既定ONの拡張
- Legacy 命令(Print/BoxCall/ExternCall/生 NewBox)を禁止(既定ON)。
- Box Eq/Ne の Compare を禁止(Enum は
.equals()/その他はnyrt.ops.op_eq)。 - ModuleFunction
me注入違反(欠落/型不一致)を Fail‑Fast。
- LLVM Resolver 健全性ガード(debug)
- ret 値が Exit 内定義 or Exit‑PHI の不変条件を debug_assert で検査。
- Exit‑PHI 作成時に preds と incoming が一致することを検査。
Verifier 移行表(strict → 既定ON)
| チェック内容 | strict 導入 | 既定ON | 備考 |
|---|---|---|---|
MirInstruction::Print 検出(Print 経路の禁止) |
Phase B(dev gate) | Phase D | Builder/Emitter で env.console.log 正規化済み。 |
NewBox 命令の禁止(Constructor 経路のみ許容) |
Phase B | Phase B | Builder 正規化と同時に既定ON。 |
Box Eq/Ne の直接 Compare 禁止(Enum は .equals) |
Phase B | Phase D | NYASH_ENUM_STRICT 併用で Enum の診断を強化。 |
ModuleFunction me 注入違反(欠落/型不一致) |
Phase B | Phase D | emit_guard 側での receiver 素材化と連動。 |
Enum Compare(Eq/Ne)(.equals 以外の経路) |
Phase B | (開発フラグ) | NYASH_ENUM_STRICT=1 で強制。将来 Phase‑32 で既定ON 予定。 |
変更対象(実施済/予定ファイル・開始行)
- Builder
src/mir/builder/calls/method_resolution.rs(静的→受領者注入)
- VM/Router
src/backend/mir_interpreter/handlers/calls/function.rssrc/backend/mir_interpreter/handlers/boxes/legacy/mod.rssrc/runtime/method_router_box/*
- Verifier
src/mir/verify/*(新規/既存拡張)
- プラグイン/Extern
src/backend/mir_interpreter/handlers/calls/legacy/extern_handler.rs(規約コメント追加・整合)
テスト計画
- 単体
- static→singleton 正規化の生成確認(MIR 形状)
- receiver 欠落時の Verifier エラー
- 結合
- Router(builtin/plugin)での一貫ディスパッチ
- HostBridge(extern)経路の文字列/数値/配列の正規化
- スモーク
- quick→plugins→full の差分比較(代表)
- selfhost 側の静的 API(Hako)
- meta 層の代表確認(plugins プロファイル):
tools/smokes/v2/profiles/plugins/callable_async_plugin_vm.sh(Future 表示安定)tools/smokes/v2/profiles/plugins/plugin_map_len_vm.sh(len エイリアス整合)tools/smokes/v2/profiles/plugins/set_bad_arity_vm.sh(arity ガード Fail‑Fast)
- 受け入れ基準
- 既存スモーク緑 + 新規スモーク(static/instance 等価)緑
- LLVM/VM 出力差異がゼロ/許容範囲
リスクと対策
- 互換性リスク(プラグイン): resolver alias を Fail-Fast 方針で維持。段階導入。
- 反射の観測: LAYER_GUARD と Verifier。ドキュメントで未定義化を宣言。
- 性能劣化: ベンチで観測し、必要ならホットパスのみ旧 ABI 直呼びエントリを併存。
ロールバック
HAKO_STATIC_AS_SINGLETON=0で旧挙動へ即時復帰。
実装 TODO(P0→)
- Builder に static→singleton 正規化を実装(最小: String/Array/Map 代表)
- Router の receiver 常時渡しを再点検(不要分岐の撤去)
- Verifier 追加(ModuleFunction 直呼び/receiver 欠落の Fail)
- スモーク追加(static/instance 等価、extern 経路)
- Docs 反映(ガイド/リファレンス/ENV 記載)
本日の優先P0(追記)
- Array.size 正規化の徹底(Builder normalize →
Extern("nyrt.array.size")固定、Optimizer で巻き戻し禁止) - EmitGuard 後の LocalSSA 素材化の再確認(values→size 連鎖で未定義参照を出さない)
付録(インターフェース最小定義)
Type.singleton(): &Type(内部/once 初期化、外部観測不可)- 呼び出し正規化:
Static(Type.method(args)) → Method(Type.singleton, args) - Verifier: ModuleFunction 直呼び/receiver=None を禁止
補足: 今回のバグ(ArrayBox(1) 漏れ)の根は「受け渡し規約が2通りあった」こと。構造で1通りに揃えるのが再発防止の最短路だよ。
進捗(実装済み)
Phase A‑1b — 関数スコープのシングルトン・キャッシュ導入(完了)
- 目的:
emit_static_me_placeholder()を各所で都度呼ぶ“分散生成”をやめ、関数内で 1 回だけ生成して再利用する。 - 実装:
MirBuilder.current_fn_singletons: HashMap<String, ValueId>を追加。- 関数開始時にスコープを作り、関数終了時に復元(キャッシュは関数単位で生存)。
maybe_prepend_static_me()からcurrent_fn_singleton(box_name)を利用し、ModuleFunction 経由の静的呼び出しに一貫してmeを付与。
- 変更点(主な参照):
src/mir/builder.rs:176, 448-468— キャッシュ本体とプレースホルダ生成の単一ルート。src/mir/builder/builder_calls/build.rs:39-46, 138-149, 232-241, 249-258, 265-274—maybe_prepend_static_me()による受領者注入。src/mir/builder/builder_calls/lowering.rs— メソッド/静的メソッドを関数化する際にキャッシュを save/restore。src/mir/builder/lifecycle.rs:50—main構築時のキャッシュ初期化。
- 効果:
- 同一関数内の静的メソッド連続呼び出しで
meプレースホルダの重複生成が解消。 - 将来の OnceLock 実体置換を 1 箇所で行える導線ができた。
- 同一関数内の静的メソッド連続呼び出しで
Phase A‑1c — ModuleFunction alias 整備 & Verifier 補強(完了)
- 目的: ModuleFunction 経由の静的呼び出しで receiver 欠落を早期検出し、VM 側も常に receiver あり前提に揃える。
- 実装:
- Verifier:
ModuleFunction(box.method/arity)で Known かつ Box 型の受領者が無い場合に Fail‑Fast。 - Runtime: MethodRouter が Void 受領者を即時 InvalidInstruction とし、legacy fallback も receiver 前提に統一。
- ModuleFunction alias:
handlers/calls/trampolines.rsを追加し、Array/Map/String/Console の ModuleFunction → Method 変換を表駆動化。
- Verifier:
Phase A‑1d — LegacyCallBridgeBox 導入(完了)
- 目的:
emit_instruction(MirInstruction::Call)の直叩きを Builder から排除し、ガード済みの入口に集約する。 - 実装:
src/mir/builder/calls/legacy_bridge/に LegacyCallBridgeBox を新設し、旧emit_legacy_callの本体を移設。- すべてのレガシー Call 発行を
emit_call_with_guard(EmitGuard)経由に統一するヘルパemit_call()を追加。 - BoxCall/PluginCall も
emit_boxcall()でローカルSSA素材化→emit_box_or_plugin_callへ流すようガード化。 src/mir/builder/builder_calls/emit.rsのemit_legacy_callは Box への委譲のみとし、モジュール境界でレガシー経路を閉じ込めた。
- 効果:
- Guard を通さない Call が発生しなくなり、Extern/Method/Global いずれも素材化漏れを検出可能に。
- レガシー経路が単一ファイルに箱化されたことで、段階的削除(将来的なフェーズB/C)や差分追跡が容易に。
Phase A‑2 — singleton 実体(lazy 初期化)導入(完了)
- 目的:
emit_static_me_placeholder()が生成する Void const を実体 BoxRef に置き換える。 - 実装:
runtime/static_singleton.rsを追加し、OnceCell<Mutex<…>>で Box 名ごとのシングルトンを lazy 作成。- Interpreter
handle_constがMirType::Box(..)を検知した場合にstatic_singleton::get()を参照し、VMValue::BoxRefを登録。
- 効果:
- MIR 側は Box 型として
meを扱い、VM 実行時に実体 Arc が注入される。後続のマクロ/Verifier 強化に備えて受領者が具体化された。
- MIR 側は Box 型として
Phase A‑3 — ループヘッダー PHI の「真のループキャリア変数」化(完了)
- 目的: ループ条件で一時文字列(例:
ch)が PHI 化され、i < s.size()が誤ってch < s.size()(String vs Integer)になる問題を根治する。 - 実装:
build.rs側で preheader の変数スナップショットを取得し、事前スキャンで得たassigned_varsとの積を取って PHI 対象を決定。- 実装箇所:
src/mir/loop_builder/build.rs:36付近(preheader_vars キャプチャと loop_carried 生成)、src/mir/loop_builder/phi.rs:14-48(PHI 対象の retain)。
- 効果:
- ループ条件の比較型が安定。
tools/smokes/v2/profiles/quick/apps/json_query_vm.shが PASS(以前のString("0") < 1失敗が消失)。
- ループ条件の比較型が安定。
残タスク(Phase A 継続)
- ✅ ParameterGuardBox 拡張
Builder(pending entry copy を含む)と optimizerrepair_*でdst ∈ paramsを禁止済み。v%0 上書き事故を構造的に遮断。 - ✅ Verifier 層の追加
check_no_parameter_reassignmentを追加し、MIR 完成後もパラメータ再定義を Fail‑Fast。 - EmitGuard 後の ArrayBox.size 固定(継続中)
map.values()→.size()の連鎖をnyrt.array.sizeへ確実に正規化し、EmitGuard の一度だけで素材化が済むよう整理する(Normalizer で Method 巻き戻し禁止)。現在 json_query_vm でString("0") < 1という残バグを追跡中。 - RouterPolicy のログ拡張
Route::BoxCall強制時の理由を mat-trace に記録し、Singleton 正規化後の逸脱調査を容易にする。
Phase A‑1e — Map.size 正規化 & Extern 安定化(完了)
- 目的:
MapBox.(size|len|length)をExtern("nyrt.map.size")に統一し、Method 形の残存で起きる受領者素材化漏れを排除。 - 実装:
src/mir/builder/normalize/map_length.rs新設。normalize::apply_allに組み込み。- Optimizer 側で
nyrt.map.sizeの Method への巻き戻しを抑止。 - Lowering で
MapBox.size/0→nyrt.map.size(recv)の直下ろしを追加。
- 効果: plugins プロファイルの
parity_map_size_has_vm/strict_plugin_map_size_vmが PASS。
Phase A‑1f — Map.keys/values と remove の整備(完了)
- 目的:
Map.values()の戻り値を ArrayBox として統一し、.size()連鎖で未定義値/HostHandle 漏れが発生しないよう構造化。 - 実装:
extern_adapter/collections.rsを新設し、HostHandle unwrap をハブ化。nyrt.map.{keys,values}/nyrt.array.sizeが常に実体 Box を扱うよう統一。- Extern adapter を host slot テーブル経由+ PluginV2 経路の二本で正規化し、両経路で
VMValue::BoxRefを返却。 - Map プラグインの
remove()を HostHandle/TLV 返却へ統一し、削除した値を呼び出し元で即利用できるよう調整。
- テスト:
map_host_keys_values_return_arrays/map_remove_returns_removed_array_lenを追加し、HostSlot 強制と plugin route の双方で ArrayBox が返る・len()が成立することを固定。map_values_array_element_vm.sh/map_remove_returns_value_vm.sh/plugin_map_len_vm.shで代表スモークを常時監視。
Known issues(2025‑10‑19 時点)
- me 注入の誤適用により plugin ModuleFunction へ影響する懸念。
- 対処:
method_index.static_signature()に該当する静的シグネチャのみme合成を許可(Builder/VM 双方でガード)。
- 対処:
- FileBox 系の
use of undefined valueは引数素材化の漏れが原因。- 対処: mir_call 作成前後の LocalSSA を全発行経路に適用済みかスイープし、欠落箇所を補強(EmitGuard で統一)。
- Plugin ABI: 既存 C ABI 互換エントリを registry に登録し、外部呼び出し経路を段階移行(resolver alias で完結)。
- Docs/Tests: Phase-31 変更点のドキュメント整備とスモーク追加(singleton/Verifier 回り)。
現在の既知問題(dev)
- quick→plugins→full でのカテゴリ 2/3(出力差・モジュール解決)の再確認が未実施。LegacyCallBridgeBox 導入後、差分の再スキャンが P0‑5 で残っている。
- Plugin ABI の追加トランポリンは不要。残りは resolver/Router 側の整合性チェックのみ。