refactor(joinir): Box-First cleanup - trace unification + SSOT + Fail-Fast

## Changes

### 1. eprintln! to trace.rs unification
**File**: src/mir/builder/control_flow/joinir/routing.rs
- Replaced direct eprintln! with trace::trace().routing()
- Box-First principle: Log responsibility centralized in trace.rs
- Enables filtering via HAKO_JOINIR_DEBUG=1

### 2. Priority field removal (SSOT)
**File**: src/mir/builder/control_flow/joinir/patterns/router.rs
- Removed #[allow(dead_code)] priority field
- Array order is SSOT (Single Source of Truth) for pattern priority
- Pattern try order: Pattern5 → Pattern4 → Pattern3 → Pattern1 → Pattern2
- Eliminated dead code warning

### 3. catch_unwind removal (Fail-Fast)
**File**: src/mir/builder/control_flow/joinir/routing_legacy_binding.rs
- Removed catch_unwind + Ok(None) silent error swallowing
- Fail-Fast principle: Panics propagate explicitly
- Enables early detection of panic sources

## Verification

 Build: cargo build --release (0 errors)
 Tests: 71 JoinIR tests all PASS
 VM: /tmp/p1_return_i.hako → Result: 3
 LLVM: /tmp/p1_return_i.hako → Mock exit code: 0

## Design Principles Applied

- **Box-First**: Log responsibility → trace.rs
- **SSOT**: Array order defines priority (no redundant field)
- **Fail-Fast**: Explicit failures, no silent error swallowing

## Statistics

- 3 files changed
- 53 deletions, 28 insertions
- Net: -25 lines (code reduction)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-15 03:27:47 +09:00
parent 447d4ea246
commit 18d56d5b88
3 changed files with 27 additions and 52 deletions

View File

