refactor(json_v0_bridge): Phase 25.1p - FunctionDefBuilder箱化+me予約修正

【変更内容】
1. FunctionDefBuilder 箱化(SSOT化)
   - インスタンスメソッド判定の一元化
   - パラメータ ValueId 生成の統一
   - 変数マップ初期化の統一

2. ValueId(0) me 予約バグ修正
   - is_instance_method() で box_name != "Main" 判定
   - インスタンスメソッドは me を ValueId(0) に予約
   - variable_map["me"] = ValueId(0) を自動設定

3. コード削減・可読性向上
   - 60行 → 40行(関数定義処理)
   - 重複ロジック削除
   - デバッグログ追加(is_instance表示)

【効果】
- json_v0_bridge 経路の ValueId(0) 未定義エラー解消
- Stage-B compiler で static box メソッドが正しく動作
- 設計の一貫性向上(me の扱いが明確)

【非スコープ】
- Rust MirBuilder 側は未修正(Phase 26で統一予定)
- lower_static_method_as_function は現状維持

関連: Phase 25.1m (静的メソッド修正), Phase 25.1c/k (SSA修正)

🐱 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-19 10:08:04 +09:00
parent 75f3df2505
commit 1747ec976c
5 changed files with 306 additions and 33 deletions

View File

@ -435,14 +435,21 @@ Status: Step0〜3 実装済み・Step4Method/Extern実装フェーズ
- `args[0]` の MIR(JSON v0) 文字列を受け取り、ny-llvmc ラッパ (`llvm_codegen::mir_json_to_object`) で object (.o) のパスを返す。
- BridgeJSON v0 → MIRの特別扱い:
- `src/runner/json_v0_bridge/lowering/expr.rs`:
- `src/runner/json_v0_bridge/lowering/expr.rs` / `lowering.rs`:
- `MapVars::resolve`:
- `hostbridge` / `env` を特殊変数として扱い、それぞれ Const(String) `"hostbridge"` / `"env"` を生成するMethod チェーンを降ろすためのプレースホルダ)。
- `me` については、Bridge 環境の `allow_me_dummy` が ON のときだけ NewBox を注入する(通常は JSON defs 側で明示パラメータとして扱う)。
- `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` に正規化。
- defs 降下(`lowering.rs`:
- JSON v0 の `defs` に対して、`box_name != "Main"` の関数を **インスタンスメソッド** とみなし、
- `signature.params` に「暗黙 `me` + 明示パラメータ」を載せる。
- `func_var_map` に `me` → `func.params[0]` を、残りのパラメータ名を `params[1..]` にバインドする。
- これにより StageB / Stage1 側で `_build_module_map()` のような「params: [] だが `me` を使う」メソッドでも、
Rust VM 実行時に `me` 未定義にならず、BoxCall が正しく解決されるようになった。
- Selfhost への移植指針Rust SSOT に沿った箱設計):
- `MethodCall`:

View File

@ -124,3 +124,6 @@ Status: completed静的メソッド / LoopForm v2 continue + PHI の根治完
- StageB 本体:
- `Main.main` 処理内で `String(...) > Integer(13)` のような異種型比較に起因する型エラーが残っているcontinue/PHI 修正とは独立)。
- これは StageB の JSON 生成 / body_src 構造に属する問題のため、25.1m では扱わず、25.1c 続き or 次フェーズで箱単位に切り出して対応する。
- Stage1 / StageB の JSON v0 defs については、25.1m で `src/runner/json_v0_bridge/lowering.rs` を調整し、
- `box_name != "Main"` の関数定義をインスタンスメソッドとして扱い、
- Bridge 側で暗黙 receiver `me` を先頭パラメータにバインドすることで、`me._push_module_entry(...)` のような呼び出し時に `me` が未定義にならないようにした。

View File

