Files
hakorune/docs/development/refactoring/p1-if-handler-boxification-plan.md
nyash-codex fd83903f87 feat(joinir): Phase P1 If Handler boxification - 40% code reduction
## Summary
Refactored loop-internal If statement handling into a boxified module
structure, achieving 154-line reduction (40%) from stmt_handlers.rs
with zero regression.

## Implementation
- Created if_in_loop/ module (9 files, ~480 lines)
  - pattern.rs: IfInLoopPattern enum (5 variants)
  - lowering/{empty,single_var_then,single_var_both,conditional_effect,unsupported}.rs
- Replaced lower_if_stmt_in_loop() (154 lines) with lower_if_stmt_in_loop_boxified()
- Made StatementEffect pub(crate) for module visibility

## Pattern Classification
1. Empty: condition-only check (no side effects)
2. SingleVarThen: if { x = a } → x = cond ? a : x
3. SingleVarBoth: if { x = a } else { x = b } → x = cond ? a : b
4. ConditionalEffect: if pred(v) { acc.push(v) } (filter pattern)
5. Unsupported: complex cases (Phase 54+)

## Test Results
 56 JoinIR tests PASSED (0 failed, 0 regression)
 Build successful (1m 02s)
 Improved maintainability (easier to add new patterns)

## Files Changed
- src/mir/join_ir/frontend/ast_lowerer/
  - if_in_loop/ (9 new files)
  - mod.rs (+2 lines: module + pub use)
  - stmt_handlers.rs (-154 lines: 40% reduction)
- CURRENT_TASK.md (+5 lines: Phase P1 記録)
- docs/development/refactoring/p1-if-handler-boxification-plan.md (new)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 07:20:56 +09:00

419 lines
12 KiB
Markdown
Raw Permalink 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.

