refactor(control_flow): Phase 134 P0 - Normalization entry point SSOT

Consolidate dual entry points into unified NormalizationPlan system:

Problem:
- Dual entry points: try_normalized_shadow() + suffix_router_box
- ~220 lines of duplicated pattern detection logic
- Maintenance burden: changes required in two places

Solution:
- New normalization module with Box-First architecture
- NormalizationPlanBox: Single source of truth for pattern detection
- NormalizationExecuteBox: Single source of truth for execution

Implementation:
- src/mir/builder/control_flow/normalization/ (new module)
  - README.md: Design contract (SSOT)
  - plan.rs: NormalizationPlan data structure
  - plan_box.rs: Pattern detection (7 unit tests)
  - execute_box.rs: Execution logic
  - mod.rs: Module integration

Refactored files:
- routing.rs::try_normalized_shadow(): 165 → 87 lines (-78 lines)
- suffix_router_box::try_lower_loop_suffix(): 258 → 116 lines (-142 lines)

Pattern support maintained:
- Phase 131: loop(true) only (consumed: 1)
- Phase 132: loop + single post (consumed: 3)
- Phase 133: loop + multiple post (consumed: 2+N)

Box-First principles:
- Plan Box: Detection responsibility only
- Execute Box: Execution responsibility only
- README.md: Contract documentation (SSOT)
- Clear separation enables independent testing

Test results:
- cargo test --lib: 1186 PASS (10 new tests added)
- Phase 133 VM/LLVM EXE: PASS (exit code 6)
- Phase 132 LLVM EXE: PASS (exit code 3)
- Phase 131 LLVM EXE: PASS (exit code 1)

Benefits:
- Code duplication eliminated (~220 lines)
- Single source of truth for normalization decisions
- Improved maintainability and testability
- Future-proof extensibility (easy to add new patterns)

Default behavior unchanged: Dev-only guard maintained

Related: Phase 134 normalization infrastructure improvement

🤖 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-18 22:29:29 +09:00
parent ef71ea955c
commit f4ab5ca5f4
9 changed files with 1373 additions and 337 deletions

View File

@ -0,0 +1,87 @@
//! Normalization plan data structures (Phase 134 P0)
//!
//! Defines what to normalize and how many statements to consume.
/// Plan for normalizing a block suffix
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NormalizationPlan {
/// Number of statements to consume from remaining block
pub consumed: usize,
/// What kind of normalization to perform
pub kind: PlanKind,
/// Whether the pattern includes an explicit return (for unreachable detection)
pub requires_return: bool,
}
/// Kind of normalization pattern detected
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlanKind {
/// Phase 131: loop(true) { ... break } alone
///
/// Pattern: Single loop statement
/// Example: `loop(true) { x = 1; break }`
LoopOnly,
/// Phase 132-133: loop(true) + post assignments + return
///
/// Pattern: loop + N assignments + return
/// Example: `loop(true) { x = 1; break }; x = x + 2; return x`
LoopWithPost {
/// Number of post-loop assignment statements
post_assign_count: usize,
},
}
impl NormalizationPlan {
/// Create a Phase 131 plan (loop-only)
pub fn loop_only() -> Self {
Self {
consumed: 1,
kind: PlanKind::LoopOnly,
requires_return: false,
}
}
/// Create a Phase 132-133 plan (loop + post assignments + return)
pub fn loop_with_post(post_assign_count: usize) -> Self {
// consumed = 1 (loop) + N (assigns) + 1 (return)
let consumed = 1 + post_assign_count + 1;
Self {
consumed,
kind: PlanKind::LoopWithPost { post_assign_count },
requires_return: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loop_only_plan() {
let plan = NormalizationPlan::loop_only();
assert_eq!(plan.consumed, 1);
assert_eq!(plan.kind, PlanKind::LoopOnly);
assert!(!plan.requires_return);
}
#[test]
fn test_loop_with_post_single() {
let plan = NormalizationPlan::loop_with_post(1);
assert_eq!(plan.consumed, 3); // loop + 1 assign + return
assert_eq!(plan.kind, PlanKind::LoopWithPost { post_assign_count: 1 });
assert!(plan.requires_return);
}
#[test]
fn test_loop_with_post_multiple() {
let plan = NormalizationPlan::loop_with_post(2);
assert_eq!(plan.consumed, 4); // loop + 2 assigns + return
assert_eq!(plan.kind, PlanKind::LoopWithPost { post_assign_count: 2 });
assert!(plan.requires_return);
}
}