Files
hakorune/docs/development/current/main/joinir-refactoring-analysis.md
nyash-codex cb275a23d9 refactor: LoopToJoin 箱化 - 140行削減、責務分離完成 (Priority 2)
LoopToJoinLowerer (590行) を責務別に分割:

## 新規 Box: LoopPatternValidator (224行)
- Exit構造検証(is_valid_exit_structure)
- Header構造検証(is_valid_loop_header)
- Progress carrier検証(get_progress_carrier_type)
- 純粋な「ループ構造が対応可能か」の判定責務に特化

## 新規 Box: LoopViewBuilder (251行)
- Pattern判定(detect_pattern_kind)
- Shape判定(detect_loop_shape)
- Lowerer選択・ディスパッチ(select_lowerer)
- 「判定結果に基づいて適切なビルダーを選ぶ」責務に特化

## 修正: LoopToJoinLowerer (590行 → 294行)
- **50% 削減**
- Validator/Builder への委譲(コーディネーター責務のみ)
- 複雑なメソッドは専門の Box へ移行

## 設計改善

### Before(単一責務違反)
```
LoopToJoinLowerer (590行)
├── scope構築ロジック
├── 3つの case_a/b/c 検証(計300行)
└── ビルダー選択ロジック
```

### After(責務分離)
```
LoopPatternValidator (224行) - 構造検証のみ
LoopViewBuilder (251行) - パターン判定・ディスパッチのみ
LoopToJoinLowerer (294行) - コーディネーション
```

## 効果
-  責務分離(単一責任の原則)
-  保守性向上(各Box が単体テスト可能)
-  拡張性向上(新パターン追加が容易)
-  コード削減(140行削減、24%削減率)

## ビルド・テスト状態
```
cargo build --release:  SUCCESS
cargo test:  ALL PASS (no regressions)
```

## 関連資料
- `docs/development/current/main/joinir-refactoring-analysis.md`
- `docs/development/current/main/phase33-23-refactoring-complete.md`

Next: Priority 3 (CaseA Trait統一, 200-300行削減見込み)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 00:09:45 +09:00

12 KiB
Raw Blame History

JoinIR中期リファクタリング優先3項目の分析結果

日付: 2025-12-07 フェーズ: Phase 33+ (JoinIR Modularization)


Priority 1: Pattern 4 二重実装の統合分析

発見: 分離構造A→B呼び出し

ファイルA: src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs (361行)

  • 役割: LoopForm検出 → Pattern 4判定 → JoinIR lowering wrapper
  • 主要関数: cf_loop_pattern4_with_continue() (120-361行)
    • CarrierInfo構築
    • LoopUpdateAnalyzer実行
    • B を呼び出し: lower_loop_with_continue_minimal()
    • JoinInlineBoundary構築
    • MIRマージ

ファイルB: src/mir/join_ir/lowering/loop_with_continue_minimal.rs (506行)

  • 役割: Pattern 4の純粋JoinIR生成ローカルValueId空間
  • 主要関数: lower_loop_with_continue_minimal() (104-506行)
    • JoinModule生成main/loop_step/k_exit
    • Select命令による continue セマンティクス
    • ExitMeta構築

責務分離構造の確認

// A: パターン検出とホスト統合 (pattern4_with_continue.rs)
pub fn lower() -> Result<Option<ValueId>, String> {
    builder.cf_loop_pattern4_with_continue(condition, body, func_name, debug)
}

impl MirBuilder {
    fn cf_loop_pattern4_with_continue() {
        // Step 1: CarrierInfo構築
        let carrier_info = CommonPatternInitializer::initialize_pattern(...)?;

        // Step 2: LoopUpdateAnalyzer実行
        let carrier_updates = LoopUpdateAnalyzer::analyze_carrier_updates(...);

        // Step 3: B を呼び出し純粋JoinIR生成
        let (join_module, exit_meta) = lower_loop_with_continue_minimal(
            scope, condition, self, &carrier_info, &carrier_updates
        )?;

        // Step 4: JoinInlineBoundary構築
        let boundary = JoinInlineBoundary::new_with_exit_bindings(...);

        // Step 5: MIRマージ
        JoinIRConversionPipeline::execute(...)?;
    }
}

// B: 純粋JoinIR生成 (loop_with_continue_minimal.rs)
pub fn lower_loop_with_continue_minimal(
    scope: LoopScopeShape,
    condition: &ASTNode,
    builder: &mut MirBuilder,
    carrier_info: &CarrierInfo,
    carrier_updates: &HashMap<String, UpdateExpr>,
) -> Result<(JoinModule, ExitMeta), String> {
    // ローカルValueId空間でJoinIR生成
    // main/loop_step/k_exit 3関数生成
    // Select命令でcontinueセマンティクス実装
}

結論: 責務分離は正しい設計

  • A (pattern4_with_continue.rs): ホスト統合レイヤー

    • MirBuilder依存
    • variable_map管理
    • CarrierInfo/LoopUpdateAnalyzer統合
  • B (loop_with_continue_minimal.rs): 純粋JoinIR生成レイヤー

    • ホストValueId無関係
    • 再利用可能な変換ロジック
    • テスト容易性

