diff --git a/docs/development/current/main/loop_pattern_space.md b/docs/development/current/main/loop_pattern_space.md index 09e61b7b..7f9f9aaa 100644 --- a/docs/development/current/main/loop_pattern_space.md +++ b/docs/development/current/main/loop_pattern_space.md @@ -157,7 +157,49 @@ loop (i < n) { --- -## 5. 関連ドキュメント +## 5. Pattern × Box マトリクス(Phase 200 追加) + +各 Pattern がどの Box を使うかを一覧にしたよ。 +これで Claude / ChatGPT / 将来の自分が「このパターンはどの箱を使うか」をすぐ把握できる。 + +| Box / Module | P1 Minimal | P2 Break | P3 If‑PHI | P4 Continue | P5 Trim‑like* | +|--------------------------------------|------------|----------|-----------|-------------|---------------| +| **ConditionEnv** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **BoolExprLowerer** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **LoopConditionScopeBox** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **LoopScopeShapeBuilder** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **ConditionEnvBuilder** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **LoopHeaderPhiBuilder** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **ExitMeta / ExitMetaCollector** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **ExitLine / ExitLineReconnector** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **JoinInlineBoundary** | ✅ | ✅ | ✅ | ✅ | ✅ | +| **BreakConditionAnalyzer** | ❌ | ✅ | ❌ | ❌ | ✅ | +| **Pattern4CarrierAnalyzer** | ❌ | ❌ | ❌ | ✅ | ❌ | +| **ContinueBranchNormalizer** | ❌ | ❌ | ❌ | ✅ | ❌ | +| **LoopUpdateAnalyzer** | ❌ | ❌ | ❌ | ✅ | ❌ | +| **LoopBodyCarrierPromoter** | ❌ | ❌ | ❌ | ❌ | ✅ | +| **TrimLoopHelper** | ❌ | ❌ | ❌ | ❌ | ✅ | +| **TrimPatternValidator** | ❌ | ❌ | ❌ | ❌ | ✅ | +| **TrimPatternLowerer** | ❌ | ❌ | ❌ | ❌ | ✅ | +| **BlockAllocator** (merge) | ✅ | ✅ | ✅ | ✅ | ✅ | +| **ValueCollector** (merge) | ✅ | ✅ | ✅ | ✅ | ✅ | +| **InstructionRewriter** (merge) | ✅ | ✅ | ✅ | ✅ | ✅ | +| **ExitPhiBuilder** (merge) | ✅ | ✅ | ✅ | ✅ | ✅ | + +**凡例**: +- ✅: 使用する(必須) +- ❌: 使用しない +- 🔮: 将来的に使用予定(設計段階) + +**備考**: +- P5 (Trim‑like) は Phase 171‑172 で validation + JoinIR lowering まで完了。 +- P6〜P12(break+continue 同時、複数キャリア、match‑PHI など)は将来パターンとして識別済み。 +- すべての Pattern は **LoopConditionScopeBox** で条件変数のスコープを分類する(Fail‑Fast の要)。 +- **TrimLoopHelper / TrimPatternValidator / TrimPatternLowerer** は P5 専用(他パターンには影響しない)。 + +--- + +## 6. 関連ドキュメント - `joinir-architecture-overview.md` JoinIR 全体の箱と契約。Loop/If/ExitLine/Boundary/条件式ラインの全体図。 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 58bc504f..45862c90 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 @@ -328,15 +328,18 @@ impl MirBuilder { use crate::mir::builder::control_flow::joinir::merge::exit_line::ExitMetaCollector; let exit_bindings = ExitMetaCollector::collect(self, &exit_meta, debug); - // Phase 33-22: Create boundary with Pattern 2 specific settings - let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only( - vec![ValueId(0)], // JoinIR's main() parameter (loop variable init) - vec![loop_var_id], // Host's loop variable - ); - boundary.condition_bindings = condition_bindings; - boundary.exit_bindings = exit_bindings.clone(); - boundary.expr_result = fragment_meta.expr_result; // Phase 33-14: Pass expr_result to merger - boundary.loop_var_name = Some(loop_var_name.clone()); // Phase 33-16: For LoopHeaderPhiBuilder + // Phase 200-2: Use JoinInlineBoundaryBuilder for clean construction + use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs( + vec![ValueId(0)], // JoinIR's main() parameter (loop variable init) + vec![loop_var_id], // Host's loop variable + ) + .with_condition_bindings(condition_bindings) + .with_exit_bindings(exit_bindings.clone()) + .with_expr_result(fragment_meta.expr_result) // Phase 33-14: Pass expr_result to merger + .with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-16: For LoopHeaderPhiBuilder + .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 new file mode 100644 index 00000000..9b78ea3a --- /dev/null +++ b/src/mir/join_ir/lowering/inline_boundary_builder.rs @@ -0,0 +1,204 @@ +//! Phase 200-2: JoinInlineBoundaryBuilder - Builder pattern for JoinInlineBoundary construction +//! +//! This module provides a fluent Builder API to construct JoinInlineBoundary objects, +//! reducing field manipulation scattering in pattern lowerers. +//! +//! # Design Philosophy +//! +//! - **Centralized Construction**: All boundary field manipulation in one place +//! - **Type Safety**: Builder ensures fields are set correctly +//! - **Readability**: Fluent API makes construction intent clear +//! - **Maintainability**: Changes to boundary structure isolated to this builder +//! +//! # Example Usage +//! +//! ```ignore +//! use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; +//! +//! let boundary = JoinInlineBoundaryBuilder::new() +//! .with_inputs(vec![ValueId(0)], vec![loop_var_id]) +//! .with_loop_var_name(Some(loop_param_name.to_string())) +//! .with_condition_bindings(condition_bindings) +//! .with_exit_bindings(exit_bindings) +//! .with_expr_result(fragment_meta.expr_result) +//! .build(); +//! ``` + +use crate::mir::ValueId; +use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding}; +use super::condition_to_joinir::ConditionBinding; + +/// Builder for constructing JoinInlineBoundary objects +/// +/// Provides a fluent API to set boundary fields without direct field manipulation. +pub struct JoinInlineBoundaryBuilder { + boundary: JoinInlineBoundary, +} + +impl JoinInlineBoundaryBuilder { + /// Create a new builder with default empty boundary + pub fn new() -> Self { + Self { + boundary: JoinInlineBoundary { + join_inputs: vec![], + host_inputs: vec![], + join_outputs: vec![], + #[allow(deprecated)] + host_outputs: vec![], + exit_bindings: vec![], + #[allow(deprecated)] + condition_inputs: vec![], + condition_bindings: vec![], + expr_result: None, + loop_var_name: None, + } + } + } + + /// Set input mappings (JoinIR-local ↔ Host ValueIds) + /// + /// # Arguments + /// + /// * `join_inputs` - JoinIR-local ValueIds (e.g., [ValueId(0)]) + /// * `host_inputs` - Host ValueIds (e.g., [loop_var_id]) + /// + /// # Panics + /// + /// Panics if `join_inputs` and `host_inputs` have different lengths. + pub fn with_inputs(mut self, join_inputs: Vec, host_inputs: Vec) -> Self { + assert_eq!( + join_inputs.len(), + host_inputs.len(), + "join_inputs and host_inputs must have same length" + ); + self.boundary.join_inputs = join_inputs; + self.boundary.host_inputs = host_inputs; + self + } + + /// Set output mappings (JoinIR-local ↔ Host ValueIds) (DEPRECATED) + /// + /// **DEPRECATED**: Use `with_exit_bindings` instead for explicit carrier naming. + #[deprecated(since = "Phase 200-2", note = "Use with_exit_bindings instead")] + pub fn with_outputs(mut self, join_outputs: Vec, host_outputs: Vec) -> Self { + self.boundary.join_outputs = join_outputs; + #[allow(deprecated)] + { + self.boundary.host_outputs = host_outputs; + } + self + } + + /// Set condition bindings (Phase 171-fix) + /// + /// Each binding explicitly maps: + /// - Variable name + /// - HOST ValueId → JoinIR ValueId + pub fn with_condition_bindings(mut self, bindings: Vec) -> Self { + self.boundary.condition_bindings = bindings; + self + } + + /// Set exit bindings (Phase 190+) + /// + /// Each binding explicitly names the carrier variable and its source/destination. + pub fn with_exit_bindings(mut self, bindings: Vec) -> Self { + self.boundary.exit_bindings = bindings; + self + } + + /// Set loop variable name (Phase 33-16) + /// + /// Used for LoopHeaderPhiBuilder to track which PHI corresponds to the loop variable. + pub fn with_loop_var_name(mut self, name: Option) -> Self { + self.boundary.loop_var_name = name; + self + } + + /// Set expression result (Phase 33-14) + /// + /// If the loop is used as an expression, this is the JoinIR-local ValueId + /// of k_exit's return value. + pub fn with_expr_result(mut self, expr: Option) -> Self { + self.boundary.expr_result = expr; + self + } + + /// Build the final JoinInlineBoundary + pub fn build(self) -> JoinInlineBoundary { + self.boundary + } +} + +impl Default for JoinInlineBoundaryBuilder { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_builder_basic() { + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(vec![ValueId(0)], vec![ValueId(4)]) + .build(); + + assert_eq!(boundary.join_inputs, vec![ValueId(0)]); + assert_eq!(boundary.host_inputs, vec![ValueId(4)]); + assert_eq!(boundary.join_outputs.len(), 0); + assert_eq!(boundary.exit_bindings.len(), 0); + assert_eq!(boundary.condition_bindings.len(), 0); + assert_eq!(boundary.expr_result, None); + assert_eq!(boundary.loop_var_name, None); + } + + #[test] + fn test_builder_full() { + let condition_binding = ConditionBinding { + name: "start".to_string(), + host_value: ValueId(33), + join_value: ValueId(1), + }; + + let exit_binding = LoopExitBinding { + carrier_name: "sum".to_string(), + join_exit_value: ValueId(18), + host_slot: ValueId(5), + }; + + let boundary = JoinInlineBoundaryBuilder::new() + .with_inputs(vec![ValueId(0)], vec![ValueId(4)]) + .with_loop_var_name(Some("i".to_string())) + .with_condition_bindings(vec![condition_binding]) + .with_exit_bindings(vec![exit_binding]) + .with_expr_result(Some(ValueId(20))) + .build(); + + assert_eq!(boundary.join_inputs, vec![ValueId(0)]); + assert_eq!(boundary.host_inputs, vec![ValueId(4)]); + assert_eq!(boundary.loop_var_name, Some("i".to_string())); + assert_eq!(boundary.condition_bindings.len(), 1); + assert_eq!(boundary.exit_bindings.len(), 1); + assert_eq!(boundary.expr_result, Some(ValueId(20))); + } + + #[test] + #[should_panic(expected = "join_inputs and host_inputs must have same length")] + fn test_builder_mismatched_inputs() { + JoinInlineBoundaryBuilder::new() + .with_inputs(vec![ValueId(0), ValueId(1)], vec![ValueId(4)]) + .build(); + } + + #[test] + fn test_builder_default() { + let builder = JoinInlineBoundaryBuilder::default(); + let boundary = builder.build(); + + assert_eq!(boundary.join_inputs.len(), 0); + assert_eq!(boundary.host_inputs.len(), 0); + } +} diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 01cfff5a..ccebc370 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -42,6 +42,7 @@ pub mod if_phi_context; // Phase 61-1 pub mod if_phi_spec; // Phase 61-2 pub mod if_select; // Phase 33 pub mod inline_boundary; // Phase 188-Impl-3: JoinIR→Host boundary +pub mod inline_boundary_builder; // Phase 200-2: Builder pattern for JoinInlineBoundary pub mod loop_form_intake; pub mod loop_pattern_router; // Phase 33-12: Loop pattern routing pub mod loop_pattern_validator; // Phase 33-23: Loop structure validation @@ -65,6 +66,8 @@ pub mod value_id_ranges; // Re-export public lowering functions pub use funcscanner_append_defs::lower_funcscanner_append_defs_to_joinir; pub use funcscanner_trim::lower_funcscanner_trim_to_joinir; +// Phase 200-2: Builder pattern +pub use inline_boundary_builder::JoinInlineBoundaryBuilder; // Phase 31: LoopToJoinLowerer 統一箱 pub use loop_to_join::LoopToJoinLowerer; // Phase 188: Pattern-based loop lowering