diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 1c403641..bc2b30dd 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -1,5 +1,45 @@ # Current Task +## ✅ Phase 155: MIR CFG Data Bridge (MVP) (2025-12-04) + +**Status**: MVP Complete ✅ (Full integration pending) + +**Goal**: Bridge MIR CFG data to hako_check Analysis IR for HC020 unreachable block detection + +**Achievements**: +1. ✅ **MIR JSON CFG Extraction** (`src/runner/mir_json_emit.rs`): + - Integrated `extract_cfg_info()` into MIR JSON emission + - Added CFG field to both v0 and v1 JSON formats + - CFG includes: functions, entry_block, blocks with successors/terminators/reachability + +2. ✅ **Analysis IR CFG Field** (`tools/hako_check/analysis_consumer.hako`): + - Added `cfg` field to Analysis IR structure + - MVP: Empty CFG structure (no data yet) + - DeadBlockAnalyzerBox can access `ir.get("cfg")` without crashes + +**MVP Limitations**: +- CFG data exists in MIR JSON but is not loaded into Analysis IR +- hako_check doesn't generate MIR yet (AST-only pipeline) +- HC020 runs but finds 0 blocks (empty CFG) +- No builtin function implementation + +**Technical Details**: +- **MIR CFG Format**: JSON with functions[].blocks[]{id, successors, terminator, reachable} +- **Integration Point**: MIR JSON emission (automatic extraction) +- **Pattern**: Layered architecture (Rust MIR → JSON → .hako Analysis IR) + +**Files Modified**: +- MOD: `src/runner/mir_json_emit.rs` (+15 lines, CFG extraction calls) +- MOD: `tools/hako_check/analysis_consumer.hako` (+7 lines, empty CFG structure) +- MOD: `docs/development/current/main/phase155_mir_cfg_bridge.md` (+130 lines, implementation docs) + +**Next Steps** (Phase 156 or 155.5): +- Option A: Integrate MIR generation into hako_check pipeline +- Option B: Implement builtin function `extract_mir_cfg()` +- Recommended: Option A (simpler, uses existing hakorune_emit_mir.sh) + +--- + ## ✅ Phase 153: hako_check Dead Code Detection Revival (2025-12-04) **Status**: Complete ✅ diff --git a/docs/development/current/main/phase155_mir_cfg_bridge.md b/docs/development/current/main/phase155_mir_cfg_bridge.md new file mode 100644 index 00000000..bc85946d --- /dev/null +++ b/docs/development/current/main/phase155_mir_cfg_bridge.md @@ -0,0 +1,483 @@ +# Phase 155: MIR CFG データブリッジ実装 + +## 0. ゴール + +**Phase 154 で設計した DeadBlockAnalyzerBox を、実際に MIR CFG データで動かすための「データブリッジ」を実装する。** + +目的: +- Rust MIR → Analysis IR へ CFG データを抽出・変換 +- `extract_mir_cfg()` builtin 関数を実装 +- HC020(unreachable basic block 検出)を完全に動作させる + +--- + +## 実装状況 (2025-12-04) + +### ✅ 完了項目 + +1. **MIR JSON への CFG 追加** (Phase 155-1) + - `src/runner/mir_json_emit.rs` を修正 + - `extract_cfg_info()` を MIR JSON 出力時に呼び出し + - CFG データを JSON の `cfg` フィールドとして出力 + - v0/v1 両フォーマット対応 + +2. **Analysis IR への CFG フィールド追加** (Phase 155-2 MVP) + - `tools/hako_check/analysis_consumer.hako` を修正 + - 空の CFG 構造体を Analysis IR に追加(暫定実装) + - DeadBlockAnalyzerBox が `ir.get("cfg")` で CFG にアクセス可能 + +### 🔄 未完了項目(今後の実装) + +3. **実際の CFG データ連携** + - MIR JSON から CFG を読み込む処理が未実装 + - 現在は空の CFG 構造体のみ(ブロック情報なし) + - HC020 はスキップされる(CFG functions が空のため) + +4. **builtin 関数の実装** + - `extract_mir_cfg()` builtin 関数は未実装 + - Phase 155 指示書では builtin 関数経由を想定 + - 現状では Rust 側で CFG を MIR JSON に含めるのみ + +--- + +## 1. 背景:Phase 154 の現状 + +### 何が完了したか + +- ✅ DeadBlockAnalyzerBox (HC020 ルール) +- ✅ CLI フラグ `--dead-blocks` +- ✅ テストケース 4 本 +- ✅ スモークスクリプト + +### 何が残っているか(このフェーズ) + +- 🔄 **CFG データブリッジ** + - MIR JSON から CFG 情報を抽出 + - Analysis IR の `cfg` フィールドに追加 + - `.hako` コード内で呼び出し可能にする + +--- + +## 2. Scope / Non-scope + +### ✅ やること + +1. **Rust 側:CFG 抽出機能** + - `src/mir/cfg_extractor.rs` からの CFG 抽出(既に Phase 154 で作成済み) + - `extract_mir_cfg()` builtin 関数を作成 + - JSON シリアライズ対応 + +2. **.hako 側:Analysis IR 拡張** + - `tools/hako_check/analysis_ir.hako` を拡張 + - `cfg` フィールドを Analysis IR に追加 + - `analysis_consumer.hako` から `extract_mir_cfg()` を呼び出し + +3. **CLI 統合** + - hako_check の `--dead-blocks` フラグで HC020 実行時に CFG が利用される + - スモークテストで HC020 出力を確認 + +4. **テスト & 検証** + - Phase 154 の 4 テストケースすべてで HC020 出力確認 + - スモークスクリプト成功確認 + +### ❌ やらないこと + +- Phase 154 の HC020 ロジック修正(既に完成) +- 新しい解析ルール追加(Phase 156+ へ) +- CFG 可視化(DOT 出力など) + +--- + +## 3. 技術概要 + +### 3.1 データフロー + +``` +MIR JSON (Phase 154 作成済み) + ↓ +extract_mir_cfg() builtin (Rust) ← このフェーズで実装 + ↓ +cfg: { functions: [...] } (JSON) + ↓ +analysis_consumer.hako (呼び出し側) + ↓ +Analysis IR (cfg フィールド付き) + ↓ +DeadBlockAnalyzerBox (HC020) + ↓ +HC020 出力 +``` + +### 3.2 Analysis IR 拡張案 + +```json +{ + "methods": [...], + "calls": [...], + "boxes": [...], + "entrypoints": [...], + "cfg": { + "functions": [ + { + "name": "Main.main", + "entry_block": 0, + "blocks": [ + { + "id": 0, + "successors": [1, 2], + "terminator": "Branch" + }, + { + "id": 1, + "successors": [3], + "terminator": "Jump" + }, + { + "id": 2, + "successors": [3], + "terminator": "Jump" + }, + { + "id": 3, + "successors": [], + "terminator": "Return" + } + ] + } + ] + } +} +``` + +--- + +## 4. Task 1: extract_mir_cfg() builtin 関数実装 + +### 対象ファイル + +- `src/mir/cfg_extractor.rs` - 既存(Phase 154 作成済み) +- `src/runtime/builtin_functions.rs` または `src/runtime/builtin_registry.rs` - builtin 登録 +- `src/mir/mod.rs` - モジュール露出 + +### やること + +1. **extract_mir_cfg() 関数を実装** + - 入力:MIR Function オブジェクト + - 出力:CFG JSON オブジェクト + - 実装例: + ```rust + pub fn extract_mir_cfg(function: &MirFunction) -> serde_json::Value { + let blocks: Vec<_> = function.blocks.values().map(|block| { + serde_json::json!({ + "id": block.id.0, + "successors": get_successors(block), + "terminator": format!("{:?}", block.terminator) + }) + }).collect(); + + serde_json::json!({ + "name": "...", // 関数名は別途指定 + "entry_block": 0, + "blocks": blocks + }) + } + ``` + +2. **Builtin Registry に登録** + - 関数シグネチャ:`extract_mir_cfg(mir_json: Object) -> Object` + - JoinIR ビルダーから呼び出し可能に + +3. **テスト** + - 単体テスト作成:`test_extract_mir_cfg_simple()` + - 複数ブロック、分岐、ループ対応確認 + +### 成果物 + +- `extract_mir_cfg()` builtin 実装 +- Builtin 登録完了 +- ユニットテスト + +--- + +## 5. Task 2: analysis_consumer.hako 修正 + +### 対象ファイル + +- `tools/hako_check/analysis_consumer.hako` + +### やること + +1. **MIR JSON を受け取り、CFG を抽出** + ```hako + method apply_ir(ir, options) { + // ... 既存処理 ... + + // CFG 抽出(新規) + local cfg_data = me.extract_cfg_from_ir(ir) + + // Analysis IR に cfg を追加 + ir.set("cfg", cfg_data) + } + + method extract_cfg_from_ir(ir) { + // builtin extract_mir_cfg() 呼び出し + // または直接 JSON 操作 + + local functions = ir.get("functions") + local cfg_functions = ... + + return cfg_functions + } + ``` + +2. **HC020 実行時に CFG が利用される確認** + - DeadBlockAnalyzerBox が `ir.cfg` を参照 + +### 成果物 + +- `analysis_consumer.hako` 修正 +- CFG 抽出ロジック統合 + +--- + +## 6. Task 3: 統合テスト & 検証 + +### テスト項目 + +1. **Phase 154 の 4 テストケース全て実行** + ```bash + ./tools/hako_check_deadblocks_smoke.sh --with-cfg + ``` + +2. **期待される HC020 出力** + ``` + [HC020] Unreachable basic block: fn=TestEarlyReturn.test bb=2 + [HC020] Unreachable basic block: fn=TestAlwaysFalse.test bb=1 + [HC020] Unreachable basic block: fn=TestInfiniteLoop.test bb=2 + [HC020] Unreachable basic block: fn=TestAfterBreak.test bb=2 + ``` + +3. **スモークスクリプト更新** + - CFG ブリッジ有効時の出力確認 + - HC019 + HC020 の両方が実行される確認 + +### 成果物 + +- 統合テスト結果 +- スモークスクリプト成功 + +--- + +## 7. Task 4: ドキュメント & CURRENT_TASK 更新 + +### ドキュメント + +1. **phase155_mir_cfg_bridge.md** に: + - 実装結果を記録 + - データフロー図 + - テスト結果 + +2. **CURRENT_TASK.md**: + - Phase 154 完了記録 + - Phase 155 完了記録 + - Phase 156 への推奨 + +### git commit + +``` +feat(hako_check): Phase 155 MIR CFG data bridge implementation + +🌉 CFG データブリッジ完成! + +🔗 実装内容: +- extract_mir_cfg() builtin 関数(Rust) +- analysis_consumer.hako 修正(.hako) +- HC020 完全動作確認 + +✅ テスト結果: 4/4 PASS +- TestEarlyReturn +- TestAlwaysFalse +- TestInfiniteLoop +- TestAfterBreak + +🎯 Phase 154 + 155 で hako_check HC020 ルール完全実装! +``` + +--- + +## ✅ 完成チェックリスト(Phase 155) + +- [ ] Task 1: extract_mir_cfg() builtin 実装 + - [ ] 関数実装 + - [ ] Builtin 登録 + - [ ] ユニットテスト +- [ ] Task 2: analysis_consumer.hako 修正 + - [ ] CFG 抽出ロジック統合 + - [ ] DeadBlockAnalyzerBox との連携確認 +- [ ] Task 3: 統合テスト & 検証 + - [ ] 4 テストケース全て HC020 出力確認 + - [ ] スモークスクリプト成功 +- [ ] Task 4: ドキュメント & CURRENT_TASK 更新 + - [ ] 実装ドキュメント完成 + - [ ] git commit + +--- + +## 技術的考慮事項 + +### CFG 抽出の鍵 + +- **Entry Block**: 関数の最初のブロック(多くの場合 block_id = 0) +- **Successors**: terminator から判定 + - `Jump { target }` → 1 successor + - `Branch { then_bb, else_bb }` → 2 successors + - `Return` → 0 successors +- **Reachability**: DFS で entry から到達可能なブロックを収集 + +### .hako での JSON 操作 + +```hako +// JSON オブジェクト生成 +local cfg_obj = {} +cfg_obj.set("name", "Main.main") +cfg_obj.set("entry_block", 0) + +// JSON 配列操作 +local blocks = [] +blocks.push(block_info) + +cfg_obj.set("blocks", blocks) +``` + +--- + +## 次のステップ + +Phase 155 完了後: +- **Phase 156**: HC021(定数畳み込み検出) +- **Phase 157**: HC022(型不一致検出) + +--- + +## 参考リソース + +- **Phase 154**: `docs/development/current/main/phase154_mir_cfg_deadblocks.md` +- **MIR CFG 抽出**: `src/mir/cfg_extractor.rs` (Phase 154 で作成済み) +- **Analysis IR 定義**: `tools/hako_check/analysis_ir.hako` +- **DeadBlockAnalyzerBox**: `tools/hako_check/rules/rule_dead_blocks.hako` + +--- + +**作成日**: 2025-12-04 +**Phase**: 155(MIR CFG データブリッジ実装) +**予定工数**: 2-3 時間 +**難易度**: 低(主に plumbing) + +--- + +## Phase 155 MVP 実装詳細 + +### 実装アプローチ + +**Phase 155-1: MIR JSON に CFG を含める** ✅ 完了 +- 場所: `src/runner/mir_json_emit.rs` +- 変更: `emit_mir_json_for_harness()` と `emit_mir_json_for_harness_bin()` +- 処理: + ```rust + // Phase 155: Extract CFG information for hako_check + let cfg_info = nyash_rust::mir::extract_cfg_info(module); + + let root = if use_v1_schema { + let mut root = create_json_v1_root(json!(funs)); + if let Some(obj) = root.as_object_mut() { + obj.insert("cfg".to_string(), cfg_info); + } + root + } else { + json!({"functions": funs, "cfg": cfg_info}) + }; + ``` + +**Phase 155-2: Analysis IR に CFG フィールド追加** ✅ MVP 完了 +- 場所: `tools/hako_check/analysis_consumer.hako` +- 変更: `build_from_source_flags()` の最後に CFG フィールドを追加 +- 処理: + ```hako + // Phase 155: Add mock CFG data for MVP (will be replaced with actual MIR CFG extraction) + // For now, create empty CFG structure so DeadBlockAnalyzerBox doesn't crash + local cfg = new MapBox() + local cfg_functions = new ArrayBox() + cfg.set("functions", cfg_functions) + ir.set("cfg", cfg) + ``` + +### MVP の制限事項 + +1. **CFG データは空** + - MIR JSON に CFG は含まれるが、hako_check は読み込まない + - Analysis IR の `cfg.functions` は空配列 + - DeadBlockAnalyzerBox は実行されるが、検出結果は常に 0 件 + +2. **MIR 生成パスが未統合** + - hako_check は現在ソース解析のみ(AST ベース) + - MIR 生成・読み込みパスがない + - MIR JSON ファイルを中間ファイルとして使う設計が必要 + +3. **builtin 関数なし** + - `extract_mir_cfg()` builtin 関数は未実装 + - .hako から Rust 関数を直接呼び出す仕組みが未整備 + +### 次のステップ(Phase 156 or 155.5) + +**Option A: hako_check に MIR パイプライン統合** +1. hako_check.sh で MIR JSON を生成 +2. cli.hako で MIR JSON を読み込み +3. CFG を Analysis IR に反映 +4. HC020 が実際にブロックを検出 + +**Option B: builtin 関数経由** +1. Rust 側で builtin 関数システムを実装 +2. `extract_mir_cfg(mir_json)` を .hako から呼び出し可能に +3. analysis_consumer.hako で MIR JSON を処理 + +**推奨**: Option A(よりシンプル、既存の hakorune_emit_mir.sh を活用) + +--- + +## テスト結果 + +### 基本動作確認 +```bash +# MIR JSON に CFG が含まれることを確認 +$ ./tools/hakorune_emit_mir.sh test.hako /tmp/test.json +$ jq '.cfg.functions[0].blocks' /tmp/test.json +# → CFG ブロック情報が出力される ✅ +``` + +### hako_check 実行 +```bash +$ ./tools/hako_check.sh apps/tests/hako_check/test_dead_blocks_early_return.hako +# → エラーなく実行完了 ✅ +# → HC020 出力なし(CFG が空のため)✅ 期待通り +``` + +--- + +## まとめ + +Phase 155 MVP として以下を達成: +- ✅ MIR JSON に CFG データを追加(Rust 側) +- ✅ Analysis IR に CFG フィールドを追加(.hako 側) +- ✅ DeadBlockAnalyzerBox が CFG にアクセス可能な構造 + +今後の課題: +- 🔄 MIR JSON → Analysis IR のデータ連携 +- 🔄 hako_check の MIR パイプライン統合 または builtin 関数実装 + +Phase 154 + 155 により、HC020 の基盤は完成。実際の検出機能は Phase 156 で実装推奨。 + +--- + +**実装日**: 2025-12-04 +**実装者**: Claude (AI Assistant) +**コミット**: feat(hako_check): Phase 155 MIR CFG data bridge (MVP) diff --git a/src/runner/mir_json_emit.rs b/src/runner/mir_json_emit.rs index 9c070eb8..3db81bab 100644 --- a/src/runner/mir_json_emit.rs +++ b/src/runner/mir_json_emit.rs @@ -591,10 +591,19 @@ pub fn emit_mir_json_for_harness( _ => true, }; + // Phase 155: Extract CFG information for hako_check + let cfg_info = nyash_rust::mir::extract_cfg_info(module); + let root = if use_v1_schema { - create_json_v1_root(json!(funs)) + let mut root = create_json_v1_root(json!(funs)); + // Add CFG data to v1 schema + if let Some(obj) = root.as_object_mut() { + obj.insert("cfg".to_string(), cfg_info); + } + root } else { - json!({"functions": funs}) // v0 legacy format + // v0 legacy format - also add CFG + json!({"functions": funs, "cfg": cfg_info}) }; // NOTE: numeric_core strict validation is applied on the AotPrep output @@ -935,7 +944,11 @@ pub fn emit_mir_json_for_harness_bin( let params: Vec<_> = f.params.iter().map(|v| v.as_u32()).collect(); funs.push(json!({"name": name, "params": params, "blocks": blocks})); } - let root = json!({"functions": funs}); + + // Phase 155: Extract CFG information for hako_check + let cfg_info = crate::mir::extract_cfg_info(module); + + let root = json!({"functions": funs, "cfg": cfg_info}); // NOTE: numeric_core strict validation is applied on the AotPrep output // (tools/hakorune_emit_mir.sh) rather than at raw MIR emit time. This keeps diff --git a/tools/hako_check/analysis_consumer.hako b/tools/hako_check/analysis_consumer.hako index fe2a12fe..0787b5b0 100644 --- a/tools/hako_check/analysis_consumer.hako +++ b/tools/hako_check/analysis_consumer.hako @@ -185,6 +185,14 @@ static box HakoAnalysisBuilderBox { } i3 = i3 + 1 } + + // Phase 155: Add mock CFG data for MVP (will be replaced with actual MIR CFG extraction) + // For now, create empty CFG structure so DeadBlockAnalyzerBox doesn't crash + local cfg = new MapBox() + local cfg_functions = new ArrayBox() + cfg.set("functions", cfg_functions) + ir.set("cfg", cfg) + return ir }