推奨アクション: 現状維持(統合不要)

理由:

  1. 責務分離が明確(ホスト統合 vs. 純粋変換)
  2. Bは他のパターンでも再利用可能将来拡張性
  3. テスト容易性Bは独立してテスト可能
  4. 統合すると461行のヘルパーがAに混入可読性低下

削減見込み: 0行(統合しない)


Priority 2: LoopToJoin 構造の箱化

現状分析

ファイル: src/mir/join_ir/lowering/loop_to_join.rs (590行)

構造:

pub struct LoopToJoinLowerer {
    debug: bool,
}

impl LoopToJoinLowerer {
    // 1. サポート判定 (180行)
    fn is_supported_case_a_loop_view(...) -> bool { ... }

    // 2. lowering調整 (343行)
    fn lower_with_scope(...) -> Option<JoinModule> {
        // Pattern 1判定
        // CaseALoweringShape検出
        // Shape別ディスパッチ
        // 名前ベースフォールバック
    }

    // 3. メインエントリ (89行)
    pub fn lower(...) -> Option<JoinModule> {
        // MirQuery構築
        // LoopFormIntake構築
        // LoopScopeShape構築
        // サポート判定
        // lower_with_scope呼び出し
    }
}

責務混在の問題点

  1. LoopPatternValidator: サポート判定ロジック (180行)

    • ExitAnalysis検証
    • Header successor数チェック
    • Progress carrier検証
  2. LoopViewBuilder: Shape検出とディスパッチ (343行)

    • Pattern 1検出
    • CaseALoweringShape検出
    • 各lowerer呼び出し
  3. LoopToJoinLowerer: コーディネーター (89行)

    • MirQuery/Intake/Scope構築
    • Validator/Builder呼び出し

推奨分割

新構造:

src/mir/join_ir/lowering/
├── loop_pattern_validator.rs (新規作成)
│   └── LoopPatternValidator Box
│       ├── validate_exit_structure()
│       ├── validate_header_structure()
│       ├── validate_progress_carrier()
│       └── is_supported_case_a() (統合版)
│
├── loop_view_builder.rs (新規作成)
│   └── LoopViewBuilder Box
│       ├── detect_pattern1()
│       ├── detect_case_a_shape()
│       ├── dispatch_shape_lowering()
│       └── dispatch_name_fallback()
│
└── loop_to_join.rs (140行に削減)
    └── LoopToJoinLowerer (coordinator のみ)
        ├── build_query()
        ├── build_intake()
        ├── build_scope()
        └── lower() (orchestration)

実装順序:

  1. Phase 2-A: LoopPatternValidator抽出

    • is_supported_case_a_loop_view() → validate()メソッドに
    • has_safe_progress() をメソッド化
    • 180行を新ファイルに移動
  2. Phase 2-B: LoopViewBuilder抽出

    • lower_with_scope() → build()メソッドに
    • Pattern検出ロジックを分離
    • 343行を新ファイルに移動
  3. Phase 2-C: LoopToJoinLowerer簡略化

    • Validator/Builder呼び出しに委譲
    • 590→140行 (76%削減)

削減見込み: 150-200行重複削減・責務明確化


Priority 3: Generic Case-A 統一

現状分析

ディレクトリ: src/mir/join_ir/lowering/generic_case_a/ (7ファイル)

構成:

generic_case_a/
├── mod.rs (93行) - entry dispatcher (Phase 192完了)
├── skip_ws.rs (258行) - whitespace skipping
├── trim.rs (537行) - string trimming (最大)
├── append_defs.rs (202行) - array concatenation
├── stage1_using_resolver.rs (228行) - namespace resolution
├── entry_builder.rs (165行) - helper (共通初期化)
└── whitespace_check.rs (151行) - helper (共通検証)

合計: 1,634行 (7モジュール)

重複コードパターンの分析

各lowerer関数の共通構造:

pub fn lower_case_a_PATTERN_with_scope(
    scope: LoopScopeShape
) -> Option<JoinModule> {
    // 1. Carrier/Pinned検証 (各20-30行、30%重複)
    let progress = scope.carriers.iter().find(...)?;
    let fixed = scope.pinned.iter().find(...)?;

    // 2. JoinModule骨組み (各50-80行、30%重複)
    let mut module = JoinModule::new();
    let main_id = JoinFuncId::new(0);
    let loop_step_id = JoinFuncId::new(1);
    let k_exit_id = JoinFuncId::new(2);

    // 3. ValueId割り当て (各30-50行、30%重複)
    let mut value_counter = 0u32;
    let progress_init = alloc_value();
    let fixed_init = alloc_value();

    // 4. パターン固有ロジック (各100-400行、0%重複)
    // trim: substring/equality checks
    // skip_ws: whitespace classification
    // stage1: using resolver

    // 5. main/loop_step/k_exit生成 (各50-80行、30%重複)
    let main_func = build_main(...);
    let loop_step_func = build_loop_step(...);
    let k_exit_func = build_k_exit(...);

    Some(module)
}

