diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 214b30bd..32aa8201 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -47,6 +47,13 @@ → Task 200-2: JoinInlineBoundaryBuilder 導入(Pattern2 で試験導入、Builder パターンで構築統一化)。 → Task 200-3: JoinIRVerifier 追加(LoopHeader PHI / ExitLine 契約のデバッグ検証)。 → Task 200-4: CURRENT_TASK/overview 微修正(Phase 200 内容を反映)。 + - [x] Phase 201: JoinInlineBoundaryBuilder Pattern3/4 展開 ✅ (2025-12-08) + → Task 201-1: Pattern2 の Builder 使用パターンを正として固める(設計ドキュメント作成)。 + → Task 201-2: Pattern3 を Builder に載せ替え(フィールド直書き排除)。 + → Task 201-3: Pattern4 を Builder に載せ替え(continue/Trim 特例対応)。 + → Task 201-4: 共通ルールチェック & unit test 拡張(2つの新テスト追加)。 + → Task 201-5: ドキュメント更新(overview + CURRENT_TASK)。 + → **成果**: 全パターン(P1/P2/P3/P4)で Builder 統一完了、フィールド直書き完全排除、挙動不変(テスト全 PASS)。 --- diff --git a/docs/development/current/main/joinir-architecture-overview.md b/docs/development/current/main/joinir-architecture-overview.md index 7b434d38..ed5d4f56 100644 --- a/docs/development/current/main/joinir-architecture-overview.md +++ b/docs/development/current/main/joinir-architecture-overview.md @@ -171,12 +171,12 @@ JoinIR ラインで守るべきルールを先に書いておくよ: - 変数再接続はヘッダ PHI の dst を使って `builder.variable_map` を更新(Reconnector)。 - expr 用の PHI には一切触れない(carrier 専用ライン)。 -- **JoinInlineBoundaryBuilder(Phase 200-2 追加)** +- **JoinInlineBoundaryBuilder(Phase 200-2 / Phase 201 完了)** - ファイル: `src/mir/join_ir/lowering/inline_boundary_builder.rs` - 責務: - JoinInlineBoundary の構築を Builder パターンで統一化。 - フィールド直書きの散乱を防ぎ、inputs/outputs/condition_bindings/exit_bindings/loop_var_name/expr_result の設定を fluent API で実施。 - - Pattern2 で試験導入済み(Pattern3/4 は将来適用予定)。 + - **Phase 201 で Pattern1/2/3/4 全てに適用完了**(境界情報組み立てを 1 箇所に集約)。 - **JoinIRVerifier(Phase 200-3 追加)** - ファイル: `src/mir/builder/control_flow/joinir/merge/mod.rs`(debug_assertions 専用関数) diff --git a/docs/development/current/main/joinir-boundary-builder-pattern.md b/docs/development/current/main/joinir-boundary-builder-pattern.md new file mode 100644 index 00000000..624dbaa4 --- /dev/null +++ b/docs/development/current/main/joinir-boundary-builder-pattern.md @@ -0,0 +1,95 @@ +# JoinInlineBoundaryBuilder 使用パターン(Phase 201) + +## Pattern2 の Canonical 形式 + +Pattern2 で確立した Builder 使用パターンを他の Pattern に適用する際の基準。 + +### Builder 構築順序 + +```rust +let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(join_inputs, host_inputs) + .with_condition_bindings(condition_bindings) + .with_exit_bindings(exit_bindings) + .with_loop_var_name(Some(loop_var_name.clone())) + .with_expr_result(fragment_meta.expr_result) + .build(); +``` + +### 各メソッドの意味 + +1. **with_inputs(join_inputs, host_inputs)** + - ループパラメータの橋渡し + - join_inputs: JoinIR 側の ValueId リスト + - host_inputs: MIR 側の ValueId リスト + +2. **with_condition_bindings(bindings)** + - 条件式専用変数の橋渡し + - ConditionBinding のリスト(変数名 + JoinIR ValueId のペア) + +3. **with_exit_bindings(bindings)** + - キャリア出口の橋渡し + - (変数名, JoinIR ValueId) のペア + +4. **with_loop_var_name(name)** + - ループ変数名(LoopHeader PHI 生成用) + - Some(変数名) または None + +5. **with_expr_result(expr)** + - ループが式として返す値 + - Some(ValueId) または None + +### フィールド直書き禁止 + +- ❌ `boundary.loop_var_name = ...;` 等の直接代入は禁止 +- ✅ Builder の fluent API のみ使用 + +### Pattern別の使用パターン + +#### Pattern2 (with break) +```rust +let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(vec![ValueId(0)], vec![loop_var_id]) + .with_condition_bindings(condition_bindings) + .with_exit_bindings(exit_bindings) + .with_loop_var_name(Some(loop_var_name.clone())) + .with_expr_result(fragment_meta.expr_result) + .build(); +``` + +#### Pattern3 (with if-phi) +```rust +let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs( + vec![ValueId(0), ValueId(1)], // i, sum + vec![loop_var_id, sum_var_id], + ) + .with_exit_bindings(vec![ + ("sum".to_string(), ValueId(18)) + ]) + .with_loop_var_name(Some(loop_var_name.clone())) + .build(); +``` + +#### Pattern4 (with continue) +```rust +let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(join_inputs, host_inputs) // Dynamic carrier count + .with_exit_bindings(exit_bindings) + .with_loop_var_name(Some(loop_var_name.clone())) + .build(); +``` + +## Phase 201 目標 + +- ✅ Pattern2/3/4 全てで Builder 使用に統一 +- ✅ フィールド直書き(`boundary.field = value;`)完全排除 +- ✅ 境界情報の組み立てを 1 箇所(Builder)に集約 +- ✅ テスト全 PASS(挙動不変) + +## 実装ファイル + +- Builder 本体: `src/mir/join_ir/lowering/inline_boundary_builder.rs` +- Pattern2: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs` +- Pattern3: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs` +- Pattern4: `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs` diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs b/src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs index ddc9a159..482b4559 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs @@ -92,13 +92,16 @@ impl MirBuilder { }; // Phase 33-22: Create boundary for JoinIR conversion - let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only( - vec![ValueId(0)], // JoinIR's main() parameter (loop variable) - vec![loop_var_id], // Host's loop variable - ); - // Phase 33-16: Set loop_var_name to enable header PHI generation - // This is required for the merge pipeline to generate header PHIs for SSA correctness - boundary.loop_var_name = Some(loop_var_name.clone()); + // Phase 201: Use JoinInlineBoundaryBuilder for clean construction + // Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md + use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs( + vec![ValueId(0)], // JoinIR's main() parameter (loop variable) + vec![loop_var_id], // Host's loop variable + ) + .with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness + .build(); // Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow use super::conversion_pipeline::JoinIRConversionPipeline; diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs index 45862c90..a35d6e03 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs @@ -329,6 +329,7 @@ impl MirBuilder { let exit_bindings = ExitMetaCollector::collect(self, &exit_meta, debug); // Phase 200-2: Use JoinInlineBoundaryBuilder for clean construction + // Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; let boundary = JoinInlineBoundaryBuilder::new() .with_inputs( diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs b/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs index d57cde8c..e6d42357 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs @@ -100,24 +100,28 @@ impl MirBuilder { }; // Phase 33-22: Create boundary for JoinIR conversion + // Phase 201: Use JoinInlineBoundaryBuilder for clean construction + // Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md // Pattern 3 has TWO carriers: i and sum self.trace_varmap("pattern3_before_merge"); - let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings( - vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i, sum init) - vec![loop_var_id, sum_var_id], // Host's loop variables - vec![ + use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; + use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding; + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs( + vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i, sum init) + vec![loop_var_id, sum_var_id], // Host's loop variables + ) + .with_exit_bindings(vec![ // Phase 33-16: Only include non-loop-variable carriers in exit_bindings // The loop variable is handled separately via boundary.loop_var_name - crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding { + LoopExitBinding { carrier_name: "sum".to_string(), join_exit_value: ValueId(18), // k_exit's parameter (sum_final) host_slot: sum_var_id, // variable_map["sum"] } - ], - ); - // Phase 33-16: Set loop_var_name to enable header PHI generation - // This is required for the merge pipeline to generate header PHIs for SSA correctness - boundary.loop_var_name = Some(loop_var_name.clone()); + ]) + .with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness + .build(); // Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow use super::conversion_pipeline::JoinIRConversionPipeline; diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs b/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs index d12c71a7..c0b8a47e 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs @@ -358,13 +358,14 @@ impl MirBuilder { &format!("join_inputs: {:?}", join_inputs.iter().map(|v| v.0).collect::>()) ); - let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings( - join_inputs, // JoinIR's main() parameters (dynamic) - host_inputs, // Host's loop variables (dynamic) - exit_bindings, - ); - // Phase 33-19: Set loop_var_name for proper exit PHI collection - boundary.loop_var_name = Some(loop_var_name.clone()); + // Phase 201: Use JoinInlineBoundaryBuilder for clean construction + // Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md + use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(join_inputs, host_inputs) // Dynamic carrier count + .with_exit_bindings(exit_bindings) + .with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-19: Enable exit PHI collection + .build(); // Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow use super::conversion_pipeline::JoinIRConversionPipeline; diff --git a/src/mir/join_ir/lowering/inline_boundary_builder.rs b/src/mir/join_ir/lowering/inline_boundary_builder.rs index 9b78ea3a..81d3e8d9 100644 --- a/src/mir/join_ir/lowering/inline_boundary_builder.rs +++ b/src/mir/join_ir/lowering/inline_boundary_builder.rs @@ -201,4 +201,58 @@ mod tests { assert_eq!(boundary.join_inputs.len(), 0); assert_eq!(boundary.host_inputs.len(), 0); } + + #[test] + fn test_builder_pattern3_style() { + // Pattern3 style: Two carriers (i + sum), exit_bindings, loop_var_name + use super::super::condition_to_joinir::ConditionBinding; + + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(vec![ValueId(0), ValueId(1)], vec![ValueId(100), ValueId(101)]) + .with_exit_bindings(vec![ + LoopExitBinding { + carrier_name: "sum".to_string(), + join_exit_value: ValueId(18), + host_slot: ValueId(101), + } + ]) + .with_loop_var_name(Some("i".to_string())) + .build(); + + assert_eq!(boundary.join_inputs.len(), 2); + assert_eq!(boundary.host_inputs.len(), 2); + assert_eq!(boundary.exit_bindings.len(), 1); + assert_eq!(boundary.exit_bindings[0].carrier_name, "sum"); + assert_eq!(boundary.loop_var_name, Some("i".to_string())); + assert_eq!(boundary.expr_result, None); + } + + #[test] + fn test_builder_pattern4_style() { + // Pattern4 style: Dynamic carrier count, continue support + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs( + vec![ValueId(0), ValueId(1), ValueId(2)], // i + 2 carriers + vec![ValueId(100), ValueId(101), ValueId(102)] + ) + .with_exit_bindings(vec![ + LoopExitBinding { + carrier_name: "i".to_string(), + join_exit_value: ValueId(11), + host_slot: ValueId(100), + }, + LoopExitBinding { + carrier_name: "sum".to_string(), + join_exit_value: ValueId(20), + host_slot: ValueId(101), + } + ]) + .with_loop_var_name(Some("i".to_string())) + .build(); + + assert_eq!(boundary.exit_bindings.len(), 2); + assert!(boundary.loop_var_name.is_some()); + assert_eq!(boundary.join_inputs.len(), 3); + assert_eq!(boundary.host_inputs.len(), 3); + } }