## Phase 33: Complete JoinIR Modularization via Box Theory (3 Phases) This commit consolidates the comprehensive modularization work across three phases: - Phase 33-10: Exit Line Modularization (ExitLineReconnector + ExitMetaCollector Boxes) - Phase 33-11: Quick Wins (Pattern 4 stub clarification, unused imports cleanup) - Phase 33-12: Large Module Modularization (split mod.rs, loop_patterns.rs restructuring) ### Phase 33-10: Exit Line Modularization (Boxes P0-P1) **New Files**: - `exit_line/reconnector.rs` (+130 lines): ExitLineReconnector Box - Responsibility: Update host variable_map with remapped exit values - Design: Phase 197-B multi-carrier support (each carrier gets specific remapped value) - Pure side effects: Only updates builder.variable_map - Testing: Independent unit testing possible without full merge machinery - `exit_line/meta_collector.rs` (+102 lines): ExitMetaCollector Box - Responsibility: Construct exit_bindings from ExitMeta + variable_map lookup - Design: Pure function philosophy (no side effects except variable_map reads) - Reusability: Pattern-agnostic (works for Pattern 1, 2, 3, 4) - Algorithm: For each carrier in exit_meta, lookup host ValueId, create binding - `exit_line/mod.rs` (+58 lines): ExitLineOrchestrator facade - Coordination: Orchestrates Phase 6 boundary reconnection - Architecture: Delegates to ExitLineReconnector (demonstrates Box composition) - Documentation: Comprehensive header explaining Box Theory modularization benefits **Modified Files**: - `merge/mod.rs` (-91 lines): Extracted reconnect_boundary() → ExitLineReconnector - Made exit_line module public (was mod, now pub mod) - Phase 6 delegation: Local function call → ExitLineOrchestrator::execute() - Added exit_bindings' join_exit_values to used_values for remapping (Phase 172-3) - `patterns/pattern2_with_break.rs` (-20 lines): Uses ExitMetaCollector - Removed: Manual exit_binding construction loop - Added: Delegated ExitMetaCollector::collect() for cleaner caller code - Benefit: Reusable collector for all pattern lowerers (Pattern 1-4) **Design Philosophy** (Exit Line Module): Each Box handles one concern: - ExitLineReconnector: Updates host variable_map with exit values - ExitMetaCollector: Constructs exit_bindings from ExitMeta - ExitLineOrchestrator: Orchestrates Phase 6 reconnection ### Phase 33-11: Quick Wins **Pattern 4 Stub Clarification** (+132 lines): - Added comprehensive header documentation (106 lines) - Made `lower()` return explicit error (not silent stub) - Migration guide: Workarounds using Pattern 1-3 - New file: `docs/development/proposals/phase-195-pattern4.md` (implementation plan) - Status: Formal documentation that Pattern 4 is deferred to Phase 195 **Cleanup**: - Removed unused imports via `cargo fix` (-10 lines, 11 files) - Files affected: generic_case_a/ (5 files), if_merge.rs, if_select.rs, etc. ### Phase 33-12: Large Module Modularization **New Files** (Modularization): - `if_lowering_router.rs` (172 lines): If-expression routing - Extracted from mod.rs lines 201-423 - Routes if-expressions to appropriate JoinIR lowering strategies - Single responsibility: If expression dispatch - `loop_pattern_router.rs` (149 lines): Loop pattern routing - Extracted from mod.rs lines 424-511 - Routes loop patterns to Pattern 1-4 implementations - Design: Dispatcher pattern for pattern selection - `loop_patterns/mod.rs` (178 lines): Pattern dispatcher + shared utilities - Created as coordinator for per-pattern files - Exports all pattern functions via pub use - Utilities: Shared logic across pattern lowerers - `loop_patterns/simple_while.rs` (225 lines): Pattern 1 lowering - `loop_patterns/with_break.rs` (129 lines): Pattern 2 lowering - `loop_patterns/with_if_phi.rs` (123 lines): Pattern 3 lowering - `loop_patterns/with_continue.rs` (129 lines): Pattern 4 stub **Modified Files** (Refactoring): - `lowering/mod.rs` (511 → 221 lines, -57%): - Removed try_lower_if_to_joinir() (223 lines) → if_lowering_router.rs - Removed try_lower_loop_pattern_to_joinir() (88 lines) → loop_pattern_router.rs - Result: Cleaner core module with routers handling dispatch - `loop_patterns.rs` → Re-export wrapper (backward compatibility) **Result**: Clearer code organization - Monolithic mod.rs split into focused routers - Large loop_patterns.rs split into per-pattern files - Better maintainability and testability ### Phase 33: Comprehensive Documentation **New Architecture Documentation** (+489 lines): - File: `docs/development/architecture/phase-33-modularization.md` - Coverage: All three phases (33-10, 33-11, 33-12) - Content: - Box Theory principles applied - Complete statistics table (commits, files, lines) - Code quality analysis - Module structure diagrams - Design patterns explanation - Testing strategy - Future work recommendations - References to implementation details **Source Code Comments** (+165 lines): - `exit_line/mod.rs`: Box Theory modularization context - `exit_line/reconnector.rs`: Design notes on multi-carrier support - `exit_line/meta_collector.rs`: Pure function philosophy - `pattern4_with_continue.rs`: Comprehensive stub documentation + migration paths - `if_lowering_router.rs`: Modularization context - `loop_pattern_router.rs`: Pattern dispatch documentation - `loop_patterns/mod.rs`: Per-pattern structure benefits **Project Documentation** (+45 lines): - CLAUDE.md: Phase 33 completion summary + links - CURRENT_TASK.md: Current state and next phases ### Metrics Summary **Phase 33 Total Impact**: - Commits: 5 commits (P0, P1, Quick Wins×2, P2) - Files Changed: 15 files modified/created - Lines Added: ~1,500 lines (Boxes + documentation + comments) - Lines Removed: ~200 lines (monolithic extractions) - Code Organization: 2 monolithic files → 7 focused modules - Documentation: 1 comprehensive architecture guide created **mod.rs Impact** (Phase 33-12 P2): - Before: 511 lines (monolithic) - After: 221 lines (dispatcher + utilities) - Reduction: -57% (290 lines extracted) **loop_patterns.rs Impact** (Phase 33-12 P2): - Before: 735 lines (monolithic) - After: 5 files in loop_patterns/ (178 + 225 + 129 + 123 + 129) - Improvement: Per-pattern organization ### Box Theory Principles Applied 1. **Single Responsibility**: Each Box handles one concern - ExitLineReconnector: variable_map updates - ExitMetaCollector: exit_binding construction - if_lowering_router: if-expression dispatch - loop_pattern_router: loop pattern dispatch - Per-pattern files: Individual pattern lowering 2. **Clear Boundaries**: Public/private visibility enforced - Boxes have explicit input/output contracts - Module boundaries clearly defined - Re-exports for backward compatibility 3. **Replaceability**: Boxes can be swapped/upgraded independently - ExitLineReconnector can be optimized without affecting ExitMetaCollector - Per-pattern files can be improved individually - Router logic decoupled from lowering implementations 4. **Testability**: Smaller modules easier to unit test - ExitMetaCollector can be tested independently - ExitLineReconnector mockable with simple boundary - Pattern lowerers isolated in separate files ### Design Patterns Introduced 1. **Facade Pattern**: ExitLineOrchestrator - Single-entry point for Phase 6 reconnection - Hides complexity of multi-step process - Coordinates ExitLineReconnector + other steps 2. **Dispatcher Pattern**: if_lowering_router + loop_pattern_router - Centralized routing logic - Easy to add new strategies - Separates dispatch from implementation 3. **Pure Function Pattern**: ExitMetaCollector::collect() - No side effects (except reading variable_map) - Easy to test, reason about, parallelize - Reusable across all pattern lowerers ### Testing Strategy - **Unit Tests**: Can test ExitMetaCollector independently - **Integration Tests**: Verify boundary reconnection works end-to-end - **Regression Tests**: Pattern 2 simple loop still passes - **Backward Compatibility**: All existing imports still work ### Future Work - **Phase 33-13**: Consolidate whitespace utilities (expected -100 lines) - **Phase 34**: Extract inline_boundary validators (expected 3h effort) - **Phase 35**: Mark loop_patterns_old.rs as legacy and remove (Phase 35+) - **Phase 195**: Implement Pattern 4 (continue) fully - **Phase 200+**: More complex loop patterns and optimizations 🧱 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
191 lines
7.5 KiB
Rust
191 lines
7.5 KiB
Rust
//! # If-Expression JoinIR Lowering Router
|
||
//!
|
||
//! **Phase 33-12 Modularization**: Extracted from `mod.rs` (lines 201-423)
|
||
//!
|
||
//! ## Responsibility
|
||
//! Routes if-expressions to appropriate JoinIR lowering strategies.
|
||
//! This is the **main entry point** for if-expression → JoinIR lowering.
|
||
//!
|
||
//! ## Design Pattern
|
||
//! - **Input**: AST if-expression + MirBuilder
|
||
//! - **Output**: JoinModule (or error)
|
||
//! - **Side effects**: Modifies MirBuilder (adds blocks, values, instructions)
|
||
//!
|
||
//! ## Why Separate Router?
|
||
//! If and loop lowering are orthogonal concerns:
|
||
//! - If: Expression-level lowering (conditional value selection)
|
||
//! - Loop: Statement-level lowering (control flow patterns)
|
||
//! - Keeping separate: Easier to extend, test, and maintain
|
||
//!
|
||
//! ## Related Modules
|
||
//! - See also: `loop_pattern_router.rs` (loop lowering)
|
||
//! - Parent: `mod.rs` (central lowering dispatcher)
|
||
//!
|
||
//! # Routing Strategy
|
||
//!
|
||
//! This router tries multiple lowering strategies in order:
|
||
//! 1. **IfMerge**: For multiple-variable patterns (Phase 33-7)
|
||
//! 2. **IfSelect**: For single-variable patterns (Phase 33-2/33-3)
|
||
//!
|
||
//! # Control Flow
|
||
//!
|
||
//! - Checks toggle flags (`joinir_if_select_enabled()`)
|
||
//! - Validates function whitelist (test/Stage-1/explicit approvals)
|
||
//! - Excludes loop-lowered functions (Phase 33-9.1 separation)
|
||
//! - Falls back to traditional if_phi on pattern mismatch
|
||
//!
|
||
//! # Phase 61-1: If-in-Loop Support
|
||
//!
|
||
//! Context parameter enables if-in-loop lowering:
|
||
//! - `None`: Pure if-expression
|
||
//! - `Some(context)`: If-in-loop with carrier variables
|
||
|
||
use crate::mir::join_ir::JoinInst;
|
||
use crate::mir::{BasicBlockId, MirFunction};
|
||
|
||
/// Phase 33-7: Try to lower if/else to JoinIR Select/IfMerge instruction
|
||
///
|
||
/// Scope:
|
||
/// - Only applies to whitelisted functions:
|
||
/// - IfSelectTest.* (Phase 33-2/33-3)
|
||
/// - IfMergeTest.* (Phase 33-7)
|
||
/// - JsonShapeToMap._read_value_from_pair/1 (Phase 33-4 Stage-1)
|
||
/// - Stage1JsonScannerBox.value_start_after_key_pos/2 (Phase 33-4 Stage-B)
|
||
/// - Requires JoinIR If-select toggle (HAKO_JOINIR_IF_SELECT / joinir_if_select_enabled)
|
||
/// - Falls back to traditional if_phi on pattern mismatch
|
||
///
|
||
/// Pattern selection:
|
||
/// - 1 variable → Select
|
||
/// - 2+ variables → IfMerge
|
||
///
|
||
/// Phase 61-1: If-in-loop support
|
||
/// - `context` parameter: If-in-loop context (carrier_names for loop variables)
|
||
/// - None = Pure If, Some(_) = If-in-loop
|
||
///
|
||
/// Returns Some(JoinInst::Select) or Some(JoinInst::IfMerge) if pattern matched, None otherwise.
|
||
pub fn try_lower_if_to_joinir(
|
||
func: &MirFunction,
|
||
block_id: BasicBlockId,
|
||
debug: bool,
|
||
context: Option<&crate::mir::join_ir::lowering::if_phi_context::IfPhiContext>, // Phase 61-1: If-in-loop context
|
||
) -> Option<JoinInst> {
|
||
// 1. dev/Core トグルチェック
|
||
//
|
||
// - Core: joinir_core_enabled() / joinir_if_select_enabled()
|
||
// - Dev: joinir_dev_enabled()(詳細ログ等)
|
||
//
|
||
// 実際の挙動切り替えは joinir_if_select_enabled() に集約し、
|
||
// Core/Dev ポリシーは config::env 側で判定する。
|
||
if !crate::config::env::joinir_if_select_enabled() {
|
||
return None;
|
||
}
|
||
let core_on = crate::config::env::joinir_core_enabled();
|
||
// Phase 185: strict check moved to caller (if_form.rs)
|
||
// let strict_on = crate::config::env::joinir_strict_enabled();
|
||
|
||
// Phase 33-9.1: Loop専任関数の除外(Loop/If責務分離)
|
||
// Loop lowering対象関数はIf loweringの対象外にすることで、
|
||
// 1関数につき1 loweringの原則を保証します
|
||
if super::is_loop_lowered_function(&func.signature.name) {
|
||
return None;
|
||
}
|
||
|
||
// Phase 33-8: デバッグログレベル取得(0-3)
|
||
let debug_level = crate::config::env::joinir_debug_level();
|
||
let _debug = debug || debug_level >= 1;
|
||
|
||
// 2. Phase 33-8: 関数名ガード拡張(テスト + Stage-1 rollout + 明示承認)
|
||
let is_allowed =
|
||
// Test functions (always enabled)
|
||
func.signature.name.starts_with("IfSelectTest.") ||
|
||
func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test
|
||
func.signature.name.starts_with("IfMergeTest.") ||
|
||
func.signature.name.starts_with("IfToplevelTest.") || // Phase 61-4: loop-outside if test
|
||
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
|
||
|
||
// Stage-1 rollout (env-controlled)
|
||
(crate::config::env::joinir_stage1_enabled() &&
|
||
func.signature.name.starts_with("Stage1")) ||
|
||
|
||
// Explicit approvals (Phase 33-4で検証済み, always on)
|
||
matches!(func.signature.name.as_str(),
|
||
"JsonShapeToMap._read_value_from_pair/1" |
|
||
"Stage1JsonScannerBox.value_start_after_key_pos/2"
|
||
);
|
||
|
||
// Phase 80: Core ON のときは許可リストを「JoinIRをまず試す」対象とみなす。
|
||
// Core OFF のときは従来どおり whitelist + env に頼る。
|
||
if !is_allowed || !core_on {
|
||
// Core OFF かつ許可外なら従来のガードでスキップ
|
||
if !is_allowed {
|
||
if debug_level >= 2 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] skipping non-allowed function: {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
return None;
|
||
}
|
||
}
|
||
// Phase 185: strict_allowed removed (strict check moved to caller: if_form.rs)
|
||
|
||
if debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] trying to lower {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
|
||
// 3. Phase 33-7: IfMerge を優先的に試行(複数変数パターン)
|
||
// IfMerge が成功すればそれを返す、失敗したら Select を試行
|
||
// Phase 61-1: context がある場合は with_context() を使用
|
||
let if_merge_lowerer = if let Some(ctx) = context {
|
||
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::with_context(debug_level, ctx.clone())
|
||
} else {
|
||
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::new(debug_level)
|
||
};
|
||
|
||
if if_merge_lowerer.can_lower_to_if_merge(func, block_id) {
|
||
if let Some(result) = if_merge_lowerer.lower_if_to_if_merge(func, block_id) {
|
||
if debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] ✅ IfMerge lowering used for {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
return Some(result);
|
||
}
|
||
}
|
||
|
||
// 4. IfMerge が失敗したら Select を試行(単一変数パターン)
|
||
// Phase 61-1: context がある場合は with_context() を使用
|
||
let if_select_lowerer = if let Some(ctx) = context {
|
||
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::with_context(debug_level, ctx.clone())
|
||
} else {
|
||
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::new(debug_level)
|
||
};
|
||
|
||
// Phase 185: Remove strict checks from lowerer (thin Result-returning box)
|
||
// Strict mode panic should happen at caller level (if_form.rs), not here
|
||
if !if_select_lowerer.can_lower_to_select(func, block_id) {
|
||
if debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] pattern not matched for {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
return None;
|
||
}
|
||
|
||
let result = if_select_lowerer.lower_if_to_select(func, block_id);
|
||
|
||
if result.is_some() && debug_level >= 1 {
|
||
eprintln!(
|
||
"[try_lower_if_to_joinir] ✅ Select lowering used for {}",
|
||
func.signature.name
|
||
);
|
||
}
|
||
|
||
result
|
||
}
|