# If Handler 箱化モジュール化 実装計画書
## 目次
1. [現状分析](#1-現状分析)
2. [ファイル構成案](#2-ファイル構成案)
3. [IfPattern enum 設計](#3-ifpattern-enum-設計)
4. [段階的実装手順](#4-段階的実装手順)
5. [テスト戦略](#5-テスト戦略)
6. [リスク評価](#6-リスク評価)
7. [タイムライン](#7-タイムライン)
8. [チェックリスト](#8-チェックリスト)
---
## 1. 現状分析
### 1.1 対象コード概要
**ファイル**: `/home/tomoaki/git/hakorune-selfhost/src/mir/join_ir/frontend/ast_lowerer/stmt_handlers.rs`
**対象関数**: `lower_if_stmt_in_loop()` (147-300行)
**現在の実装**: 5段階のケース分岐による If 処理
- **ケース 1**: 空の If164-166行、3行
- **ケース 2**: 単一変数更新 then のみ169-202行、34行
- **ケース 3**: 両方に単一変数更新204-234行、31行
- **ケース 4**: Phase 56 条件付き側効果パターン236-292行、57行
- **ケース 5**: 複雑なケース・未対応294-300行、7行
**合計**: 154行ロジック部分
### 1.2 既存のパターン分類システム
プロジェクトには既に2つのパターン分類システムが存在
1. **IfSelectLowerer** (`src/mir/join_ir/lowering/if_select.rs`)
- MIR レベルの If パターン検出
- `IfPatternType`: Simple/Local の2パターン
- PHI 早期チェック機能
- 176行、箱化済み
2. **IfLoweringDryRunner** (`src/mir/join_ir/lowering/if_dry_runner.rs`)
- MIR モジュール全体スキャナー
- パフォーマンス計測機能
- 統計情報収集
- 166行、箱化済み
### 1.3 設計原則Phase 33-10
**JoinIR は PHI 生成器SSOT、PHI 変換器ではない**
この原則により:
- 既存 PHI を持つ MIR は JoinIR 変換をスキップ
- JoinIR は新規 PHI のみを生成
- 既存経路との共存が可能
---
## 2. ファイル構成案
### 2.1 新規ファイル構成
```
src/mir/join_ir/frontend/ast_lowerer/
├── stmt_handlers.rs (縮小版: ~100行)
│ └── lower_statement() - エントリーポイント
│ └── lower_local_stmt() - Local 処理
│ └── lower_assignment_stmt() - Assignment 処理
│ └── lower_print_stmt() - Print 処理
│ └── lower_method_stmt() - Method 処理
│ └── lower_if_stmt_in_loop() - If ディスパッチャー(リファクタ後)
└── if_in_loop/
├── mod.rs (~40行)
│ └── pub use declarations
├── pattern.rs (~80行)
│ ├── IfInLoopPattern enum
│ ├── detect() 関数
│ └── ヘルパー関数
├── lowering/
│ ├── mod.rs (~20行)
│ ├── empty.rs (~20行) - ケース 1
│ ├── single_var_then.rs (~50行) - ケース 2
│ ├── single_var_both.rs (~50行) - ケース 3
│ ├── conditional_effect.rs (~80行) - ケース 4
│ └── unsupported.rs (~20行) - ケース 5
└── tests.rs (~120行)
└── 各パターンのユニットテスト
```
**新規ファイル合計**: ~480行現在154行 → +326行、構造化コストを含む
### 2.2 ファイル役割分担
| ファイル | 責務 | 行数見積 |
|---------|------|---------|
| `if_in_loop/mod.rs` | 公開API、再エクスポート | 40 |
| `if_in_loop/pattern.rs` | パターン検出ロジック | 80 |
| `if_in_loop/lowering/mod.rs` | lowering 統合 | 20 |
| `if_in_loop/lowering/empty.rs` | 空If処理 | 20 |
| `if_in_loop/lowering/single_var_then.rs` | Then単一変数 | 50 |
| `if_in_loop/lowering/single_var_both.rs` | 両方単一変数 | 50 |
| `if_in_loop/lowering/conditional_effect.rs` | 条件付き側効果 | 80 |
| `if_in_loop/lowering/unsupported.rs` | 未対応処理 | 20 |
| `if_in_loop/tests.rs` | テストスイート | 120 |
| **合計** | | **480** |
---
## 3. IfPattern enum 設計
### 3.1 パターン定義
```rust
//! if_in_loop/pattern.rs
use crate::mir::ValueId;
/// ループ内 If 文のパターン分類
#[derive(Debug, Clone, PartialEq)]
pub enum IfInLoopPattern {
/// ケース 1: 空の If条件チェックのみ
/// if cond { } else { }
Empty {
cond: ValueId,
},
/// ケース 2: 単一変数更新 then のみ
/// if cond { x = expr }
SingleVarThen {
cond: ValueId,
var_name: String,
then_expr: serde_json::Value,
else_val: ValueId, // 元の値
},
/// ケース 3: 両方に単一変数更新(同じ変数)
/// if cond { x = a } else { x = b }
SingleVarBoth {
cond: ValueId,
var_name: String,
then_expr: serde_json::Value,
else_expr: serde_json::Value,
},
/// ケース 4: 条件付き側効果パターンfilter 用)
/// if pred(v) { acc.push(v) }
ConditionalEffect {
cond: ValueId,
receiver_expr: serde_json::Value,
method: String,
args: Vec<serde_json::Value>,
},
/// ケース 5: 複雑なケース(未対応)
Unsupported {
then_stmts_len: usize,
else_stmts_len: usize,
},
}
impl IfInLoopPattern {
/// If 文からパターンを検出
pub fn detect(
stmt: &serde_json::Value,
ctx: &ExtractCtx,
) -> Result<Self, PatternDetectionError> {
// 実装詳細は計画書本編参照
}
}
```
---
## 4. 段階的実装手順
### Step 1: 基礎インフラ構築2-3時間
**目標**: ファイル構成とパターン検出ロジックの実装
**作業内容**:
1. ディレクトリ構造作成
2. `if_in_loop/mod.rs` 作成
3. `if_in_loop/pattern.rs` 実装
4. `if_in_loop/lowering/mod.rs` 作成(スケルトン)
**完了条件**:
- ✅ ファイル構造完成
- ✅ パターン検出ロジックがコンパイル可能
- ✅ 基本テストが通る
### Step 2: 各パターンの lowering 実装4-6時間
**ケース実装順序**:
1. ケース 1: Empty (30分)
2. ケース 2: SingleVarThen (1時間)
3. ケース 3: SingleVarBoth (1時間)
4. ケース 4: ConditionalEffect (2時間) - 最複雑
5. ケース 5: Unsupported (30分)
6. lowering/mod.rs 統合 (1時間)
**完了条件**:
- ✅ 5つのケース全て実装完了
- ✅ lowering/mod.rs で統合完了
- ✅ ユニットテストが通る
### Step 3: stmt_handlers.rs のリファクタリング1-2時間
**変更内容**:
```rust
// stmt_handlers.rs (リファクタ後)
use super::if_in_loop::{IfInLoopPattern, lower_pattern};
impl AstToJoinIrLowerer {
fn lower_if_stmt_in_loop(
&mut self,
stmt: &serde_json::Value,
ctx: &mut ExtractCtx,
) -> (Vec<JoinInst>, StatementEffect) {
// パターン検出
let pattern = IfInLoopPattern::detect(stmt, ctx)
.expect("Failed to detect If pattern");
// パターンに応じた lowering
lower_pattern(&pattern, self, ctx)
}
}
```
**完了条件**:
- ✅ stmt_handlers.rs が簡潔になった154行 → 10行
- ✅ 既存テストが全て通る
- ✅ 動作に変更がない(リファクタリングのみ)
### Step 4: テスト追加2-3時間
**テストケース構成**:
- 各パターンの基本ケース: 5個
- 複雑な式の処理: 5個
- エッジケース: 5個
- エラーハンドリング: 3個
- パターン検出テスト: 5個
- **合計**: 30-40個のテスト
**完了条件**:
- ✅ 各パターンごとに最低2つのテスト
- ✅ エラーケースのテスト
- ✅ 全テストが通る
- ✅ カバレッジ80%以上(理想)
### Step 5: 統合・検証1-2時間
**検証項目**:
- ✅ 既存テスト全て通過
- ✅ 新規テスト全て通過
- ✅ 実際の .hako ファイルで動作確認
- ✅ パフォーマンス劣化なし
- ✅ コンパイル警告ゼロ
---
## 5. テスト戦略
### 5.1 テストレベル
| レベル | 対象 | 目的 | テスト数 |
|--------|------|------|---------|
| **ユニットテスト** | パターン検出ロジック | 各パターンの正確な検出 | 10-15 |
| **統合テスト** | lowering 処理 | 正しい JoinInst 生成 | 10-15 |
| **E2Eテスト** | .hako ファイル実行 | 実際の動作確認 | 3-5 |
| **回帰テスト** | 既存テスト | 変更による影響確認 | 既存全て |
### 5.2 回帰テストリスト
既存テストケースで動作確認が必要なもの:
```bash
# Loop内If テスト
apps/tests/loop_if_phi.hako
apps/tests/loop_if_phi_continue.hako
apps/tests/loop_phi_one_sided.hako
# Macro If テスト
apps/tests/macro/if/assign.hako
apps/tests/macro/if/assign_both_branches.hako
```
---
## 6. リスク評価
### 6.1 技術的リスク
| リスク | 深刻度 | 確率 | 対策 |
|--------|--------|------|------|
| **既存テストの破損** | 高 | 中 | Step 3 で段階的移行、既存ロジック保持 |
| **パフォーマンス劣化** | 中 | 低 | 関数呼び出しオーバーヘッド最小化 |
| **新バグの混入** | 高 | 中 | 包括的テストスイート、A/Bテスト |
### 6.2 回避策:段階的移行
```rust
fn lower_if_stmt_in_loop(...) -> ... {
if std::env::var("HAKO_USE_OLD_IF_HANDLER").is_ok() {
// 旧実装(保持)
self.lower_if_stmt_in_loop_legacy(stmt, ctx)
} else {
// 新実装
let pattern = IfInLoopPattern::detect(stmt, ctx)?;
lower_pattern(&pattern, self, ctx)
}
}
```
---
## 7. タイムライン
### 7.1 スケジュール概要
| フェーズ | 期間 | 作業内容 | 完了判定 |
|---------|------|---------|---------|
| **準備** | 0.5時間 | 要件確認、設計レビュー | ✅ 計画書承認 |
| **Step 1** | 2-3時間 | 基礎インフラ構築 | ✅ パターン検出実装 |
| **Step 2** | 4-6時間 | 各パターン lowering 実装 | ✅ 5ケース全実装 |
| **Step 3** | 1-2時間 | stmt_handlers リファクタ | ✅ 委譲完了 |
| **Step 4** | 2-3時間 | テスト追加 | ✅ カバレッジ80%+ |
| **Step 5** | 1-2時間 | 統合・検証 | ✅ CI通過 |
| **合計** | **10.5-16.5時間** | | |
### 7.2 詳細スケジュール3日間想定
#### Day 14-6時間
- **午前** (2-3時間): Step 1 基礎インフラ構築
- **午後** (2-3時間): Step 2-1, 2-2 開始
#### Day 24-6時間
- **午前** (2-3時間): Step 2-3, 2-4
- **午後** (2-3時間): Step 2-5, 2-6
#### Day 32.5-4.5時間)
- **午前** (1-2時間): Step 3
- **午後** (1.5-2.5時間): Step 4, 5
---
## 8. チェックリスト
### 8.1 実装完了チェックリスト
#### 基礎インフラ (Step 1)
- [ ] ディレクトリ構造作成完了
- [ ] `if_in_loop/mod.rs` 作成完了
- [ ] `if_in_loop/pattern.rs` 実装完了
- [ ] コンパイル成功
- [ ] 基本パターン検出テスト通過
#### パターン lowering (Step 2)
- [ ] `lowering/empty.rs` 実装完了
- [ ] `lowering/single_var_then.rs` 実装完了
- [ ] `lowering/single_var_both.rs` 実装完了
- [ ] `lowering/conditional_effect.rs` 実装完了
- [ ] `lowering/unsupported.rs` 実装完了
- [ ] `lowering/mod.rs` 統合完了
#### リファクタリング (Step 3)
- [ ] `stmt_handlers.rs` 修正完了
- [ ] コンパイル成功
- [ ] 既存テスト全通過
#### テスト (Step 4)
- [ ] `if_in_loop/tests.rs` 作成完了
- [ ] ケース 1-5 各テスト作成
- [ ] パターン検出テスト
- [ ] カバレッジ80%以上達成
#### 統合・検証 (Step 5)
- [ ] 全ユニットテスト通過
- [ ] 全統合テスト通過
- [ ] 既存回帰テスト通過
- [ ] E2Eテスト通過
- [ ] CI/CD通過
---
## 9. 成果物
### 9.1 コード成果物
1. **新規ファイル** (9ファイル、~480行)
2. **修正ファイル** (1ファイル)
- `stmt_handlers.rs` (330行 → 186行、-144行)
3. **純増減**
- 新規: +480行
- 削減: -144行
- **実質: +336行**(構造化コスト含む)
---
## 10. 次のステップ
### 10.1 実装後の展開
1. **Phase 33-12**: IfMerge 実装複数変数PHI対応
2. **Phase 34**: return/break を含む If の JoinIR 表現
3. **Phase 35**: If/PHI レガシー箱の完全削除
---
**作成日**: 2025-11-29
**Phase**: 56 後のリファクタリング計画
**作成者**: Task agent (Plan mode) + Claude Code
**目的**: stmt_handlers.rs の If 処理を箱化モジュール化し、保守性と拡張性を向上