Files
hakorune/src/mir/join_ir/lowering/if_lowering_router.rs
nyash-codex 35f5a48eb0 docs(joinir): Phase 33 Completion - Box Theory Modularization Summary
## 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>
2025-12-07 04:03:42 +09:00

191 lines
7.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! # 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
}