Files
hakorune/docs/development/current/main/phase229-action-plan.md
nyash-codex d4f90976da refactor(joinir): Phase 244 - ConditionLoweringBox trait unification
Unify condition lowering logic across Pattern 2/4 with trait-based API.

New infrastructure:
- condition_lowering_box.rs: ConditionLoweringBox trait + ConditionContext (293 lines)
- ExprLowerer implements ConditionLoweringBox trait (+51 lines)

Pattern migrations:
- Pattern 2 (loop_with_break_minimal.rs): Use trait API
- Pattern 4 (loop_with_continue_minimal.rs): Use trait API

Benefits:
- Unified condition lowering interface
- Extensible for future lowering strategies
- Clean API boundary between patterns and lowering logic
- Zero code duplication

Test results: 911/911 PASS (+2 new tests)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 02:35:31 +09:00

11 KiB
Raw Blame History

Phase 229 アクションプラン: ConditionAlias 削除

🎯 目標

ConditionAlias 型を削除し、CarrierInfo を簡素化する

  • 削減: CarrierInfo のフィールド 6 → 5
  • 効果: データ整合性チェック箇所 3 → 1
  • 時間: 1〜2時間
  • リスク: 低

📋 実装手順

Step 1: resolve 関数実装 (15分)

ファイル: src/mir/join_ir/lowering/carrier_info.rs

impl CarrierInfo {
    /// Phase 229: Resolve promoted LoopBodyLocal carrier name
    ///
    /// # Arguments
    ///
    /// * `old_name` - Original LoopBodyLocal variable name (e.g., "digit_pos")
    ///
    /// # Returns
    ///
    /// * `Some(carrier_name)` - If this variable was promoted (e.g., "is_digit_pos")
    /// * `None` - If not a promoted variable
    ///
    /// # Design
    ///
    /// Uses naming convention: "var_name" → "is_var_name"
    /// Alternative: Linear search in carriers with original_name field
    pub fn resolve_promoted_carrier(&self, old_name: &str) -> Option<&str> {
        // Check if this variable was promoted
        if !self.promoted_loopbodylocals.contains(&old_name.to_string()) {
            return None;
        }

        // Naming convention: "digit_pos" → "is_digit_pos"
        let expected_name = format!("is_{}", old_name);
        self.carriers.iter()
            .find(|c| c.name == expected_name)
            .map(|c| c.name.as_str())
    }
}

追加箇所: line 420 付近CarrierInfo impl ブロックの最後)


Step 2: pattern2_with_break.rs 修正 (20分)

ファイル: src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs

削除 (line 354-391):

// Phase 224-D: Add condition aliases to ConditionEnv
// This allows promoted variables to be referenced by their original names in conditions
for alias in &carrier_info.condition_aliases {
    // ... 約40行削除
}

追加 (line 354):

// Phase 229: Resolve promoted LoopBodyLocal variables dynamically
// This replaces condition_aliases with on-demand resolution
for promoted_var in &carrier_info.promoted_loopbodylocals {
    if let Some(carrier_name) = carrier_info.resolve_promoted_carrier(promoted_var) {
        // Find carrier's join_id in carriers list (BEFORE filtering, with join_ids)
        if let Some(carrier) = carriers_with_join_ids.iter().find(|c| c.name == carrier_name) {
            if let Some(join_id) = carrier.join_id {
                // Add dynamic mapping: old_name → carrier's join_id
                env.insert(promoted_var.clone(), join_id);
                eprintln!(
                    "[pattern2/phase229] Resolved promoted variable '{}' → carrier '{}' (join_id={:?})",
                    promoted_var, carrier_name, join_id
                );
            } else {
                eprintln!(
                    "[pattern2/phase229] WARNING: Promoted carrier '{}' has no join_id yet!",
                    carrier_name
                );
            }
        } else {
            eprintln!(
                "[pattern2/phase229] WARNING: Promoted carrier '{}' not found in carriers list",
                carrier_name
            );
        }
    }
}

Step 3: Promoter 修正 (20分)

3-1. Trim Promoter

ファイル: src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs

削除 (line 87-90):