@ -123,10 +123,6 @@ pub(crate) struct LoopPatternEntry {
/// Human-readable pattern name for debugging /// Human-readable pattern name for debugging
pub(crate) name: &'static str, pub(crate) name: &'static str,
/// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30
#[allow(dead_code)]
pub(crate) priority: u8,
/// Detection function: returns true if this pattern matches /// Detection function: returns true if this pattern matches
pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool, pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool,
@ -135,29 +131,32 @@ pub(crate) struct LoopPatternEntry {
} }
/// Static table of all registered loop patterns. /// Static table of all registered loop patterns.
/// Patterns are tried in priority order (lowest first). ///
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
/// Array order defines priority - earlier entries are tried first.
/// Pattern5 (most specific) → Pattern4 → Pattern3 → Pattern1 → Pattern2
/// ///
/// # Current Patterns (Phase 131-11: Pattern 5 added) /// # Current Patterns (Phase 131-11: Pattern 5 added)
/// ///
/// All patterns now use structure-based detection via LoopFeatures and classify(): /// All patterns now use structure-based detection via LoopFeatures and classify():
/// ///
/// - Pattern 5 (priority 1): Infinite Loop with Early Exit (llvm_stage3_loop_only.hako) [Phase 131-11] /// - Pattern 5: Infinite Loop with Early Exit (llvm_stage3_loop_only.hako) [Phase 131-11]
/// - Detection: pattern_kind == InfiniteEarlyExit /// - Detection: pattern_kind == InfiniteEarlyExit
/// - Structure: is_infinite_loop && has_break && has_continue /// - Structure: is_infinite_loop && has_break && has_continue
/// ///
/// - Pattern 4 (priority 5): Loop with Continue (loop_continue_pattern4.hako) /// - Pattern 4: Loop with Continue (loop_continue_pattern4.hako)
/// - Detection: pattern_kind == Pattern4Continue /// - Detection: pattern_kind == Pattern4Continue
/// - Structure: has_continue && !has_break /// - Structure: has_continue && !has_break
/// ///
/// - Pattern 3 (priority 30): Loop with If-Else PHI (loop_if_phi.hako) /// - Pattern 3: Loop with If-Else PHI (loop_if_phi.hako)
/// - Detection: pattern_kind == Pattern3IfPhi /// - Detection: pattern_kind == Pattern3IfPhi
/// - Structure: has_if_else_phi && !has_break && !has_continue /// - Structure: has_if_else_phi && !has_break && !has_continue
/// ///
/// - Pattern 1 (priority 10): Simple While Loop (loop_min_while.hako) /// - Pattern 1: Simple While Loop (loop_min_while.hako)
/// - Detection: pattern_kind == Pattern1SimpleWhile /// - Detection: pattern_kind == Pattern1SimpleWhile
/// - Structure: !has_break && !has_continue && !has_if_else_phi /// - Structure: !has_break && !has_continue && !has_if_else_phi
/// ///
/// - Pattern 2 (priority 20): Loop with Conditional Break (joinir_min_loop.hako) /// - Pattern 2: Loop with Conditional Break (joinir_min_loop.hako)
/// - Detection: pattern_kind == Pattern2Break /// - Detection: pattern_kind == Pattern2Break
/// - Structure: has_break && !has_continue /// - Structure: has_break && !has_continue
/// ///
@ -165,31 +164,26 @@ pub(crate) struct LoopPatternEntry {
pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
LoopPatternEntry { LoopPatternEntry {
name: "Pattern5_InfiniteEarlyExit", name: "Pattern5_InfiniteEarlyExit",
priority: 1, // Highest priority - most specific (infinite loop with break+continue)
detect: super::pattern5_infinite_early_exit::can_lower, detect: super::pattern5_infinite_early_exit::can_lower,
lower: super::pattern5_infinite_early_exit::lower, lower: super::pattern5_infinite_early_exit::lower,
}, },
LoopPatternEntry { LoopPatternEntry {
name: "Pattern4_WithContinue", name: "Pattern4_WithContinue",
priority: 5, // Second priority - continue without break
detect: super::pattern4_with_continue::can_lower, detect: super::pattern4_with_continue::can_lower,
lower: super::pattern4_with_continue::lower, lower: super::pattern4_with_continue::lower,
}, },
LoopPatternEntry { LoopPatternEntry {
name: "Pattern3_WithIfPhi", name: "Pattern3_WithIfPhi",
priority: 30, // NOTE: Pattern 3 must be checked BEFORE Pattern 1 (both use "main")
detect: super::pattern3_with_if_phi::can_lower, detect: super::pattern3_with_if_phi::can_lower,
lower: super::pattern3_with_if_phi::lower, lower: super::pattern3_with_if_phi::lower,
}, },
LoopPatternEntry { LoopPatternEntry {
name: "Pattern1_Minimal", name: "Pattern1_Minimal",
priority: 10,
detect: super::pattern1_minimal::can_lower, detect: super::pattern1_minimal::can_lower,
lower: super::pattern1_minimal::lower, lower: super::pattern1_minimal::lower,
}, },
LoopPatternEntry { LoopPatternEntry {
name: "Pattern2_WithBreak", name: "Pattern2_WithBreak",
priority: 20,
detect: super::pattern2_with_break::can_lower, detect: super::pattern2_with_break::can_lower,
lower: super::pattern2_with_break::lower, lower: super::pattern2_with_break::lower,
}, },

View File

@ -128,19 +128,19 @@ impl MirBuilder {
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available // Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
// Clone fn_body_ast to avoid borrow checker issues // Clone fn_body_ast to avoid borrow checker issues
let fn_body_clone = self.fn_body_ast.clone(); let fn_body_clone = self.fn_body_ast.clone();
eprintln!( trace::trace().routing(
"[routing] fn_body_ast is {} for '{}'", "router",
if fn_body_clone.is_some() { func_name,
"SOME" &format!(
} else { "fn_body_ast is {}",
"NONE" if fn_body_clone.is_some() { "SOME" } else { "NONE" }
}, ),
func_name
); );
let ctx = if let Some(ref fn_body) = fn_body_clone { let ctx = if let Some(ref fn_body) = fn_body_clone {
eprintln!( trace::trace().routing(
"[routing] Creating ctx with fn_body ({} nodes)", "router",
fn_body.len() func_name,
&format!("Creating ctx with fn_body ({} nodes)", fn_body.len()),
); );
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body) LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
} else { } else {

View File

@ -128,35 +128,16 @@ impl MirBuilder {
), ),
); );
// Step 3: Lower to JoinIR with panic catch // Step 3: Lower to JoinIR (Fail-Fast: no silent error swallowing)
// Phase 132-Post: Removed catch_unwind - panics should be fixed at the source
let join_module = { let join_module = {
let json_clone = program_json.clone(); let json_clone = program_json.clone();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { let mut lowerer = AstToJoinIrLowerer::new();
let mut lowerer = AstToJoinIrLowerer::new();
lowerer.lower_program_json(&json_clone)
}));
match result { // If lower_program_json panics, the panic will propagate up
Ok(module) => module, // This is intentional - we want to catch and fix panics at their source
Err(e) => { // rather than silently swallowing them with Ok(None)
let panic_msg = if let Some(s) = e.downcast_ref::<&str>() { lowerer.lower_program_json(&json_clone)
s.to_string()
} else if let Some(s) = e.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic".to_string()
};
trace::trace().debug(
"router",
&format!(
"JoinIR lowering failed for {}: {} (unsupported pattern)",
func_name, panic_msg
),
);
return Ok(None);
}
}
}; };
let join_meta = JoinFuncMetaMap::new(); let join_meta = JoinFuncMetaMap::new();