refactor: unify string helpers and pattern2 derived slot

This commit is contained in:
2025-12-28 13:22:02 +09:00
parent 84e1cd7c7b
commit 10e6a15552
41 changed files with 2044 additions and 585 deletions

View File

@ -122,7 +122,9 @@ pub enum PlanKind {
### Phase 142 P0: Statement-Level Normalization **CURRENT**
- **Change**: Normalization unit changed from "block suffix" to "statement (loop only)"
- **Pattern**: `loop(true) { ... break }` - always returns `LoopOnly`, regardless of subsequent statements
- **Pattern**: `loop(true)` with **Normalized-supported body shapes** only
- Body ends with `break` and prior statements are `assignment`/`local` only
- Body is a single `if` with `break`/`continue` branches (optional else)
- **Consumed**: Always 1 statement (the loop itself)
- **Kind**: `PlanKind::LoopOnly`
- **Subsequent statements**: Handled by normal MIR lowering (not normalized)

View File

@ -99,9 +99,21 @@ impl NormalizationPlanBox {
}
}
// Phase 142 P0: Always return loop_only for loop(true), regardless of what follows
// Phase 142 P0: Only return loop_only when loop body is in Normalized scope
// Normalization unit is now "statement (loop 1個)" not "block suffix"
// Subsequent statements (return, assignments, etc.) handled by normal MIR lowering
if let Some(body) = loop_body {
if !loop_true_body_supported_for_normalized(body) {
if debug {
trace.routing(
"normalization/plan",
func_name,
"Loop(true) body is out of scope for Normalized (returning None)",
);
}
return Ok(None);
}
}
if debug {
trace.routing(
"normalization/plan",
@ -113,6 +125,44 @@ impl NormalizationPlanBox {
}
}
fn loop_true_body_supported_for_normalized(body: &[ASTNode]) -> bool {
if body.is_empty() {
return false;
}
if body.len() == 1 {
if let ASTNode::If {
then_body,
else_body,
..
} = &body[0]
{
if is_break_or_continue_only(then_body)
&& else_body
.as_ref()
.map_or(true, |branch| is_break_or_continue_only(branch))
{
return true;
}
}
}
if !matches!(body.last(), Some(ASTNode::Break { .. })) {
return false;
}
body[..body.len() - 1].iter().all(|stmt| {
matches!(stmt, ASTNode::Assignment { .. } | ASTNode::Local { .. })
})
}
fn is_break_or_continue_only(stmts: &[ASTNode]) -> bool {
if stmts.len() != 1 {
return false;
}
matches!(stmts[0], ASTNode::Break { .. } | ASTNode::Continue { .. })
}
#[cfg(test)]
mod tests {
use super::*;