Files
hakorune/docs/private/roadmap/phases/phase-31-box-Normalization/INDEX_JA.md

27 KiB
Raw Blame History

Phase31 — Box NormalizationStatic→Singleton 正規化)

最終更新: 20251022Verifier 移行表追加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 で逸脱を FailFast。
  • 導入: フラグ既定OFFで段階導入A→B→C。プラグイン側で len/length エイリアスを正規化(トランポリン撤退済み)。

追加計画Phase31 取り込み)— MIR 正規化とアーキ境界の固定

  • MirCall 一本化(最小カノニカル形)
    • 許可: Method / ModuleFunction("Box.m/arity") / Constructor / Extern(補助: Value/Closure
    • 禁止: Legacy BoxCall/ExternCall/Print/生 NewBox、Box Eq/NeEnum は .equals()、その他は nyrt.ops.op_eq
    • 参考: docs/development/architecture/mir/mir-canonical-calls.md
  • Three RingsCore/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 ADocs/型定義のみ): 文書の追加・MirCall::CallFlags の拡張may_throw/tail_ok/inline_hint
  • Phase BBuilder/Verifierの小正規化: NewBox→Constructor、Print撤退、Box Eq/Ne 正規化strictガードは既存を拡張
  • Phase CKnown/Rewrite 最適化・Metaのみ: Enum/Callable の純粋メソッドを直呼び(パリティスモーク追加)
  • Phase D恒久化: レガシー禁止を既定ON、Intern ID 既定ONダンプは name+id 併記)

Phase C.1Selfhost Parity — MirCall/Static Box/Constructor

  • 目的: Rust 線Builder/VM/LLVMの MirCall 正規形と SelfhostHakorune Scriptの Compiler/VM を同形に揃えるSSOT
  • 範囲:
    • Selfhost VM: ModuleFunction 名を受理時に Box.method/arity へ正規化(将来は Emitter 側で常時 /arity 付与)。
    • Selfhost VM: Callee::Constructor{box_type} を処理し birth 呼び出しまでを担当。legacy NewBox は不可。
    • Selfhost Emitter: ModuleFunction 発行ヘルパを追加し、/arity の付与を一元化。
  • 非目標:
    • Stage1 JSON の糖衣→正規形RewriteKnown 本実装)は次フェーズで導入。
  • 検証:
    • quick/integration スモークのパリティ回帰ゼロModuleFunction /arity 有無・Constructor 0/1/2/3

今日の更新P0仕上げP1 部分)

  • host anchors: nyash_array_new_h を既定ONfeature gate 撤去)。
  • extern_adapter(Array/Map): nyrt.array.size 受領者の HostHandle unwrapMap.keys/values/size の dev ログ追加。
  • Router 再配線: PluginBoxV2 の Array/Map スロットを表テーブル前提で正規に通すbuiltin/plugin 両経路)。
  • env 読み統一: runtime 配下の直 std::env::varenv_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 Guardhost slot 中の再入許可): Map.values() → Array.set の再入を slot 配下のみ許可し、連鎖を安定化。
    • 実装: host_api::nyrt_host_call_slot 実行中に threadlocal IN_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 を grepPIPE 経由の出力欠落を回避)。