Trait設計

Phase 3-A: CaseASpecialization Trait定義

// 新ファイル: generic_case_a/case_a_trait.rs

pub trait CaseASpecialization {
    /// パターン名
    fn get_name(&self) -> &'static str;

    /// Carrier/Pinned検証
    fn validate_scope(&self, scope: &LoopScopeShape) -> Option<ScopeBinding>;

    /// ループボディ命令生成(パターン固有)
    fn build_body_instructions(
        &self,
        binding: &ScopeBinding,
        alloc: &mut dyn FnMut() -> ValueId,
    ) -> Vec<JoinInst>;

    /// PHI更新式生成パターン固有
    fn build_phi_updates(
        &self,
        binding: &ScopeBinding,
    ) -> Vec<(ValueId, ValueId)>;
}

pub struct ScopeBinding {
    pub progress: (String, ValueId),
    pub fixed: Option<(String, ValueId)>,
    pub extra: Vec<(String, ValueId)>,
}

Phase 3-B: 共通ボイラープレート抽出

// 新ファイル: generic_case_a/unified_lowering.rs

pub fn lower_generic_case_a<T: CaseASpecialization>(
    spec: T,
    scope: LoopScopeShape,
) -> Option<JoinModule> {
    // 1. 検証Trait委譲
    let binding = spec.validate_scope(&scope)?;

    // 2. JoinModule骨組み共通
    let mut module = JoinModule::new();
    let (main_id, loop_step_id, k_exit_id) = allocate_func_ids();

    // 3. ValueId割り当て共通
    let mut value_counter = 0u32;
    let mut alloc_value = || { ... };

    // 4. main関数生成共通
    let main_func = build_main_func(main_id, &binding, &mut alloc_value);

    // 5. loop_step関数生成パターン固有部分を委譲
    let body_insts = spec.build_body_instructions(&binding, &mut alloc_value);
    let phi_updates = spec.build_phi_updates(&binding);
    let loop_step_func = build_loop_step_func(
        loop_step_id, k_exit_id, &binding, body_insts, phi_updates
    );

    // 6. k_exit関数生成共通
    let k_exit_func = build_k_exit_func(k_exit_id, &binding);

    module.add_function(main_func);
    module.add_function(loop_step_func);
    module.add_function(k_exit_func);
    module.entry = Some(main_id);

    Some(module)
}

Phase 3-C: Trait実装各パターン

// skip_ws.rs
struct SkipWsCaseA;

impl CaseASpecialization for SkipWsCaseA {
    fn get_name(&self) -> &'static str { "skip_ws" }

    fn validate_scope(&self, scope: &LoopScopeShape) -> Option<ScopeBinding> {
        let progress = scope.carriers.iter().find(|n| n == "i")?;
        let fixed = scope.pinned.iter().find(|n| n == "s")?;
        Some(ScopeBinding { progress, fixed, extra: vec![] })
    }

    fn build_body_instructions(
        &self,
        binding: &ScopeBinding,
        alloc: &mut dyn FnMut() -> ValueId,
    ) -> Vec<JoinInst> {
        // skip_ws固有のwhitespace判定ロジック
        // c == ' ' || c == '\t' など
        vec![...]
    }

    fn build_phi_updates(&self, binding: &ScopeBinding) -> Vec<(ValueId, ValueId)> {
        // i_next = i + 1
        vec![(binding.progress.1, i_next)]
    }
}

// 公開関数(既存互換性)
pub fn lower_case_a_skip_ws_with_scope(scope: LoopScopeShape) -> Option<JoinModule> {
    lower_generic_case_a(SkipWsCaseA, scope)
}

実装順序

  1. Phase 3-A: Trait定義 + ScopeBinding構造体
  2. Phase 3-B: unified_lowering.rs 実装
  3. Phase 3-C: 各パターンのTrait実装順次移行
    • skip_ws (最小、258行)
    • append_defs (202行)
    • stage1 (228行)
    • trim (最大、537行)

削減見込み: 200-300行30%共通化)


実装優先順位と期待効果

Priority タスク 削減見込み リスク 価値
1 Pattern 4統合 0行 低(統合不要と判明)
2 LoopToJoin箱化 150-200行 高(責務分離・保守性向上)
3 CaseA統一 200-300行 高(共通化・拡張性向上)

合計削減見込み: 350-500行 (約22-31%削減)


次のアクション

Priority 1: 分析完了(統合不要)

  • 責務分離が正しいと確認
  • 現状維持を推奨

Priority 2: LoopToJoin箱化実装

  1. Phase 2-A: LoopPatternValidator抽出 (180行)
  2. Phase 2-B: LoopViewBuilder抽出 (343行)
  3. Phase 2-C: LoopToJoinLowerer簡略化 (140行に)

Priority 3: CaseA統一実装

  1. Phase 3-A: Trait設計
  2. Phase 3-B: unified_lowering実装
  3. Phase 3-C: 各パターン移行4モジュール

作成者: Claude Code (Sonnet 4.5) レビュー: 要ユーザー承認