phase29ap(p0): reduce legacy extractors (pattern1/3)

This commit is contained in:
2025-12-30 22:13:24 +09:00
parent 49f6457619
commit ab81747d6a
8 changed files with 49 additions and 199 deletions

View File

@ -2,8 +2,8 @@
## Current Focus
- Phase: `docs/development/current/main/phases/phase-29ao/README.md` (closeout)
- Next: Phase 29ap (planned; see `docs/development/current/main/phases/phase-29ao/README.md`)
- Phase: `docs/development/current/main/phases/phase-29ap/README.md`
- Next: Phase 29ap P1 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`)
## Gate (SSOT)

View File

@ -5,7 +5,7 @@ Scope: 「次にやる候補」を短く列挙するメモ。入口は `docs/dev
## Active
- Phase 29ap (planned): legacy削減の段階計画extractor/router+ composer SSOT
- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P1 planned)
- JoinIR regression gate SSOT: `docs/development/current/main/phases/phase-29ae/README.md`
- CorePlan hardening (docs-first): `docs/development/current/main/phases/phase-29al/README.md`

View File

@ -33,8 +33,8 @@ Related:
## 1.1 Current (active)
- Active phase: `docs/development/current/main/phases/phase-29ao/README.md`
- Next step: Phase 29ap (planned; P55 done)
- Active phase: `docs/development/current/main/phases/phase-29ap/README.md`
- Next step: Phase 29ap P1 (planned)
## 2. すでに固めた SSOT再発防止の土台

View File

@ -0,0 +1,32 @@
---
Status: Active
Scope: Legacy extractor reduction (planner+composer SSOT)
Related:
- docs/development/current/main/design/coreplan-migration-roadmap-ssot.md
- docs/development/current/main/design/coreplan-migration-done-criteria-ssot.md
- docs/development/current/main/design/coreloop-composer-v0-v1-boundary-ssot.md
- docs/development/current/main/phases/phase-29ae/README.md
---
# Phase 29ap: Legacy extractor reduction (Step-E)
Goal: Reduce legacy extractor fallbacks while keeping planner+composer as the SSOT path.
Gate (SSOT):
- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
## P0: Reduce legacy extractors (Pattern1/3) ✅
- Scope:
- Remove Pattern1/3 extractor fallbacks in `single_planner`.
- Remove Pattern3 JoinIR router entry so plan line is the only path.
- Pattern1 JoinIR entry is retained for now (stdlib loops still rely on it).
- Guardrails:
- Outcome remains `Ok(None)` for non-matching cases.
- No new logs or error strings.
## Next (planned)
- P1: Pattern6/7 JoinIR wrapper → composer entry only
- P2: Router pattern-name branching reduction (planner outcome + composer SSOT)
- P3: Facts/Feature expansion if needed

View File