carrier_info.condition_aliases.push(crate::mir::join_ir::lowering::carrier_info::ConditionAlias {
    old_name: trimmed_var_name.to_string(),
    carrier_name: carrier_var_name.clone(),
});

コメント追加 (line 87):

// Phase 229: promoted_loopbodylocals already records this promotion
// No need for condition_aliases - resolved dynamically via CarrierInfo::resolve_promoted_carrier()

3-2. DigitPos Promoter

ファイル: src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs

削除 (line 203-206):

carrier_info.condition_aliases.push(crate::mir::join_ir::lowering::carrier_info::ConditionAlias {
    old_name: var_name.to_string(),
    carrier_name: carrier_var_name.clone(),
});

コメント追加 (line 203):

// Phase 229: promoted_loopbodylocals already records this promotion
// No need for condition_aliases - resolved dynamically via CarrierInfo::resolve_promoted_carrier()

Step 4: ConditionAlias 型削除 (10分)

ファイル: src/mir/join_ir/lowering/carrier_info.rs

削除 (line 85-101):

/// Phase 224-D: Condition alias for promoted LoopBodyLocal variables
///
/// Maps old variable names to their promoted carrier names for condition resolution.
/// ...
#[derive(Debug, Clone)]
pub struct ConditionAlias {
    pub old_name: String,
    pub carrier_name: String,
}

削除 (line 195):

pub condition_aliases: Vec<ConditionAlias>,

削除 (各 constructor で):

  • line 259: condition_aliases: Vec::new(),
  • line 320: condition_aliases: Vec::new(),
  • line 348: condition_aliases: Vec::new(),
  • line 410-414: merge_other() の condition_aliases マージコード

Step 5: pattern4_carrier_analyzer.rs 修正 (5分)

ファイル: src/mir/builder/control_flow/joinir/patterns/pattern4_carrier_analyzer.rs

削除 (line 76):

condition_aliases: all_carriers.condition_aliases.clone(), // Phase 224-D

削除 (line 299):

condition_aliases: Vec::new(), // Phase 224-D

Step 6: pattern_pipeline.rs 修正 (5分)

ファイル: src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs

削除 (line 412):

condition_aliases: Vec::new(), // Phase 224-D

削除 (line 452):

condition_aliases: Vec::new(), // Phase 224-D

テスト手順

Level 1: ビルド確認 (5分)

cargo build --release 2>&1 | tee /tmp/phase229-build.log
# エラーがないことを確認

Level 2: 単体テスト (5分)

# CarrierInfo resolve テスト(手動確認)
cargo test --lib carrier_info -- --nocapture

# Pattern detection テスト
cargo test --lib loop_pattern_detection -- --nocapture

Level 3: パターンテスト (10分)

# Trim pattern
cargo test --release test_mir_joinir_funcscanner_trim 2>&1 | tee /tmp/phase229-trim.log

# DigitPos pattern
cargo test --release test_loopbodylocal_digitpos 2>&1 | tee /tmp/phase229-digitpos.log

# Pattern 2 (break) integration
cargo test --release test_loop_with_break 2>&1 | tee /tmp/phase229-pattern2.log

Level 4: 決定性テスト (10分)

# 3回実行して ValueId が変わらないことを確認
for i in 1 2 3; do
    echo "=== Run $i ===" | tee -a /tmp/phase229-determinism.log
    cargo test --release test_loop_with_break 2>&1 | grep -E "ValueId|test result" | tee -a /tmp/phase229-determinism.log
done

# 3回の出力が同一であることを確認

Level 5: E2E テスト (20分)

# Smoke tests (loop 関連のみ)
tools/smokes/v2/run.sh --profile quick --filter "loop_*" 2>&1 | tee /tmp/phase229-smoke.log

# Full MIR test suite
cargo test --release --test '*' 2>&1 | grep -E "(test result|FAILED)" | tee /tmp/phase229-full.log

🎯 成功条件

ビルド

  • cargo build --release が成功
  • 警告 0 件unused imports, dead code など)

