diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 803176a8..1a5f7f4d 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -86,6 +86,17 @@ - infer_type_from_phi は P3-C フォールバック専用に縮退 - 4つの柱(Structure / Scope / JoinIR / Type Hints)完成 +### Phase 69: MIR 決定性 & Trio 経路の整理 ✅ 一部完了(2025-11-30) + +- 目的: LoopSnapshotMergeBox / LoopForm 周辺の Inspector/Trio 依存を整理しつつ、MIR の predecessor 順を決定的にしてフラッキーテストを解消する。 +- 実績: + - 69-1: LoopSnapshotMergeBox と Trio 経路の現状を確認し、merge_exit_with_classification が LocalScopeInspectorBox を引き回しているだけであり、情報自体は LoopScopeShape/ExitAnalysis 側に揃っていることを整理。 + - 69-2: `merge_exit_with_classification` から Inspector 引数を削除し、LoopScopeShape/ExitAnalysis 経由で必要な情報を取る形に縮退(約 42 行削減)。既存の 3 テストはすべて PASS。 + - 69-3: `BasicBlock.predecessors` を `HashSet` → `BTreeSet` に変更するなど、MIR の predecessor イテレーションを決定的にし、これまで非決定性でフラッキーだった 2 つのループ系テストを安定化。loopform 14/14 / merge_exit 3/3 を含む関連テストはすべて PASS。 +- 未了: + - 69-4: Trio 3 箱(LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox)の本体削除は、legacy bridge 完全移行後の Phase 67+ で扱う。 + - 69-5: conservative.rs の docs/ 移設も今後の小フェーズとして残しておく。 + ## 2. 次の一手(Phase 69+) ### 直近の候補タスク diff --git a/docs/development/current/main/phase69-4-trio-deletion-plan.md b/docs/development/current/main/phase69-4-trio-deletion-plan.md new file mode 100644 index 00000000..3e25df2d --- /dev/null +++ b/docs/development/current/main/phase69-4-trio-deletion-plan.md @@ -0,0 +1,211 @@ +# Phase 69-4: Trio 3箱整理・削除プラン + +**目的**: Trio Legacy Boxes (LoopVarClassBox, LoopExitLivenessBox, LocalScopeInspectorBox) の Phase 70 完全削除に向けた準備 + +**方針**: このフェーズは「Trio 3箱をいつ・どう消すかを完全に文章で固める」ところまで。実コード削除は Phase 70 で json_v0_bridge の置き換えとセットで実施。 + +--- + +## Phase 69-4.1: Trio 残存 callsite の最終棚卸し ✅ 完了 + +**実施日**: 2025-11-30 +**実施方法**: Task agent による全コードベース調査 + +### 📊 棚卸し結果サマリー + +**Trio 定義ファイル (1,353行)**: +- `src/mir/phi_core/loop_var_classifier.rs`: 578行 +- `src/mir/phi_core/loop_exit_liveness.rs`: 414行 +- `src/mir/phi_core/local_scope_inspector.rs`: 361行 + +**外部依存箇所 (2箇所)**: +1. **src/mir/join_ir/lowering/loop_form_intake.rs** (~30行) + - 3箱すべてを使用(変数分類・Exit後生存分析・定義位置追跡) + - LoopScopeShape への移行が必要 + +2. **src/mir/phi_core/loop_snapshot_merge.rs** (~60行) + - LocalScopeInspectorBox と LoopVarClassBox を使用 + - Exit PHI 生成で使用中 + +**内部依存 (隠蔽済み)**: +- `src/mir/join_ir/lowering/loop_scope_shape/builder.rs` + - `from_existing_boxes_legacy()` メソッド内で使用 + - すでに phi_core 内部に隠蔽済み + +**合計削減見込み**: ~1,443行 +- 定義ファイル: 1,353行 +- loop_form_intake.rs 使用箇所: ~30行 +- loop_snapshot_merge.rs 使用箇所: ~60行 + +--- + +## Phase 69-4.2: phi_core 側の公開面を絞る ✅ 完了 + +**実施日**: 2025-11-30 +**実施内容**: `src/mir/phi_core/mod.rs` にドキュメント追加 + +### 📝 追加したドキュメント + +**Trio 公開面削減方針 (L17-40)**: +```rust +// Phase 69-4.2: Trio 公開面削減方針 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ⚠️ Trio Legacy Boxes (Phase 70 削除予定): +// - LocalScopeInspectorBox (361行) - 変数定義位置追跡(LoopScopeShapeで代替済み) +// - LoopVarClassBox (578行) - 変数分類(LoopScopeShapeで代替済み) +// - LoopExitLivenessBox (414行) - Exit後生存変数分析(LoopScopeShapeで代替済み) +// +// 現在の外部依存(Phase 69-4.1棚卸し済み): +// 1. src/mir/join_ir/lowering/loop_form_intake.rs (~30行) - LoopScopeShape移行待ち +// 2. src/mir/phi_core/loop_snapshot_merge.rs (~60行) - Exit PHI生成で使用中 +// +// Phase 69-4.2 方針: +// - ✅ pub 公開継続(外部依存2箇所が残存) +// - 🎯 目標: phi_core 内部+テストのみが知る状態(現在達成できず) +// - 📋 Phase 70 実装時: json_v0_bridge 移行後に完全削除 +// +// TODO(Phase 70): json_v0_bridge の LoopScopeShape 移行完了後、以下を削除: +// - pub mod local_scope_inspector; (361行) +// - pub mod loop_var_classifier; (578行) +// - pub mod loop_exit_liveness; (414行) +// - loop_snapshot_merge.rs 内の Trio 使用箇所 (~60行) +// - loop_form_intake.rs 内の Trio 使用箇所 (~30行) +// 合計削減見込み: ~1,443行 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +**loop_exit_liveness への追加マーカー (L64-66)**: +```rust +// ⚠️ Phase 69-4.2: Trio Legacy Box (Phase 70 削除予定) +// - 現在の外部依存: loop_form_intake.rs が使用中 +// - TODO(Phase 70): LoopScopeShape 移行後に削除 +``` + +### 🎯 現状認識 + +**達成できた点**: +- ✅ Trio 3箱の削除計画をコード内に明文化 +- ✅ 外部依存2箇所を明記 +- ✅ Phase 70 削除条件を TODO として記録 + +**達成できなかった点**: +- ❌ pub 公開の除去(外部依存が残存するため継続) +- ❌ 「phi_core 内部+テストのみが知る」状態(Phase 70 で達成) + +**結論**: Phase 69-4.2 の目標「方針の明文化」は達成。pub 除去は Phase 70 で実施。 + +--- + +## Phase 69-4.3: json_v0_bridge の Trio 依存設計 ⏳ 未実施 + +**目標**: loop_form_intake.rs の Trio 依存を LoopScopeShape に置き換える設計を固める + +**現在の Trio 使用パターン** (loop_form_intake.rs L169-182): +```rust +// 現在: Trio を直接使用 +let mut inspector = LocalScopeInspectorBox::new(); +inspector.record_snapshot(loop_form.header, &header_vals_mir); +for (bb, snap) in &loop_form.break_snapshots { ... } +for (bb, snap) in &loop_form.continue_snapshots { ... } + +let mut var_classes = LoopVarClassBox::new(); +let mut liveness = LoopExitLivenessBox::new(); +liveness.analyze_exit_usage(&loop_form.exit_checks, &loop_form.exit_snapshots); + +let classified = var_classes.classify_all( + &loop_form.pinned_vars, + &loop_form.carrier_vars, + &inspector, + &exit_preds_set +); +``` + +**移行案** (Task agent 提案): +```rust +// 移行後: LoopScopeShape を使用 +let scope_shape = LoopScopeShape::from_loop_form(loop_form, intake, query)?; +let ordered_pinned = scope_shape.pinned; +let ordered_carriers = scope_shape.carriers; +// 必要な情報はすべて LoopScopeShape から取得 +``` + +**設計方針**: +1. LoopScopeShape が Trio の機能を完全にカバーしていることを確認 +2. loop_form_intake.rs の Trio 使用箇所を LoopScopeShape 経由に書き換え +3. loop_snapshot_merge.rs の Exit PHI 生成も LoopScopeShape 経由に移行 + +--- + +## Phase 69-4.4: Trio 削除条件を固定 ⏳ 未実施 + +**削除条件**: +1. ✅ LoopScopeShape が Trio 機能を完全にカバー(Phase 48-4 達成済み) +2. ⏳ loop_form_intake.rs が LoopScopeShape 経由に移行 +3. ⏳ loop_snapshot_merge.rs が LoopScopeShape 経由に移行 +4. ⏳ 上記2箇所の移行テスト完了(既存テスト退行なし) + +**削除手順** (Phase 70): +1. loop_form_intake.rs の Trio 使用箇所を LoopScopeShape に置き換え (~30行削減) +2. loop_snapshot_merge.rs の Trio 使用箇所を LoopScopeShape に置き換え (~60行削減) +3. 既存テスト実行(14 loopform tests + 関連テスト) +4. Trio 3箱定義ファイル削除 (1,353行削減) +5. phi_core/mod.rs から pub mod 削除 +6. 最終テスト実行・リグレッション確認 + +**合計削減**: ~1,443行 + +--- + +## Phase 69-4.5: Phase 70 への橋渡し ⏳ 未実施 + +**Phase 70 実装タスク**: + +**Step 1: loop_form_intake.rs 移行** +- [ ] LoopScopeShape::from_loop_form() の実装確認 +- [ ] Trio 使用箇所 (L169-182) を LoopScopeShape 経由に書き換え +- [ ] テスト実行(loopform 14テスト) + +**Step 2: loop_snapshot_merge.rs 移行** +- [ ] merge_exit_with_classification() を LoopScopeShape 経由に書き換え +- [ ] Exit PHI 生成ロジックの移行 +- [ ] テスト実行(loopform 14テスト + Exit PHI テスト) + +**Step 3: Trio 完全削除** +- [ ] loop_var_classifier.rs 削除 (578行) +- [ ] loop_exit_liveness.rs 削除 (414行) +- [ ] local_scope_inspector.rs 削除 (361行) +- [ ] phi_core/mod.rs から pub mod 削除 +- [ ] 最終テスト実行 + +**Step 4: ドキュメント更新** +- [ ] CURRENT_TASK.md 更新(Phase 70 完了記録) +- [ ] コミットメッセージ作成(削減行数・移行内容) + +--- + +## まとめ + +### Phase 69-4 達成状況 + +| タスク | 状態 | 成果 | +|--------|------|------| +| 69-4.1: Trio callsite 棚卸し | ✅ 完了 | 1,443行削減計画確定 | +| 69-4.2: phi_core 公開面削減 | ✅ 完了 | 削除方針・TODO 明文化 | +| 69-4.3: json_v0_bridge 設計 | ⏳ 未実施 | LoopScopeShape 移行設計 | +| 69-4.4: 削除条件固定 | ⏳ 未実施 | 削除手順・テスト計画 | +| 69-4.5: Phase 70 橋渡し | ⏳ 未実施 | 実装タスク整理 | + +### Phase 70 への引き継ぎ事項 + +**確定済み**: +- ✅ Trio 削減見込み: ~1,443行 +- ✅ 外部依存: 2箇所(loop_form_intake.rs, loop_snapshot_merge.rs) +- ✅ 削除方針: LoopScopeShape 移行後に完全削除 +- ✅ ドキュメント: phi_core/mod.rs に TODO(Phase 70) マーカー設置 + +**未確定**: +- ⏳ json_v0_bridge の具体的な移行実装 +- ⏳ テスト戦略(退行防止) +- ⏳ 削除順序の最終確定 + +**Phase 70 開始条件**: Phase 69-4.3 ~ 69-4.5 完了後 diff --git a/src/mir/join_ir/lowering/loop_scope_shape/structural.rs b/src/mir/join_ir/lowering/loop_scope_shape/structural.rs new file mode 100644 index 00000000..04d6d114 --- /dev/null +++ b/src/mir/join_ir/lowering/loop_scope_shape/structural.rs @@ -0,0 +1,237 @@ +//! LoopStructuralAnalysis - ループの構造的性質を解析する箱 +//! +//! # Phase 48-5.5: 箱化モジュール化 +//! +//! Case-A/B/C 判定などの構造解析を統一的に扱う。 +//! ExitAnalysis と LoopScopeShape を組み合わせて、ループの構造的性質を判定する。 +//! +//! ## 責務 +//! +//! - ループの構造的性質の解析(出口数、非局所 exit、progress_carrier など) +//! - Case-A minimal 判定(1出口 & 非局所なし & progress_carrier あり) +//! - 将来の Case-B/C 拡張の基盤 +//! +//! ## 設計原則(箱理論) +//! +//! - **単一責務**: ループの構造解析のみ +//! - **質問箱**: is_case_a_minimal() で判定結果を提供 +//! - **依存最小**: ExitAnalysis と LoopScopeShape のみに依存 + +use crate::mir::control_form::{analyze_exits, ExitAnalysis, LoopId}; +use crate::mir::loop_form::LoopForm; + +use super::shape::LoopScopeShape; + +/// ループの構造的性質を解析した結果 +/// +/// # Phase 48-5.5: 箱化の利点 +/// +/// - ExitAnalysis と progress_carrier を組み合わせた判定を一箇所に集約 +/// - Case-A/B/C 判定の拡張が容易 +/// - テスト容易(ExitAnalysis + progress_carrier を渡すだけ) +#[derive(Debug, Clone)] +pub struct LoopStructuralAnalysis { + /// 出口辺の解析結果 + exit_analysis: ExitAnalysis, + /// progress_carrier の有無 + has_progress_carrier: bool, +} + +impl LoopStructuralAnalysis { + /// LoopForm と LoopScopeShape から構造解析を生成 + /// + /// # Example + /// + /// ```ignore + /// let analysis = LoopStructuralAnalysis::from_loop_scope(loop_form, scope); + /// if analysis.is_case_a_minimal() { + /// // Case-A ループとして処理 + /// } + /// ``` + pub fn from_loop_scope(loop_form: &LoopForm, scope: &LoopScopeShape) -> Self { + let loop_id = LoopId(0); + let exit_edges = loop_form.to_exit_edges(loop_id); + let exit_analysis = analyze_exits(&exit_edges); + + Self { + exit_analysis, + has_progress_carrier: scope.progress_carrier.is_some(), + } + } + + /// Case-A minimal: 1出口 & 非局所なし & progress_carrier あり + /// + /// # 必須条件 + /// + /// 1. ループ外出口が 1 グループのみ(`is_single_exit_group()`) + /// 2. 非局所 exit がない(Return/Throw なし) + /// 3. progress_carrier が存在する + /// + /// # Phase 48-5: 構造ベース判定 + /// + /// 従来の関数名ハードコードに依存せず、ループの構造的性質のみで判定する。 + pub fn is_case_a_minimal(&self) -> bool { + self.exit_analysis.is_single_exit_group() && self.has_progress_carrier + } + + /// ExitAnalysis への参照を取得(詳細情報アクセス用) + /// + /// # 用途 + /// + /// - 出口グループ数の確認 + /// - 非局所 exit の有無確認 + /// - 出口先ブロックの取得 + pub fn exit_analysis(&self) -> &ExitAnalysis { + &self.exit_analysis + } + + /// progress_carrier の有無 + pub fn has_progress_carrier(&self) -> bool { + self.has_progress_carrier + } + + // 将来の拡張用(Phase 48-6+ で実装予定) + // + // /// Case-B: 複数出口 & 非局所なし + // pub fn is_case_b_multiple_exits(&self) -> bool { + // self.exit_analysis.loop_exit_groups.len() > 1 + // && self.exit_analysis.nonlocal_exits.is_empty() + // } + // + // /// Case-C: 非局所 exit あり + // pub fn is_case_c_nonlocal(&self) -> bool { + // !self.exit_analysis.nonlocal_exits.is_empty() + // } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mir::control_form::{ExitEdge, ExitKind, LoopId}; + use crate::mir::{BasicBlockId, ValueId}; + use std::collections::{BTreeMap, BTreeSet}; + + fn make_single_exit_analysis() -> ExitAnalysis { + let exit_edges = vec![ExitEdge { + id: crate::mir::control_form::ExitEdgeId(0), + loop_id: LoopId(0), + from: BasicBlockId::new(4), + to: BasicBlockId::new(100), + kind: ExitKind::ConditionFalse, + }]; + analyze_exits(&exit_edges) + } + + fn make_multiple_exit_analysis() -> ExitAnalysis { + let exit_edges = vec![ + ExitEdge { + id: crate::mir::control_form::ExitEdgeId(0), + loop_id: LoopId(0), + from: BasicBlockId::new(4), + to: BasicBlockId::new(100), + kind: ExitKind::ConditionFalse, + }, + ExitEdge { + id: crate::mir::control_form::ExitEdgeId(1), + loop_id: LoopId(0), + from: BasicBlockId::new(5), + to: BasicBlockId::new(101), + kind: ExitKind::Break { label: None }, + }, + ]; + analyze_exits(&exit_edges) + } + + fn make_scope_with_carrier() -> LoopScopeShape { + LoopScopeShape { + header: BasicBlockId::new(2), + body: BasicBlockId::new(3), + latch: BasicBlockId::new(4), + exit: BasicBlockId::new(100), + pinned: vec!["s".to_string()].into_iter().collect(), + carriers: vec!["i".to_string()].into_iter().collect(), + body_locals: BTreeSet::new(), + exit_live: vec!["s".to_string(), "i".to_string()] + .into_iter() + .collect(), + progress_carrier: Some("i".to_string()), + variable_definitions: BTreeMap::new(), + } + } + + fn make_scope_without_carrier() -> LoopScopeShape { + LoopScopeShape { + header: BasicBlockId::new(2), + body: BasicBlockId::new(3), + latch: BasicBlockId::new(4), + exit: BasicBlockId::new(100), + pinned: vec!["s".to_string()].into_iter().collect(), + carriers: BTreeSet::new(), + body_locals: BTreeSet::new(), + exit_live: vec!["s".to_string()].into_iter().collect(), + progress_carrier: None, + variable_definitions: BTreeMap::new(), + } + } + + #[test] + fn test_case_a_minimal_positive() { + // 1出口 & 非局所なし & progress_carrier あり → Case-A + let exit_analysis = make_single_exit_analysis(); + let scope = make_scope_with_carrier(); + + let analysis = LoopStructuralAnalysis { + exit_analysis, + has_progress_carrier: scope.progress_carrier.is_some(), + }; + + assert!(analysis.is_case_a_minimal()); + assert!(analysis.has_progress_carrier()); + } + + #[test] + fn test_case_a_minimal_no_carrier() { + // 1出口 & 非局所なし だが progress_carrier なし → Case-A ではない + let exit_analysis = make_single_exit_analysis(); + let scope = make_scope_without_carrier(); + + let analysis = LoopStructuralAnalysis { + exit_analysis, + has_progress_carrier: scope.progress_carrier.is_some(), + }; + + assert!(!analysis.is_case_a_minimal()); + assert!(!analysis.has_progress_carrier()); + } + + #[test] + fn test_case_a_minimal_multiple_exits() { + // 複数出口 → Case-A ではない + let exit_analysis = make_multiple_exit_analysis(); + let scope = make_scope_with_carrier(); + + let analysis = LoopStructuralAnalysis { + exit_analysis, + has_progress_carrier: scope.progress_carrier.is_some(), + }; + + assert!(!analysis.is_case_a_minimal()); + assert!(analysis.has_progress_carrier()); + } + + #[test] + fn test_exit_analysis_access() { + let exit_analysis = make_single_exit_analysis(); + let scope = make_scope_with_carrier(); + + let analysis = LoopStructuralAnalysis { + exit_analysis, + has_progress_carrier: scope.progress_carrier.is_some(), + }; + + // ExitAnalysis へのアクセス確認 + assert!(analysis.exit_analysis().is_single_exit_group()); + assert_eq!(analysis.exit_analysis().loop_exit_groups.len(), 1); + assert_eq!(analysis.exit_analysis().nonlocal_exits.len(), 0); + } +} diff --git a/src/mir/phi_core/mod.rs b/src/mir/phi_core/mod.rs index feb5b110..f881e583 100644 --- a/src/mir/phi_core/mod.rs +++ b/src/mir/phi_core/mod.rs @@ -14,7 +14,32 @@ pub mod if_phi; pub mod loop_snapshot_merge; pub mod loopform_builder; -// Option C PHI bug fix: Box-based design +// Phase 69-4.2: Trio 公開面削減方針 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// ⚠️ Trio Legacy Boxes (Phase 70 削除予定): +// - LocalScopeInspectorBox (361行) - 変数定義位置追跡(LoopScopeShapeで代替済み) +// - LoopVarClassBox (578行) - 変数分類(LoopScopeShapeで代替済み) +// - LoopExitLivenessBox (414行) - Exit後生存変数分析(LoopScopeShapeで代替済み) +// +// 現在の外部依存(Phase 69-4.1棚卸し済み): +// 1. src/mir/join_ir/lowering/loop_form_intake.rs (~30行) - LoopScopeShape移行待ち +// 2. src/mir/phi_core/loop_snapshot_merge.rs (~60行) - Exit PHI生成で使用中 +// +// Phase 69-4.2 方針: +// - ✅ pub 公開継続(外部依存2箇所が残存) +// - 🎯 目標: phi_core 内部+テストのみが知る状態(現在達成できず) +// - 📋 Phase 70 実装時: json_v0_bridge 移行後に完全削除 +// +// TODO(Phase 70): json_v0_bridge の LoopScopeShape 移行完了後、以下を削除: +// - pub mod local_scope_inspector; (361行) +// - pub mod loop_var_classifier; (578行) +// - pub mod loop_exit_liveness; (414行) +// - loop_snapshot_merge.rs 内の Trio 使用箇所 (~60行) +// - loop_form_intake.rs 内の Trio 使用箇所 (~30行) +// 合計削減見込み: ~1,443行 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +// Option C PHI bug fix: Box-based design (Phase 70 削除予定) pub mod local_scope_inspector; pub mod loop_var_classifier; @@ -36,6 +61,9 @@ pub mod phi_builder_box; // Phase 35-5: phi_invariants 削除(JoinIR Verifierに移譲済み) // Phase 26-F-4: Loop Exit Liveness Box - exit後で使われる変数決定箱 +// ⚠️ Phase 69-4.2: Trio Legacy Box (Phase 70 削除予定) +// - 現在の外部依存: loop_form_intake.rs が使用中 +// - TODO(Phase 70): LoopScopeShape 移行後に削除 pub mod loop_exit_liveness; // Phase 61-7.0: Dead code 削除