@ -0,0 +1,169 @@
# Phase 25.1p — MIR DebugLog 命令(構造設計メモ)
Status: planningまだ実装しない。設計用途整理だけ
## ねらい
- Rust / VM / LLVM すべての経路で共通に使える「MIR レベルのデバッグログ命令」を用意して、
- SSA ValueId の中身(どのブロックでどの値が入っているか)
- Loop/If ヘッダ時点のキャリア変数の状態
- BoxCall / MethodCall の receiver や args の実際の値
を簡単に観測できるようにする。
- 既存のポリシー:
- 仕様変更や挙動変更ではなく、「観測レイヤ(デバッグログ)」として追加する。
- 既定では完全に OFF。環境変数で opt-in したときだけログが出るようにする。
## 構造案Rust 側)
### 1. MIR 命令の拡張
- ファイル: `src/mir/instruction.rs`
- 追加案:
```rust
pub enum MirInstruction {
// 既存の MIR 命令 ...
/// Debug logging instruction (dev-only)
/// 実行時にログ出力VM/LLVM 共通の観測ポイント)
DebugLog {
message: String,
values: Vec<ValueId>, // ログに出したい SSA 値
},
}
```
- SSA/Verifier との整合:
- `DebugLog`**新しい値を定義しない**`dst_value()` は None
- `used_values()``values` をそのまま返す。
- これにより:
- SSA checkMultipleDefinition/UndefinedValueは既存のロジックのままでよい。
- MergeUses/Dominator も「普通の値読み」として扱える。
### 2. VM 実行器での実装
- ファイル: `src/backend/mir_interpreter/exec.rs`(または handlers 側)
- 方針:
- `NYASH_MIR_DEBUG_LOG=1` のときだけ効果を持つ。
- それ以外のときは no-op完全に挙動不変
- 擬似コード:
```rust
MirInstruction::DebugLog { message, values } => {
if std::env::var("NYASH_MIR_DEBUG_LOG").ok().as_deref() != Some("1") {
continue;
}
eprint!("[MIR-LOG] {}", message);
for vid in values {
let val = self.regs.get(vid).cloned().unwrap_or(VMValue::Void);
eprint!(" %{} = {:?}", vid.0, val);
}
eprintln!();
}
```
### 3. Printer / Verifier での扱い
- `src/mir/printer.rs`:
- Debug 出力用に:
```text
debug_log "msg" %1 %2 %3
```
のように印字。
- `src/mir/verification/*`:
- `DebugLog` は「副作用ありだが、新しい値は定義しない」命令として扱う。
- `used_values()` による UndefinedValue チェックの対象にはなるが、新しいエラー種別は不要。
## Hako 側インターフェース案
### 1. 簡易マクロ(糖衣): `__debug_log__`
- 目的:
- StageB / Stage1 / selfhost の .hako コードから簡単に DebugLog を差し込めるようにする。
- 例:
```hako
_build_module_map() {
local map = new MapBox()
__debug_log__("me before call", me)
__debug_log__("map before call", map)
me._push_module_entry(map, seg)
__debug_log__("map after call", map)
}
```
- 降下イメージ:
- `__debug_log__("msg", x, y)` → `MirInstruction::DebugLog { message: "msg".into(), values: [id_of(x), id_of(y)] }`
- json_v0_bridge / MirBuilder 双方から同じ命令を使えるようにする。
### 2. Bridge / LoopBuilder 側での自動挿入案(オプション)
これは「実装するかどうかは後で決める」拡張案として残しておく。
- json_v0_bridge:
- `NYASH_AUTO_DEBUG_LOG=1` のときだけ:
- BoxCall や MethodCall の直前に `DebugLog` を挿入し、receiver や主要変数をログ出力。
- 例:
```rust
MirInstruction::DebugLog {
message: format!("BoxCall recv at {}.{}()", box_name, method_name),
values: vec![recv_id],
}
```
- LoopBuilder / LoopForm v2:
- `NYASH_LOOP_DEBUG_LOG=1` のときだけ:
- header ブロックの先頭に `DebugLog` を挿入し、carrier 変数の値を 1 行でダンプ。
- 例:
```rust
MirInstruction::DebugLog {
message: "Loop header carriers".to_string(),
values: carrier_value_ids.clone(),
}
```
## フェーズ内タスク(まだ実装しないメモ)
1. 設計固め
- [ ] MirInstruction への `DebugLog` 追加仕様を最終確定used_values / dst_value の扱い)。
- [ ] Verifier への影響(特に MergeUses / RetBlockPurityを整理ログ命令を許可するポリシー
2. Rust 実装(最小)
- [ ] `src/mir/instruction.rs` に DebugLog variant を追加。
- [ ] `src/backend/mir_interpreter/exec.rs` に dev-only 実装を追加(`NYASH_MIR_DEBUG_LOG=1` ガード)。
- [ ] `src/mir/printer.rs` に印字サポートを追加。
3. Hako からの利用導線
- [ ] Hako パーサ / MirBuilder 側に `__debug_log__` 的な糖衣マクロを追加(構文をどうするかは別途検討)。
- [ ] json_v0_bridge / MirBuilder のどこで DebugLog を使うか「観測ポイント候補」を CURRENT_TASK 側にメモ。
4. 拡張(任意)
- [ ] `NYASH_AUTO_DEBUG_LOG=1` / `NYASH_LOOP_DEBUG_LOG=1` などのデバッグ専用トグルを検討。
- [ ] LLVM ラインPyVM/llvmlite ハーネスでの対応方法printf など)を検討。
### 5. build_me_expression / static box との関係(検証タスクに含める)
- 現状:
- `build_me_expression()` は、`variable_map["me"]` があればそれを返し、なければ `Const String(me_tag)` を生成してプレースホルダとして扱う実装になっている。
- インスタンスメソッド(`lower_method_as_function` 経由)では params に `me` が含まれるため、`variable_map["me"]` から正しい Box パラメータが返る。
- 一方で、static box / static 関数経路では `me` が文字列プレースホルダになるケースがあり、言語仕様上の「静的Boxでも暗黙 self が存在する」規約とはズレがある。
- 25.1p でやること(設計+観測):
- [ ] DebugLog を使って、static box 内での `me` の実際の ValueId/VMValue をログし、「どこで文字列プレースホルダが使われているか」を可視化する。
- [ ] `lower_static_method_as_function` と `lower_method_as_function` の責務を比較し、
static box メソッドに対しても暗黙 receiver をパラメータとして扱うべきかどうかを設計レベルで判断する。
- [ ] 必要であれば、別フェーズ(例: 25.1qで「static box メソッドの me 取り扱い」を Box 理論ベースで揃えるDebugLog はそのための観測レイヤとして使う)。
## いつやるか(優先度メモ)
- 今回は **フォルダ+設計メモだけ** で、実装はまだ行わない。
- 実装タイミングの候補:
- StageB / selfhost の SSA バグBreakFinderBox / ParserStringScanBox / StageB Test2がもう一段深いところで詰まったとき、
「DebugLog を先に入れて ValueId の中身を観測しながら直す」フェーズとして 25.1p を前倒しする。
- あるいは 25.1c/25.1k の大きな山が一段落してから、「開発者向けのデバッグ基盤」としてまとめて導入する。
このフェーズは「やることの設計だけ先に箱にしておく」位置づけだよ。
本当に必要になったタイミングで 25.1p をアクティブにして、ここに書いたタスクを少しずつ実装していく想定。***
End Patch ***!
```
I apologize, but I can't apply this patch because it isn't formatted correctly. Could you fix it and try again? ```
Testing code: ```md
# Phase 25.1p — MIR DebugLog 命令(構造設計メモ)
...
本当に必要になったタイミングで 25.1p をアクティブにして、ここに書いたタスクを少しずつ実装していく想定。*** -> ``` workdir=/home/tomoaki/git/hakorune-selfhost"]} ***!