テスト

  • Trim pattern テストが PASS
  • DigitPos pattern テストが PASS
  • Pattern 2 integration テストが PASS
  • 決定性テスト3回実行で同じ ValueId

コード品質

  • CarrierInfo のフィールド数: 6 → 5
  • condition_aliases 参照: 15箇所 → 0箇所
  • 新しいドキュメントコメント追加

📝 コミットメッセージ

refactor(joinir): Phase 229 - Remove redundant ConditionAlias

Problem:
- ConditionAlias duplicates information already in promoted_loopbodylocals
- Maintaining 3 data structures (promoted_loopbodylocals, carriers, condition_aliases) is error-prone
- confusion: "Why do we need condition_aliases when we have promoted_loopbodylocals?"

Solution:
- Add CarrierInfo::resolve_promoted_carrier() for dynamic resolution
- Remove ConditionAlias type and all usages
- Use promoted_loopbodylocals + naming convention ("var" → "is_var")

Impact:
- CarrierInfo fields: 6 → 5
- Maintenance cost: 3 data structures → 2
- No functional changes (all tests pass)

Files changed:
- carrier_info.rs: Added resolve_promoted_carrier(), removed ConditionAlias
- pattern2_with_break.rs: Dynamic resolution instead of condition_aliases
- loop_body_carrier_promoter.rs: Removed condition_aliases.push()
- loop_body_digitpos_promoter.rs: Removed condition_aliases.push()
- pattern4_carrier_analyzer.rs: Removed condition_aliases fields
- pattern_pipeline.rs: Removed condition_aliases fields

Tests:
- ✅ test_mir_joinir_funcscanner_trim
- ✅ test_loopbodylocal_digitpos
- ✅ test_loop_with_break
- ✅ Determinism test (3 runs with same ValueId)

🚨 リスク管理

低リスク要因

  1. 既存のテストで検証可能

    • Trim pattern テスト
    • DigitPos pattern テスト
    • Pattern 2 integration テスト
  2. 影響範囲が明確

    • CarrierInfo とその使用箇所のみ
    • MIR generation ロジックは変更なし
  3. 後方互換性

    • promoted_loopbodylocals は残る
    • CarrierVar.role は残る
    • 外部インターフェース変更なし

もしもの時の対処

ビルドエラー:

# condition_aliases の参照が残っていたら
rg "condition_aliases" src/mir --type rust
# 漏れを修正

テスト失敗:

# ログを確認
cat /tmp/phase229-pattern2.log | grep -E "ERROR|FAILED|panic"

# resolve が失敗していたら
# → 命名規則の確認("is_" prefix
# → promoted_loopbodylocals に記録されているか確認

決定性失敗:

# 3回実行で ValueId が異なる場合
# → Phase 229 の変更は関係なし(元々の問題)
# → git log で最近の HashMap 変更をチェック

📊 見積もり

タスク 時間
Step 1: resolve 実装 15分
Step 2: pattern2 修正 20分
Step 3: Promoter 修正 20分
Step 4-6: 型削除 20分
テスト実行 50分
ドキュメント更新 15分
合計 2時間20分

チェックリスト

実装時にこのリストを確認:

  • Step 1: CarrierInfo::resolve_promoted_carrier() 実装
  • Step 2: pattern2_with_break.rs の condition_aliases ループ削除
  • Step 3-1: loop_body_carrier_promoter.rs の condition_aliases.push() 削除
  • Step 3-2: loop_body_digitpos_promoter.rs の condition_aliases.push() 削除
  • Step 4: ConditionAlias 型削除
  • Step 5: pattern4_carrier_analyzer.rs 修正
  • Step 6: pattern_pipeline.rs 修正
  • Level 1: ビルド確認
  • Level 2: 単体テスト
  • Level 3: パターンテストTrim, DigitPos, Pattern 2
  • Level 4: 決定性テスト3回実行
  • Level 5: E2E テストSmoke + Full
  • コミットメッセージ作成
  • CURRENT_TASK.md 更新

作成日: 2025-12-10 対象: Phase 229 実装 前提: Phase 227-228 完了 Status: Active
Scope: アクションプランJoinIR/ExprLowerer ライン)