@ -180,7 +180,7 @@ fn lower_via_plan(
/// - Cons: Extraction can be expensive (but amortized over lowering)
///
/// ## StructureBased (Feature Classification)
/// - Used by: Pattern1, Pattern2, Pattern3, Pattern4, Pattern5, Pattern8, Pattern9
/// - Used by: Pattern1, Pattern2, Pattern4 (legacy)
/// - Strategy: Check pattern_kind (from LoopPatternContext), plus optional structural checks
/// - Pros: Fast classification, reuses centralized feature detection
/// - Cons: Two sources of truth (classify + structural checks)
@ -201,7 +201,7 @@ pub(crate) enum CanLowerStrategy {
ExtractionBased,
/// Structure-based detection: Check pattern_kind from LoopPatternContext
/// Used by Pattern1, Pattern2, Pattern3, Pattern4, Pattern5, Pattern8, Pattern9
/// Used by Pattern1, Pattern2, Pattern4
StructureBased,
}
@ -222,26 +222,20 @@ pub(crate) struct LoopPatternEntry {
///
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
/// Array order defines priority - earlier entries are tried first.
/// Pattern4 → Pattern8 → Pattern9 → Pattern3 → Pattern1 → Pattern2
/// Pattern4 → Pattern8 → Pattern9 → Pattern1 → Pattern2
///
/// # Current Patterns (Structure-based detection, established Phase 131-11+)
///
/// Pattern detection strategies (updated Phase 286):
/// - Pattern6/7: ExtractionBased (Plan line, Phase 273+)
/// - Pattern5/8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
/// - Pattern1-4: StructureBased (LoopFeatures classification, legacy)
/// - Pattern8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
/// - Pattern1/2/4: StructureBased (LoopFeatures classification, legacy)
///
/// - Pattern 4: Loop with Continue (loop_continue_pattern4.hako)
/// - Detection: pattern_kind == Pattern4Continue
/// - Structure: has_continue && !has_break
///
/// - Pattern 3: Loop with If-Else PHI (loop_if_phi.hako)
/// - Detection: pattern_kind == Pattern3IfPhi
/// - Structure: has_if_else_phi && !has_break && !has_continue
///
/// - Pattern 1: Simple While Loop (loop_min_while.hako)
/// - Detection: pattern_kind == Pattern1SimpleWhile
/// - Structure: !has_break && !has_continue && !has_if_else_phi
/// - Pattern3: moved to plan-based routing (planner+composer SSOT)
///
/// - Pattern 2: Loop with Conditional Break (joinir_min_loop.hako)
/// - Detection: pattern_kind == Pattern2Break
@ -274,11 +268,6 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
detect: super::pattern9_accum_const_loop::can_lower,
lower: super::pattern9_accum_const_loop::lower,
},
LoopPatternEntry {
name: "Pattern3_WithIfPhi",
detect: super::pattern3_with_if_phi::can_lower,
lower: super::pattern3_with_if_phi::lower,
},
LoopPatternEntry {
name: "Pattern1_Minimal",
detect: super::pattern1_minimal::can_lower,
@ -302,17 +291,17 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
/// This router uses multiple detection strategies:
/// - Plan-based (Pattern6/7): extract_*_plan() → DomainPlan (Phase 273+ SSOT)
/// - Extraction-based (Pattern8/9): extract_*() functions (already implemented)
/// - Structure-based (Pattern1-5): ctx.pattern_kind classification (legacy)
/// - Structure-based (Pattern1/2/4): ctx.pattern_kind classification (legacy)
///
/// # Plan Line SSOT for Pattern6/7 (Phase 273+)
///
/// This function implements the following routing strategy:
/// 1. Try Plan-based Pattern6 (extract_scan_with_init_plan) → DomainPlan
/// 2. Try Plan-based Pattern7 (extract_split_scan_plan) → DomainPlan
/// 3. Fall through to legacy Pattern1-5 table for other patterns
/// 3. Fall through to legacy Pattern1/2/4/8/9 table for other patterns
///
/// The Plan line (Extractor → Normalizer → Verifier → Lowerer) is the
/// current operational SSOT for Pattern6/7. Legacy patterns (1-5) use
/// current operational SSOT for Pattern6/7. Legacy patterns (1/2/4/8/9) use
/// the traditional LoopPatternContext-based routing.
///
/// Plan-based architecture (Phase 273 P1-P3):
@ -324,7 +313,7 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
/// SSOT Entry Points:
/// - Pattern6: src/mir/builder/control_flow/plan/normalizer.rs (ScanWithInit normalization)
/// - Pattern7: src/mir/builder/control_flow/plan/normalizer.rs (SplitScan normalization)
/// - Pattern1-5: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
/// - Pattern1/2/4/8/9: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
pub(crate) fn route_loop_pattern(
builder: &mut MirBuilder,
ctx: &LoopPatternContext,

View File

@ -5,7 +5,6 @@ use crate::ast::{ASTNode, BinaryOperator};
// Phase 282 P9a: Use common_helpers
use super::common_helpers::has_control_flow_statement as common_has_control_flow;
use crate::mir::builder::control_flow::plan::policies::pattern1_subset_policy::is_pattern1_step_only_body;
#[derive(Debug, Clone)]
pub(crate) struct Pattern1Parts {
@ -165,82 +164,6 @@ fn has_control_flow_statement(body: &[ASTNode]) -> bool {
common_has_control_flow(body)
}
// ============================================================================
// Phase 286 P2.1: Pattern1 → Plan/Frag SSOT extractor
// ============================================================================
/// Phase 286 P2.1: Minimal subset extractor for Pattern1 Plan line
///
/// Supported subset (PoC safety - Compare + Add only):
/// - Loop condition: `<var> < <int_lit>`
/// - Loop increment: `<var> = <var> + <int_lit>`
///
/// Returns Ok(None) for unsupported patterns → legacy fallback
pub(crate) fn extract_pattern1_plan(
condition: &ASTNode,
body: &[ASTNode],
) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String> {
use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern1SimpleWhilePlan};
// Step 1: Validate via existing extractor
let parts = extract_simple_while_parts(condition, body)?;
if parts.is_none() {
return Ok(None); // Not Pattern1 → legacy fallback
}
let parts = parts.unwrap();
// Step 2: Validate loop condition is `<var> < <int_lit>`
if !validate_loop_condition_plan(condition, &parts.loop_var) {
return Ok(None); // Unsupported condition format
}
// Step 3: Extract loop increment `<var> = <var> + <int_lit>`
// Phase 286 P2.2: Use common helper from common_helpers
let loop_increment = match super::common_helpers::extract_loop_increment_plan(body, &parts.loop_var)? {
Some(inc) => inc,
None => return Ok(None), // No loop increment found
};
if !is_pattern1_step_only_body(body, &parts.loop_var) {
return Ok(None);
}
Ok(Some(DomainPlan::Pattern1SimpleWhile(Pattern1SimpleWhilePlan {
loop_var: parts.loop_var,
condition: condition.clone(),
loop_increment,
})))
}
/// Validate loop condition: supports `<var> < <int_lit>` only
fn validate_loop_condition_plan(cond: &ASTNode, loop_var: &str) -> bool {
use crate::ast::LiteralValue;
if let ASTNode::BinaryOp { operator, left, right, .. } = cond {
if !matches!(operator, BinaryOperator::Less) {
return false; // Only < supported for PoC
}
// Left must be the loop variable
if let ASTNode::Variable { name, .. } = left.as_ref() {
if name != loop_var {
return false;
}
} else {
return false;
}
// Right must be integer literal
if !matches!(right.as_ref(), ASTNode::Literal { value: LiteralValue::Integer(_), .. }) {
return false;
}
true
} else {
false
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -468,97 +468,3 @@ mod tests {
assert!(result.unwrap().is_none()); // Has break → Not Pattern3
}
}
// ============================================================================
// Phase 286 P2.6: Pattern3 → Plan/Frag SSOT extractor
// ============================================================================
/// Phase 286 P2.6: Extract Pattern3 (Loop with If-Phi) Plan
///
/// Leverages existing `extract_loop_with_if_phi_parts()` for validation,
/// then extracts detailed AST components for Plan normalization.
///
/// # Returns
/// - Ok(Some(DomainPlan::Pattern3IfPhi)) if Pattern3 match confirmed
/// - Ok(None) if not Pattern3 (structural mismatch)
/// - Err if malformed AST (rare)
pub(crate) fn extract_pattern3_plan(
condition: &ASTNode,
body: &[ASTNode],
) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String> {
use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern3IfPhiPlan};
// Step 1: Validate via existing extractor
let parts = extract_loop_with_if_phi_parts(condition, body)?;
if parts.is_none() {
return Ok(None); // Not Pattern3 → fallback
}
let parts = parts.unwrap();
// Step 2: Extract if-else statement (validated to exist)
let if_stmt = super::common_helpers::find_if_else_statement(body)
.ok_or_else(|| "Pattern3: if-else statement not found after validation".to_string())?;
// Step 3: Extract if condition and branch updates
let (if_condition, then_update, else_update) = match if_stmt {
ASTNode::If {
condition: if_cond,
then_body,
else_body: Some(else_body),
..
} => {
// Extract carrier update from then branch
let then_update = extract_single_update(then_body, &parts.merged_var)?;
// Extract carrier update from else branch
let else_update = extract_single_update(else_body, &parts.merged_var)?;
(if_cond.as_ref().clone(), then_update, else_update)
}
_ => {
return Err("Pattern3: if-else structure mismatch after validation".to_string());
}
};
// Step 4: Extract loop increment
let loop_increment = match super::common_helpers::extract_loop_increment_plan(body, &parts.loop_var)? {
Some(inc) => inc,
None => {
return Err(format!(
"Pattern3: loop increment not found for variable '{}'",
parts.loop_var
));
}
};
Ok(Some(DomainPlan::Pattern3IfPhi(Pattern3IfPhiPlan {
loop_var: parts.loop_var,
carrier_var: parts.merged_var,
condition: condition.clone(),
if_condition,
then_update,
else_update,
loop_increment,
})))
}
/// Extract update expression from a branch body
///
/// Expects a single assignment: `carrier = <expr>`
/// Returns the RHS expression AST
fn extract_single_update(body: &[ASTNode], carrier_var: &str) -> Result<ASTNode, String> {
for stmt in body {
if let ASTNode::Assignment { target, value, .. } = stmt {
if let ASTNode::Variable { name, .. } = target.as_ref() {
if name == carrier_var {
return Ok(value.as_ref().clone());
}
}
}
}
Err(format!(
"Pattern3: carrier update not found for variable '{}' in branch",
carrier_var
))
}

View File

@ -96,11 +96,11 @@ fn fallback_extract(
allow_pattern8: bool,
) -> Result<Option<DomainPlan>, String> {
match kind {
PlanRuleId::Pattern1 => extractors::pattern1::extract_pattern1_plan(ctx.condition, ctx.body),
PlanRuleId::Pattern1 => Ok(None),
PlanRuleId::Pattern2 => {
extractors::pattern2_break::extract_pattern2_plan(ctx.condition, ctx.body)
}
PlanRuleId::Pattern3 => extractors::pattern3::extract_pattern3_plan(ctx.condition, ctx.body),
PlanRuleId::Pattern3 => Ok(None),
PlanRuleId::Pattern4 => extractors::pattern4::extract_pattern4_plan(ctx.condition, ctx.body),
PlanRuleId::Pattern5 => extractors::pattern5::extract_pattern5_plan(ctx.condition, ctx.body),
PlanRuleId::Pattern6 => {