P1 安定化(今回分)

  • builtin ルータ: ARRAY/MAP の host route 判定を devonly ログで観測可能に(HAKO_DEBUG_HOST_SLOT=1)。
  • extern_adapter: Array/Map のレガシー分岐を撤去し、HostHandle unwrap をハブ化。nyrt.array.size/nyrt.map.{keys,values,size} の戻り値を正規化。
  • 回帰テスト追加:
    • ParameterGuardBoxENV ON/OFF
    • ループヘッダ PHI 即時挿入(先頭固定・更新 inplace
    • 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 直呼び」を検出して FailFast開発時
  • プラグイン 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 != birthCallee::Method { box_name: "Type", receiver: Some(Type.singleton), method }
    • 既に instance の場合は変換無し。
  • 影響ファイル(予定):
    • src/mir/builder/calls/method_resolution.rs
    • src/mir/builder/builder_calls/*ModuleFunction 経路)

2) ルータVM/ランタイム)

  • 常に receiver を渡す前提に一本化static/instanceの分岐を削除
  • 不変: birth() の取り扱い(自動/明示)は既存契約維持。
  • 影響(例):
    • src/backend/mir_interpreter/handlers/calls/function.rsModuleFunction ブリッジ)
    • src/backend/mir_interpreter/handlers/boxes/legacy/mod.rsBoxCall ディスパッチ)
    • src/runtime/method_router_box/*(外部/Plugin 経路の期待形)

3) VerifierFailFast

  • ルール:
    • 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 を補う)。
  • 引数正規化: BoxRefArrayBox 等)→プリミティブ化は既存の normalize_extern_argVMへ集約。

ガード/フラグ

  • HAKO_STATIC_AS_SINGLETON=1NYASH_* 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_eqEnum は .equals() 正規化。プリミティブのみ Compare を維持。
  • Verifier 強化
    • Legacy 禁止リストに NewBox を追加既定ON
    • static ModuleFunction の me 欠落/型不一致を Fail-Fast する検査を追加。
  • Optimizer / Runtime
    • Constructor callee を VM handler で取り扱い(既存 handle_new_box に委譲)。
    • Lifecycle ガードNewBox→birthの検査を Constructor 経路にも対応。
  • テスト
    • 既存 MIR ユニットを Constructor 経路に移行し、受領者/Alloc 効果を固定。

Phase C完了: Meta Known + Intern 観測)

  • Meta Known/Rewrite
    • EnumBox 純粋メソッドtag/arity/get/equals/toStringを ModuleFunction 経由で統一 → Optimizer が Method(receiver=Type.singleton) に縮約。
    • ArrayBox/StringBox methodRef/2 を ModuleFunction 呼び出しへ統一し、CallableBox 経路の素材化を一本化。
    • CallableBox.arity/0 は READ 効果に固定call/async pipeline と分離)。
    • Gate: NYASH_REWRITE_META_KNOWN既定ONoptout: =0)。
  • Intern 観測
    • NYASH_DUMP_INTERN=1 で ModuleFunction/Extern 名 ↔ 64bit 安定ID を逐次ダンプ(デバッグ向け)。
    • NYASH_DUMP_INTERN_TABLE=1 で関数単位の集計を1回ダンプ必要時に quick profile で opt-in
    • SelfhostHakorune Scriptラインも同じ MirCall 正規形で出力し、Rust VM/LLVM とパリティ確保済み。

Phase D恒久化へ移行中

  • Verifier
    • Legacy 禁止Print / 生 NewBox / Box Eq・Ne / ModuleFunction me 注入違反を既定ONに固定。strict モードは警告専用へ移行。
  • Optimizer
    • Builder で完了した Print/Constructor 正規化の重複Rewriteを撤去し、統一経路のみ残す。
    • Known/Rewrite の safe whitelist をメタ箱全般に拡張CallableBox など追加分を段階導入)。
  • Intern
    • Stable ID 採用Callee payloadに name+id を併記)への布石として API/ドキュメントを整備。
  • スモーク/プロファイル
    • integration profile の自己ホスト parity 期待値を現行 resolver 診断に合わせて更新し、常緑化を完了。
    • quick profile は既定で quietNYASH_DUMP_INTERN_TABLE=0)。観測が必要なときのみ opt-in する。
    • 20251022 時点: quick profile で userbox_birth_to_string_vm など 9 件が未緑。
      • setField が static singleton 正規化前の経路を通っており、Phase31 B/C の follow-upUserBox/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 既定ONNYASH_REWRITE_META_KNOWN=1Intern 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 注入違反(欠落/型不一致)を FailFast。
  • LLVM Resolver 健全性ガードdebug
    • ret 値が Exit 内定義 or ExitPHI の不変条件を debug_assert で検査。
    • ExitPHI 作成時に preds と incoming が一致することを検査。

Verifier 移行表strict → 既定ON

チェック内容 strict 導入 既定ON 備考
MirInstruction::Print 検出Print 経路の禁止) Phase Bdev 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 で強制。将来 Phase32 で既定ON 予定。

変更対象(実施済/予定ファイル・開始行)

  • Builder
    • src/mir/builder/calls/method_resolution.rs(静的→受領者注入)
  • VM/Router
    • src/backend/mir_interpreter/handlers/calls/function.rs
    • src/backend/mir_interpreter/handlers/boxes/legacy/mod.rs
    • src/runtime/method_router_box/*
  • Verifier
    • src/mir/verify/*(新規/既存拡張)
  • プラグイン/Extern
    • src/backend/mir_interpreter/handlers/calls/legacy/extern_handler.rs(規約コメント追加・整合)

テスト計画

  • 単体
    • static→singleton 正規化の生成確認MIR 形状)
    • receiver 欠落時の Verifier エラー
  • 結合
    • Routerbuiltin/pluginでの一貫ディスパッチ
    • HostBridgeextern経路の文字列/数値/配列の正規化
  • スモーク
    • quick→plugins→full の差分比較(代表)
    • selfhost 側の静的 APIHako
    • meta 層の代表確認plugins プロファイル):
      • tools/smokes/v2/profiles/plugins/callable_async_plugin_vm.shFuture 表示安定)
      • tools/smokes/v2/profiles/plugins/plugin_map_len_vm.shlen エイリアス整合)
      • tools/smokes/v2/profiles/plugins/set_bad_arity_vm.sharity ガード FailFast
  • 受け入れ基準
    • 既存スモーク緑 + 新規スモークstatic/instance 等価)緑
    • LLVM/VM 出力差異がゼロ/許容範囲

リスクと対策

  • 互換性リスク(プラグイン): resolver alias を Fail-Fast 方針で維持。段階導入。
  • 反射の観測: LAYER_GUARD と Verifier。ドキュメントで未定義化を宣言。
  • 性能劣化: ベンチで観測し、必要ならホットパスのみ旧 ABI 直呼びエントリを併存。

ロールバック

  • HAKO_STATIC_AS_SINGLETON=0 で旧挙動へ即時復帰。

実装 TODOP0→

  1. Builder に static→singleton 正規化を実装(最小: String/Array/Map 代表)
  2. Router の receiver 常時渡しを再点検(不要分岐の撤去)
  3. Verifier 追加ModuleFunction 直呼び/receiver 欠落の Fail
  4. スモーク追加static/instance 等価、extern 経路)
  5. 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 A1b — 関数スコープのシングルトン・キャッシュ導入(完了)

  • 目的: 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-274maybe_prepend_static_me() による受領者注入。
    • src/mir/builder/builder_calls/lowering.rs — メソッド/静的メソッドを関数化する際にキャッシュを save/restore。
    • src/mir/builder/lifecycle.rs:50main 構築時のキャッシュ初期化。
  • 効果:
    • 同一関数内の静的メソッド連続呼び出しで me プレースホルダの重複生成が解消。
    • 将来の OnceLock 実体置換を 1 箇所で行える導線ができた。

Phase A1c — ModuleFunction alias 整備 & Verifier 補強(完了)

  • 目的: ModuleFunction 経由の静的呼び出しで receiver 欠落を早期検出し、VM 側も常に receiver あり前提に揃える。
  • 実装:
    • Verifier: ModuleFunction(box.method/arity) で Known かつ Box 型の受領者が無い場合に FailFast。
    • Runtime: MethodRouter が Void 受領者を即時 InvalidInstruction とし、legacy fallback も receiver 前提に統一。
    • ModuleFunction alias: handlers/calls/trampolines.rs を追加し、Array/Map/String/Console の ModuleFunction → Method 変換を表駆動化。

Phase A1d — LegacyCallBridgeBox 導入(完了)

  • 目的: emit_instruction(MirInstruction::Call) の直叩きを Builder から排除し、ガード済みの入口に集約する。
  • 実装:
    • src/mir/builder/calls/legacy_bridge/ に LegacyCallBridgeBox を新設し、旧 emit_legacy_call の本体を移設。
    • すべてのレガシー Call 発行を emit_call_with_guardEmitGuard経由に統一するヘルパ emit_call() を追加。
    • BoxCall/PluginCall も emit_boxcall() でローカルSSA素材化→emit_box_or_plugin_call へ流すようガード化。
    • src/mir/builder/builder_calls/emit.rsemit_legacy_call は Box への委譲のみとし、モジュール境界でレガシー経路を閉じ込めた。
  • 効果:
    • Guard を通さない Call が発生しなくなり、Extern/Method/Global いずれも素材化漏れを検出可能に。
    • レガシー経路が単一ファイルに箱化されたことで、段階的削除将来的なフェーズB/Cや差分追跡が容易に。

Phase A2 — singleton 実体lazy 初期化)導入(完了)

  • 目的: emit_static_me_placeholder() が生成する Void const を実体 BoxRef に置き換える。
  • 実装:
    • runtime/static_singleton.rs を追加し、OnceCell<Mutex<…>> で Box 名ごとのシングルトンを lazy 作成。
    • Interpreter handle_constMirType::Box(..) を検知した場合に static_singleton::get() を参照し、VMValue::BoxRef を登録。
  • 効果:
    • MIR 側は Box 型として me を扱い、VM 実行時に実体 Arc が注入される。後続のマクロ/Verifier 強化に備えて受領者が具体化された。

Phase A3 — ループヘッダー 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-48PHI 対象の retain
  • 効果:
    • ループ条件の比較型が安定。tools/smokes/v2/profiles/quick/apps/json_query_vm.sh が PASS以前の String("0") < 1 失敗が消失)。

残タスクPhase A 継続)

  • ParameterGuardBox 拡張
    Builderpending entry copy を含む)と optimizer repair_*dst ∈ params を禁止済み。v%0 上書き事故を構造的に遮断。
  • Verifier 層の追加
    check_no_parameter_reassignment を追加し、MIR 完成後もパラメータ再定義を FailFast。
  • 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 A1e — 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/0nyrt.map.size(recv) の直下ろしを追加。
  • 効果: plugins プロファイルの parity_map_size_has_vm / strict_plugin_map_size_vm が PASS。

Phase A1f — 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 issues20251019 時点)

  • 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 導入後、差分の再スキャンが P05 で残っている。
  • Plugin ABI の追加トランポリンは不要。残りは resolver/Router 側の整合性チェックのみ。