Files
hakorune/docs/development/roadmap/phases/phase-25.1p/README.md
nyash-codex 1747ec976c 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>
2025-11-19 10:08:04 +09:00

170 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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"]} ***!