phase29ap(p8): remove pattern4 legacy routing
This commit is contained in:
17
apps/tests/phase29ap_pattern4_continue_min.hako
Normal file
17
apps/tests/phase29ap_pattern4_continue_min.hako
Normal file
@ -0,0 +1,17 @@
|
||||
// Phase 29ap P8: Pattern4 continue minimal fixture (plan routing)
|
||||
// Expected output: 6
|
||||
|
||||
static box Main {
|
||||
main() {
|
||||
local i, sum
|
||||
i = 0
|
||||
sum = 0
|
||||
loop(i < 4) {
|
||||
if (i == 0) { i = i + 1; continue }
|
||||
sum = sum + i
|
||||
i = i + 1
|
||||
}
|
||||
print(sum)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
## Current Focus
|
||||
|
||||
- Phase: `docs/development/current/main/phases/phase-29ap/README.md`
|
||||
- Next: Phase 29ap P8 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`)
|
||||
- Next: Phase 29ap P9 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`)
|
||||
|
||||
## Gate (SSOT)
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ Scope: 「次にやる候補」を短く列挙するメモ。入口は `docs/dev
|
||||
|
||||
## Active
|
||||
|
||||
- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P8 planned)
|
||||
- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P9 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`
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ Related:
|
||||
## 1.1 Current (active)
|
||||
|
||||
- Active phase: `docs/development/current/main/phases/phase-29ap/README.md`
|
||||
- Next step: Phase 29ap P8 (planned)
|
||||
- Next step: Phase 29ap P9 (planned)
|
||||
|
||||
## 2. すでに固めた SSOT(再発防止の土台)
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ Goal: JoinIR の最小回帰セットを SSOT として固定する。
|
||||
- Pattern2 (release adopt, VM): `phase29ao_pattern2_release_adopt_vm`
|
||||
- Pattern3 (If‑Phi, VM): `phase118_pattern3_if_sum_vm`
|
||||
- Pattern3 (release adopt, VM): `phase29ao_pattern3_release_adopt_vm`
|
||||
- Pattern4 (continue min, VM): `phase29ap_pattern4_continue_min_vm`
|
||||
- Pattern1 (strict shadow, VM): `phase29ao_pattern1_strict_shadow_vm`
|
||||
- Pattern1 (subset reject, VM): `phase29ao_pattern1_subset_reject_extra_stmt_vm`
|
||||
- Pattern1 (stdlib to_lower, VM): `phase29ap_stringutils_tolower_vm`
|
||||
|
||||
@ -57,7 +57,7 @@ Gate (SSOT):
|
||||
|
||||
- Scope:
|
||||
- Remove Pattern8 from `LOOP_PATTERNS` so plan/composer stays SSOT for normal loops.
|
||||
- Keep legacy table as last resort for Pattern6_NestedLoopMinimal / Pattern4 / Pattern9 only.
|
||||
- Keep legacy table as a last resort for the remaining legacy entries.
|
||||
- Guardrails:
|
||||
- No change to logs or error strings.
|
||||
- Legacy routing remains a last-resort fallback.
|
||||
@ -81,8 +81,6 @@ Gate (SSOT):
|
||||
- No new logs or error strings.
|
||||
- Subset only: if shape deviates, return `Ok(None)`.
|
||||
|
||||
## Next (planned)
|
||||
|
||||
## P7: Pattern9 legacy table removal ✅
|
||||
|
||||
- Scope:
|
||||
@ -91,6 +89,15 @@ Gate (SSOT):
|
||||
- Guardrails:
|
||||
- No change to logs or error strings.
|
||||
|
||||
## P8: Remove Pattern4 legacy routing ✅
|
||||
|
||||
- Scope:
|
||||
- Remove Pattern4_WithContinue from `LOOP_PATTERNS`.
|
||||
- Add a minimal continue fixture + regression smoke under JoinIR gate.
|
||||
- Guardrails:
|
||||
- No change to logs or error strings.
|
||||
- Plan/composer remains the only routing path for Pattern4.
|
||||
|
||||
## Next (planned)
|
||||
|
||||
- P8: Follow-up legacy table shrink or leave as-is with justification
|
||||
- P9: Pattern6_NestedLoopMinimal migration/design (keep phase1883 gate green)
|
||||
|
||||
@ -23,5 +23,5 @@
|
||||
pub(crate) mod pattern1;
|
||||
pub(crate) mod pattern2; // Phase 282 P4: Pattern2 extraction
|
||||
pub(crate) mod pattern3; // Phase 282 P5: Pattern3 extraction
|
||||
pub(crate) mod pattern4; // Phase 282 P6: Pattern4 extraction
|
||||
// Pattern4 extraction migrated to plan routing (Phase 29ap P8).
|
||||
// Pattern5/8/9 extractors moved to plan layer; joinir uses plan::single_planner routing.
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
//! Phase 29aj P1: Plan-layer extractor wrapper (SSOT)
|
||||
|
||||
pub(crate) use crate::mir::builder::control_flow::plan::extractors::pattern4::*;
|
||||
@ -3,7 +3,7 @@
|
||||
//! Phase 2: Extracted from control_flow.rs
|
||||
//! - Pattern 1: Simple While Loop (pattern1_minimal.rs)
|
||||
//! - Pattern 3: Loop with If-Else PHI (pattern3_with_if_phi.rs)
|
||||
//! - Pattern 4: Loop with Continue (pattern4_with_continue.rs) [Phase 194+]
|
||||
//! - Pattern 4: Loop with Continue (migrated to plan routing in Phase 29ap P8)
|
||||
//!
|
||||
//! Phase 194: Table-driven router for pattern dispatch
|
||||
//! - Router module provides table-driven pattern matching
|
||||
@ -31,14 +31,13 @@
|
||||
//! - condition_env_builder.rs: Unified ConditionEnv construction (Issue 5)
|
||||
//!
|
||||
//! Phase 33-23: Pattern-Specific Analyzers (Stage 2)
|
||||
//! - pattern4_carrier_analyzer.rs: Pattern 4 carrier analysis and normalization (Issue 2)
|
||||
//!
|
||||
//! Stage 3 + Issue 1: Trim Pattern Extraction
|
||||
//! - trim_pattern_validator.rs: Trim pattern validation and whitespace check generation
|
||||
//! - trim_pattern_lowerer.rs: Trim-specific JoinIR lowering
|
||||
//!
|
||||
//! Phase 179-B: Generic Pattern Framework
|
||||
//! - pattern_pipeline.rs: Unified preprocessing pipeline for Patterns 1-4
|
||||
//! - pattern_pipeline.rs: Unified preprocessing pipeline for Patterns 1-3
|
||||
//!
|
||||
//! Phase 91 P5b: Escape Pattern Recognizer
|
||||
//! - escape_pattern_recognizer.rs: P5b (escape sequence handling) pattern detection
|
||||
@ -78,8 +77,6 @@ pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
|
||||
pub(in crate::mir::builder) mod loop_scope_shape_builder;
|
||||
pub(in crate::mir::builder) mod pattern1_minimal;
|
||||
pub(in crate::mir::builder) mod pattern3_with_if_phi;
|
||||
pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
|
||||
pub(in crate::mir::builder) mod pattern4_with_continue;
|
||||
pub(in crate::mir::builder) mod pattern6_nested_minimal; // Phase 188.3: 1-level nested loop (Pattern1 outer + Pattern1 inner)
|
||||
pub(in crate::mir::builder) mod pattern8_scan_bool_predicate; // Phase 259 P0: boolean predicate scan (is_integer/is_valid)
|
||||
pub(in crate::mir::builder) mod pattern_pipeline;
|
||||
|
||||
@ -1,366 +0,0 @@
|
||||
//! Phase 33-23: Pattern 4 Carrier Analysis
|
||||
//!
|
||||
//! Extracts carrier analysis logic from pattern4_with_continue.rs.
|
||||
//! Responsible for:
|
||||
//! - Identifying which carriers are updated by continue branches
|
||||
//! - Normalizing else-continue clauses
|
||||
//! - Filtering out invalid carriers
|
||||
//!
|
||||
//! # Design Philosophy
|
||||
//!
|
||||
//! - **Single responsibility**: Focus only on carrier analysis for Pattern 4
|
||||
//! - **Reusability**: Can be used by future continue-pattern variants
|
||||
//! - **Testability**: Pure functions that are easy to unit test
|
||||
//! - **Independence**: Does not depend on MirBuilder context
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierVar};
|
||||
use crate::mir::join_ir::lowering::continue_branch_normalizer::ContinueBranchNormalizer;
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
|
||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
pub(crate) struct Pattern4CarrierAnalyzer;
|
||||
|
||||
impl Pattern4CarrierAnalyzer {
|
||||
/// Analyze and filter carriers for continue pattern
|
||||
///
|
||||
/// Identifies which carriers are actually updated by continue branches
|
||||
/// and filters out carriers that don't participate in the loop body.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `loop_body` - The loop body AST nodes to analyze
|
||||
/// * `all_carriers` - All potential carrier variables from initial analysis
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// CarrierInfo containing only the carriers that are actually updated
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```nyash
|
||||
/// local i = 0
|
||||
/// local sum = 0
|
||||
/// local M = 10 // Constant, should be filtered out
|
||||
/// loop(i < M) {
|
||||
/// i = i + 1
|
||||
/// if i % 2 == 0 { continue }
|
||||
/// sum = sum + i
|
||||
/// }
|
||||
/// // Result: carriers = [i, sum], NOT [i, sum, M]
|
||||
/// ```
|
||||
pub fn analyze_carriers(
|
||||
loop_body: &[ASTNode],
|
||||
all_carriers: &CarrierInfo,
|
||||
) -> Result<CarrierInfo, String> {
|
||||
// Identify which carriers are updated in loop body
|
||||
let carrier_updates =
|
||||
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, &all_carriers.carriers);
|
||||
|
||||
// Filter carriers: only keep those that have update expressions
|
||||
let updated_carriers: Vec<CarrierVar> = all_carriers
|
||||
.carriers
|
||||
.iter()
|
||||
.filter(|carrier| carrier_updates.contains_key(&carrier.name))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
Ok(CarrierInfo {
|
||||
loop_var_name: all_carriers.loop_var_name.clone(),
|
||||
loop_var_id: all_carriers.loop_var_id,
|
||||
carriers: updated_carriers,
|
||||
trim_helper: all_carriers.trim_helper.clone(),
|
||||
promoted_loopbodylocals: all_carriers.promoted_loopbodylocals.clone(), // Phase 224
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: all_carriers.promoted_bindings.clone(), // Phase 76
|
||||
})
|
||||
}
|
||||
|
||||
/// Analyze carrier update expressions
|
||||
///
|
||||
/// Delegates to LoopUpdateAnalyzer but returns the result in a more
|
||||
/// convenient form for Pattern 4 processing.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Map from carrier name to UpdateExpr
|
||||
pub fn analyze_carrier_updates(
|
||||
loop_body: &[ASTNode],
|
||||
carriers: &[CarrierVar],
|
||||
) -> BTreeMap<String, UpdateExpr> {
|
||||
// Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers)
|
||||
}
|
||||
|
||||
/// Normalize continue branches to standard form
|
||||
///
|
||||
/// Transforms else-continue patterns to a canonical form
|
||||
/// for easier JoinIR lowering.
|
||||
///
|
||||
/// # Pattern
|
||||
///
|
||||
/// Transforms: `if (cond) { body } else { continue }`
|
||||
/// Into: `if (!cond) { continue } else { body }`
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `body` - Loop body statements to normalize
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Normalized loop body with all continue statements in then branches
|
||||
pub fn normalize_continue_branches(body: &[ASTNode]) -> Vec<ASTNode> {
|
||||
ContinueBranchNormalizer::normalize_loop_body(body)
|
||||
}
|
||||
|
||||
/// Verify continue pattern structure
|
||||
///
|
||||
/// Ensures the loop has proper continue structure for Pattern 4.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `body` - Loop body statements to validate
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Ok(()) if continue structure is valid, Err(message) otherwise
|
||||
#[allow(dead_code)]
|
||||
pub fn validate_continue_structure(body: &[ASTNode]) -> Result<(), String> {
|
||||
// Check for at least one continue statement
|
||||
for stmt in body {
|
||||
if Self::has_continue(stmt) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err("No continue statement found in loop body".to_string())
|
||||
}
|
||||
|
||||
/// Helper: Check if node or its children contain continue
|
||||
///
|
||||
/// Recursively searches the AST for continue statements.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `node` - AST node to check
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// true if the node or any of its children is a Continue statement
|
||||
#[allow(dead_code)]
|
||||
fn has_continue(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Continue { .. } => true,
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
then_body.iter().any(|n| Self::has_continue(n))
|
||||
|| else_body
|
||||
.as_ref()
|
||||
.map_or(false, |body| body.iter().any(|n| Self::has_continue(n)))
|
||||
}
|
||||
ASTNode::Loop { body, .. } => body.iter().any(|n| Self::has_continue(n)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
||||
use crate::mir::ValueId;
|
||||
|
||||
#[test]
|
||||
fn test_validate_continue_structure_present() {
|
||||
// Test with continue statement
|
||||
let body = vec![ASTNode::Continue {
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
assert!(Pattern4CarrierAnalyzer::validate_continue_structure(&body).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_continue_structure_absent() {
|
||||
// Test without continue statement
|
||||
let body = vec![ASTNode::Break {
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
assert!(Pattern4CarrierAnalyzer::validate_continue_structure(&body).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_continue_detection() {
|
||||
let continue_node = ASTNode::Continue {
|
||||
span: Span::unknown(),
|
||||
};
|
||||
assert!(Pattern4CarrierAnalyzer::has_continue(&continue_node));
|
||||
|
||||
let break_node = ASTNode::Break {
|
||||
span: Span::unknown(),
|
||||
};
|
||||
assert!(!Pattern4CarrierAnalyzer::has_continue(&break_node));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_continue_nested_in_if() {
|
||||
// if (x) { continue } else { ... }
|
||||
let if_node = ASTNode::If {
|
||||
condition: Box::new(ASTNode::Variable {
|
||||
name: "x".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
then_body: vec![ASTNode::Continue {
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
else_body: None,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
assert!(Pattern4CarrierAnalyzer::has_continue(&if_node));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_analyze_carriers_filtering() {
|
||||
// Test that analyze_carriers filters out non-updated carriers
|
||||
let span = Span::unknown();
|
||||
|
||||
// Create loop body: i = i + 1, sum = sum + i
|
||||
let loop_body = vec![
|
||||
ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: span.clone(),
|
||||
}),
|
||||
span: span.clone(),
|
||||
}),
|
||||
span: span.clone(),
|
||||
},
|
||||
ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
span: span.clone(),
|
||||
}),
|
||||
span: span.clone(),
|
||||
},
|
||||
];
|
||||
|
||||
// Create CarrierInfo with i, sum, and M (constant)
|
||||
let all_carriers = CarrierInfo {
|
||||
loop_var_name: "i".to_string(),
|
||||
loop_var_id: ValueId(0),
|
||||
carriers: vec![
|
||||
CarrierVar {
|
||||
name: "i".to_string(),
|
||||
host_id: ValueId(1),
|
||||
join_id: None,
|
||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
binding_id: None,
|
||||
},
|
||||
CarrierVar {
|
||||
name: "sum".to_string(),
|
||||
host_id: ValueId(2),
|
||||
join_id: None,
|
||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
binding_id: None,
|
||||
},
|
||||
CarrierVar {
|
||||
name: "M".to_string(),
|
||||
host_id: ValueId(3),
|
||||
join_id: None,
|
||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
binding_id: None,
|
||||
},
|
||||
],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: Vec::new(), // Phase 224
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(), // Phase 76
|
||||
};
|
||||
|
||||
// Analyze carriers
|
||||
let result = Pattern4CarrierAnalyzer::analyze_carriers(&loop_body, &all_carriers);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let filtered = result.unwrap();
|
||||
// Should only have i and sum, not M
|
||||
assert_eq!(filtered.carriers.len(), 2);
|
||||
assert!(filtered.carriers.iter().any(|c| c.name == "i"));
|
||||
assert!(filtered.carriers.iter().any(|c| c.name == "sum"));
|
||||
assert!(!filtered.carriers.iter().any(|c| c.name == "M"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_continue_branches() {
|
||||
// Test normalization delegation
|
||||
let span = Span::unknown();
|
||||
|
||||
// Create if-else-continue pattern
|
||||
let body = vec![ASTNode::If {
|
||||
condition: Box::new(ASTNode::Variable {
|
||||
name: "x".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
then_body: vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "y".to_string(),
|
||||
span: span.clone(),
|
||||
}),
|
||||
value: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: span.clone(),
|
||||
}),
|
||||
span: span.clone(),
|
||||
}],
|
||||
else_body: Some(vec![ASTNode::Continue { span: span.clone() }]),
|
||||
span: span.clone(),
|
||||
}];
|
||||
|
||||
let normalized = Pattern4CarrierAnalyzer::normalize_continue_branches(&body);
|
||||
|
||||
// Should be transformed to if (!x) { continue } else { y = 1 }
|
||||
assert_eq!(normalized.len(), 1);
|
||||
if let ASTNode::If {
|
||||
condition,
|
||||
then_body,
|
||||
..
|
||||
} = &normalized[0]
|
||||
{
|
||||
// Condition should be negated
|
||||
assert!(matches!(**condition, ASTNode::UnaryOp { .. }));
|
||||
// Then body should be continue
|
||||
assert_eq!(then_body.len(), 1);
|
||||
assert!(matches!(then_body[0], ASTNode::Continue { .. }));
|
||||
} else {
|
||||
panic!("Expected If node");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,534 +0,0 @@
|
||||
//! Phase 33-21: Pattern 4 (Loop with Continue) Implementation
|
||||
//!
|
||||
//! **Current Status**: Fully implemented using Select-based continue semantics.
|
||||
//!
|
||||
//! ## Implementation Approach
|
||||
//!
|
||||
//! Continue is handled via Select instruction for carrier updates:
|
||||
//! - When continue condition is true: carrier keeps its value (no update)
|
||||
//! - When continue condition is false: carrier gets updated value
|
||||
//!
|
||||
//! This avoids the complexity of explicit latch blocks by using PHI-based
|
||||
//! value selection at each iteration.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```nyash
|
||||
//! local i = 0
|
||||
//! local sum = 0
|
||||
//! loop(i < 10) {
|
||||
//! i = i + 1
|
||||
//! if i % 2 == 0 { continue } // Skip even numbers
|
||||
//! sum = sum + i
|
||||
//! }
|
||||
//! // sum = 25 (1+3+5+7+9)
|
||||
//! ```
|
||||
//!
|
||||
//! ## Key Implementation Details
|
||||
//!
|
||||
//! - **Select instruction**: Used to conditionally skip carrier updates
|
||||
//! - **Header PHI**: Tracks carrier values across iterations
|
||||
//! - **ExitMeta**: Maps final carrier values to host variable slots
|
||||
//! - **Phase 33-21 fix**: Correct remapping of function parameters to header PHI dsts
|
||||
|
||||
use super::super::trace;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||
use crate::mir::loop_pattern_detection::error_messages;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
// Phase 284 P1: has_return_in_body/has_return_node removed
|
||||
// Return detection now handled by return_collector SSOT in conversion_pipeline.rs
|
||||
|
||||
/// Phase 282 P6: Detection function for Pattern 4 (ExtractionBased)
|
||||
///
|
||||
/// Migrated from StructureBased to ExtractionBased detection model.
|
||||
/// Uses pure extractor (extract_loop_with_continue_parts) for SSOT.
|
||||
///
|
||||
/// Pattern 4 matches loops with continue statements.
|
||||
///
|
||||
/// # ExtractionBased Detection (Phase 282 P6)
|
||||
///
|
||||
/// Two-stage guard:
|
||||
/// 1. **Safety valve**: ctx.pattern_kind == Pattern4Continue (O(1) perf guard)
|
||||
/// 2. **Extractor**: extract_loop_with_continue_parts (SSOT)
|
||||
///
|
||||
/// # Detection Rules (from extractor)
|
||||
///
|
||||
/// 1. **Condition**: Comparison operator with left=variable (Pattern1 reuse)
|
||||
/// 2. **Must have continue**: At least 1 continue statement (recursive detection)
|
||||
/// 3. **No break**: break statements → Pattern2 territory
|
||||
/// 4. **No return**: return statements → Err (close-but-unsupported, Phase 142 P2 pending)
|
||||
/// 5. **Carrier validation**: Delegated to CommonPatternInitializer (existing logic)
|
||||
///
|
||||
/// If all conditions are met, Pattern 4 is detected.
|
||||
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
use super::common_init::CommonPatternInitializer;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
|
||||
// Phase 282 P6 Step 1: Pattern kind safety valve (O(1) guard)
|
||||
if ctx.pattern_kind != LoopPatternKind::Pattern4Continue {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase 282 P6 Step 2: ExtractionBased detection (SSOT)
|
||||
use super::extractors::pattern4::extract_loop_with_continue_parts;
|
||||
|
||||
match extract_loop_with_continue_parts(ctx.condition, ctx.body) {
|
||||
Ok(Some(parts)) => {
|
||||
trace::trace().debug(
|
||||
"pattern4/can_lower",
|
||||
&format!(
|
||||
"✅ Pattern4 detected: loop_var='{}', continue_count={}",
|
||||
parts.loop_var, parts.continue_count
|
||||
),
|
||||
);
|
||||
|
||||
// Phase 282 P6 Step 3: Carrier validation (existing logic preserved)
|
||||
CommonPatternInitializer::check_carrier_updates_allowed(
|
||||
ctx.body,
|
||||
&parts.loop_var,
|
||||
&builder.variable_ctx.variable_map,
|
||||
)
|
||||
}
|
||||
Ok(None) => {
|
||||
trace::trace().debug(
|
||||
"pattern4/can_lower",
|
||||
"Not Pattern4 (extraction returned None - structural mismatch)",
|
||||
);
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
// USER CORRECTION: Log "unsupported" for Err cases (e.g., return found)
|
||||
trace::trace().debug(
|
||||
"pattern4/can_lower",
|
||||
&format!("Pattern4 unsupported: {}", e),
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 282 P6: Lowering function for Pattern 4 (ExtractionBased)
|
||||
///
|
||||
/// Re-extracts pattern parts to enforce SSOT (extraction is single source of truth).
|
||||
/// Wrapper around cf_loop_pattern4_with_continue to match router signature.
|
||||
///
|
||||
/// # Implementation (Phase 195-197 + Phase 282 P6)
|
||||
///
|
||||
/// 1. Re-extract pattern parts (SSOT enforcement - Phase 282 P6)
|
||||
/// 2. Extract loop variables from condition
|
||||
/// 3. Generate JoinIR with continue support (lower_loop_with_continue_minimal)
|
||||
/// 4. Convert JoinModule → MirModule
|
||||
/// 5. Create JoinInlineBoundary for input/output mapping
|
||||
/// 6. Merge MIR blocks into current_function
|
||||
/// 7. Return loop result (first carrier value)
|
||||
pub(crate) fn lower(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &super::router::LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Phase 282 P6 Step 4: Re-extract to enforce SSOT (extraction must succeed in lower())
|
||||
use super::extractors::pattern4::extract_loop_with_continue_parts;
|
||||
|
||||
let parts = match extract_loop_with_continue_parts(ctx.condition, ctx.body) {
|
||||
Ok(Some(p)) => p,
|
||||
Ok(None) => {
|
||||
return Err(
|
||||
"[pattern4/lower] Extraction returned None (should not happen - can_lower() passed)"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
// USER CORRECTION: Return Err directly for fail-fast (e.g., return found)
|
||||
return Err(format!("[pattern4/lower] Extraction failed: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4/lower",
|
||||
&format!(
|
||||
"Pattern4 lowering: loop_var='{}', continue_count={}",
|
||||
parts.loop_var, parts.continue_count
|
||||
),
|
||||
);
|
||||
|
||||
// Phase 284 P1: Return check removed - now handled by return_collector SSOT
|
||||
// in conversion_pipeline.rs (JoinIR line common entry point)
|
||||
|
||||
// Phase 33-19: Connect to actual implementation (zero behavior change)
|
||||
builder.cf_loop_pattern4_with_continue(ctx.condition, ctx.body, ctx.func_name, ctx.debug)
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
/// Phase 179-B: Pattern 4 (Loop with Continue) lowerer
|
||||
///
|
||||
/// **Refactored**: Now uses PatternPipelineContext for unified preprocessing
|
||||
///
|
||||
/// # Pipeline (Phase 179-B)
|
||||
/// 1. Build preprocessing context → PatternPipelineContext
|
||||
/// 2. Pattern 4-specific processing (continue normalization, carrier analysis)
|
||||
/// 3. Call JoinIR lowerer → JoinModule
|
||||
/// 4. Create boundary from context → JoinInlineBoundary
|
||||
/// 5. Merge MIR blocks → JoinIRConversionPipeline
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```nyash
|
||||
/// local i = 0
|
||||
/// local sum = 0
|
||||
/// loop(i < 10) {
|
||||
/// i = i + 1
|
||||
/// if (i % 2 == 0) {
|
||||
/// continue // Skip even numbers
|
||||
/// }
|
||||
/// sum = sum + i
|
||||
/// }
|
||||
/// // sum = 25 (1+3+5+7+9)
|
||||
/// ```
|
||||
///
|
||||
/// Note: Pattern 4 has complex carrier analysis logic that remains inline
|
||||
/// for now, using Pattern4CarrierAnalyzer for Select-based continue semantics.
|
||||
pub(in crate::mir::builder) fn cf_loop_pattern4_with_continue(
|
||||
&mut self,
|
||||
condition: &ASTNode,
|
||||
_body: &[ASTNode],
|
||||
_func_name: &str,
|
||||
debug: bool,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().debug("pattern4", "Calling Pattern 4 minimal lowerer");
|
||||
|
||||
let prepared = prepare_pattern4_context(self, condition, _body)?;
|
||||
lower_pattern4_joinir(self, condition, &prepared, debug)
|
||||
}
|
||||
}
|
||||
|
||||
/// Preprocessed data for Pattern 4 lowering.
|
||||
struct Pattern4Prepared<'a> {
|
||||
loop_var_name: String,
|
||||
loop_scope: crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape,
|
||||
carrier_info: crate::mir::join_ir::lowering::carrier_info::CarrierInfo,
|
||||
carrier_updates: BTreeMap<String, UpdateExpr>,
|
||||
/// Phase 284 P1: Reference to loop body for return detection
|
||||
body: &'a [ASTNode],
|
||||
}
|
||||
|
||||
/// Normalize, build context, analyze carriers, and promote loop-body locals.
|
||||
fn prepare_pattern4_context<'a>(
|
||||
builder: &mut MirBuilder,
|
||||
condition: &ASTNode,
|
||||
body: &'a [ASTNode],
|
||||
) -> Result<Pattern4Prepared<'a>, String> {
|
||||
use super::pattern4_carrier_analyzer::Pattern4CarrierAnalyzer;
|
||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
|
||||
ConditionPromotionRequest, ConditionPromotionResult, LoopBodyCondPromoter,
|
||||
};
|
||||
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
||||
|
||||
// Normalize continue branches for analysis/lowering
|
||||
let normalized_body = Pattern4CarrierAnalyzer::normalize_continue_branches(body);
|
||||
|
||||
// Build preprocessing context
|
||||
let ctx = build_pattern_context(
|
||||
builder,
|
||||
condition,
|
||||
&normalized_body,
|
||||
PatternVariant::Pattern4,
|
||||
)?;
|
||||
|
||||
let loop_var_name = ctx.loop_var_name.clone();
|
||||
let loop_scope = ctx.loop_scope.clone();
|
||||
let carrier_info_prelim = ctx.carrier_info.clone();
|
||||
|
||||
// Analyze carrier updates and filter carriers
|
||||
let carrier_updates = Pattern4CarrierAnalyzer::analyze_carrier_updates(
|
||||
&normalized_body,
|
||||
&carrier_info_prelim.carriers,
|
||||
);
|
||||
let mut carrier_info =
|
||||
Pattern4CarrierAnalyzer::analyze_carriers(&normalized_body, &carrier_info_prelim)?;
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!(
|
||||
"CarrierInfo: loop_var={}, carriers={:?}",
|
||||
carrier_info.loop_var_name,
|
||||
carrier_info
|
||||
.carriers
|
||||
.iter()
|
||||
.map(|c| &c.name)
|
||||
.collect::<Vec<_>>()
|
||||
),
|
||||
);
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!(
|
||||
"Analyzed {} carrier update expressions",
|
||||
carrier_updates.len()
|
||||
),
|
||||
);
|
||||
for (carrier_name, update_expr) in &carrier_updates {
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!(" {} → {:?}", carrier_name, update_expr),
|
||||
);
|
||||
}
|
||||
|
||||
// LoopBodyLocal promotion for conditions (skip_whitespace etc.)
|
||||
let continue_cond = LoopBodyCondPromoter::extract_continue_condition(&normalized_body);
|
||||
let conditions_to_analyze: Vec<&ASTNode> = if let Some(cont_cond) = continue_cond {
|
||||
vec![condition, cont_cond]
|
||||
} else {
|
||||
vec![condition]
|
||||
};
|
||||
|
||||
let cond_scope =
|
||||
LoopConditionScopeBox::analyze(&loop_var_name, &conditions_to_analyze, Some(&loop_scope));
|
||||
|
||||
if cond_scope.has_loop_body_local() {
|
||||
let promotion_req = ConditionPromotionRequest {
|
||||
loop_param_name: &loop_var_name,
|
||||
cond_scope: &cond_scope,
|
||||
scope_shape: Some(&loop_scope),
|
||||
break_cond: None, // Pattern 4 has no break
|
||||
continue_cond,
|
||||
loop_body: &normalized_body,
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
// Phase 136 Step 4/7: Use binding_ctx for binding_map reference
|
||||
binding_map: Some(builder.binding_ctx.binding_map()),
|
||||
};
|
||||
|
||||
match LoopBodyCondPromoter::try_promote_for_condition(promotion_req) {
|
||||
ConditionPromotionResult::Promoted {
|
||||
carrier_info: promoted_carrier,
|
||||
promoted_var,
|
||||
carrier_name,
|
||||
} => {
|
||||
trace::trace().debug(
|
||||
"pattern4/cond_promoter",
|
||||
&format!(
|
||||
"LoopBodyLocal '{}' promoted to carrier '{}'",
|
||||
promoted_var, carrier_name
|
||||
),
|
||||
);
|
||||
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
{
|
||||
use crate::mir::join_ir::lowering::carrier_binding_assigner::CarrierBindingAssigner;
|
||||
let mut promoted_carrier = promoted_carrier;
|
||||
CarrierBindingAssigner::assign_promoted_binding(
|
||||
builder,
|
||||
&mut promoted_carrier,
|
||||
&promoted_var,
|
||||
&carrier_name,
|
||||
)
|
||||
.map_err(|e| format!("[phase78/binding_assign] {:?}", e))?;
|
||||
carrier_info.merge_from(&promoted_carrier);
|
||||
}
|
||||
#[cfg(not(feature = "normalized_dev"))]
|
||||
{
|
||||
carrier_info.merge_from(&promoted_carrier);
|
||||
}
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4/cond_promoter",
|
||||
&format!(
|
||||
"Merged carrier '{}' into CarrierInfo (total carriers: {})",
|
||||
carrier_name,
|
||||
carrier_info.carrier_count()
|
||||
),
|
||||
);
|
||||
|
||||
if let Some(helper) = carrier_info.trim_helper() {
|
||||
if helper.is_safe_trim() {
|
||||
trace::trace().debug(
|
||||
"pattern4/cond_promoter",
|
||||
"Safe Trim/skip_whitespace pattern detected",
|
||||
);
|
||||
trace::trace().debug(
|
||||
"pattern4/cond_promoter",
|
||||
&format!(
|
||||
"Carrier: '{}', original var: '{}', whitespace chars: {:?}",
|
||||
helper.carrier_name, helper.original_var, helper.whitespace_chars
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Err(error_messages::format_error_pattern4_trim_not_safe(
|
||||
&helper.carrier_name,
|
||||
helper.whitespace_count(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
ConditionPromotionResult::CannotPromote { reason, vars } => {
|
||||
return Err(error_messages::format_error_pattern4_promotion_failed(
|
||||
&vars, &reason,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Pattern4Prepared {
|
||||
loop_var_name,
|
||||
loop_scope,
|
||||
carrier_info,
|
||||
carrier_updates,
|
||||
body, // Phase 284 P1: Include body for return detection
|
||||
})
|
||||
}
|
||||
|
||||
/// JoinIR lowering stage for Pattern 4 using prepared context.
|
||||
fn lower_pattern4_joinir(
|
||||
builder: &mut MirBuilder,
|
||||
condition: &ASTNode,
|
||||
prepared: &Pattern4Prepared<'_>,
|
||||
debug: bool,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
|
||||
use crate::mir::join_ir::lowering::return_collector::{collect_return_from_body, ReturnInfo};
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
|
||||
trace::trace().varmap("pattern4_start", &builder.variable_ctx.variable_map);
|
||||
|
||||
// Phase 284 P1: Detect return statements in body
|
||||
let return_info: Option<ReturnInfo> = match collect_return_from_body(prepared.body) {
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
return Err(format!("[pattern4] Return detection failed: {}", e));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(ref ret_info) = return_info {
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!(
|
||||
"Phase 284 P1: Return detected - value={}, has_condition={}, in_else={}",
|
||||
ret_info.value,
|
||||
ret_info.condition.is_some(),
|
||||
ret_info.in_else
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
// Phase 136 Step 4/7: Use binding_ctx for binding_map clone
|
||||
let binding_map_clone = builder.binding_ctx.binding_map().clone();
|
||||
|
||||
let (join_module, exit_meta) = match lower_loop_with_continue_minimal(
|
||||
prepared.loop_scope.clone(),
|
||||
condition,
|
||||
builder,
|
||||
&prepared.carrier_info,
|
||||
&prepared.carrier_updates,
|
||||
&mut join_value_space,
|
||||
return_info.as_ref(), // Phase 284 P1
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
Some(&binding_map_clone),
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
trace::trace().debug("pattern4", &format!("Pattern 4 lowerer failed: {}", e));
|
||||
return Err(error_messages::format_error_pattern4_lowering_failed(&e));
|
||||
}
|
||||
};
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("ExitMeta: {} exit bindings", exit_meta.exit_values.len()),
|
||||
);
|
||||
for (carrier_name, join_value) in &exit_meta.exit_values {
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!(" {} → ValueId({})", carrier_name, join_value.0),
|
||||
);
|
||||
}
|
||||
|
||||
let exit_bindings = ExitMetaCollector::collect(
|
||||
builder,
|
||||
&exit_meta,
|
||||
Some(&prepared.carrier_info), // Phase 228-8: Include ConditionOnly carriers
|
||||
debug,
|
||||
);
|
||||
|
||||
for carrier in &prepared.carrier_info.carriers {
|
||||
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
|
||||
return Err(error_messages::format_error_pattern4_carrier_not_found(
|
||||
&carrier.name,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 256.8.5: Use JoinModule.entry.params as SSOT (no hardcoded ValueIds)
|
||||
use super::common::get_entry_function;
|
||||
let main_func = get_entry_function(&join_module, "pattern4")?;
|
||||
|
||||
// SSOT: Use actual params allocated by JoinIR lowerer
|
||||
let join_inputs = main_func.params.clone();
|
||||
|
||||
// Build host_inputs in same order (loop_var + carriers)
|
||||
let mut host_inputs = vec![prepared.carrier_info.loop_var_id];
|
||||
for carrier in &prepared.carrier_info.carriers {
|
||||
host_inputs.push(carrier.host_id);
|
||||
}
|
||||
|
||||
// Verify count consistency (fail-fast)
|
||||
if join_inputs.len() != host_inputs.len() {
|
||||
return Err(format!(
|
||||
"[pattern4] Params count mismatch: join_inputs={}, host_inputs={}",
|
||||
join_inputs.len(), host_inputs.len()
|
||||
));
|
||||
}
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!(
|
||||
"join_inputs (SSOT): {:?}, host_inputs: {:?}",
|
||||
join_inputs.iter().map(|v| v.0).collect::<Vec<_>>(),
|
||||
host_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
|
||||
),
|
||||
);
|
||||
|
||||
// Phase 284 P1: Build continuation set - include k_return if return_info exists
|
||||
let continuation_funcs = {
|
||||
use crate::mir::join_ir::lowering::canonical_names as cn;
|
||||
let mut funcs = std::collections::BTreeSet::new();
|
||||
funcs.insert(cn::K_EXIT.to_string());
|
||||
if return_info.is_some() {
|
||||
funcs.insert(cn::K_RETURN.to_string());
|
||||
}
|
||||
funcs
|
||||
};
|
||||
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(prepared.loop_var_name.clone()))
|
||||
.with_carrier_info(prepared.carrier_info.clone())
|
||||
.with_continuation_funcs(continuation_funcs) // Phase 284 P1
|
||||
.build();
|
||||
|
||||
let _result_val = JoinIRConversionPipeline::execute(
|
||||
builder,
|
||||
join_module,
|
||||
Some(&boundary),
|
||||
"pattern4",
|
||||
debug,
|
||||
)?;
|
||||
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("Loop complete, returning Void {:?}", void_val),
|
||||
);
|
||||
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
@ -151,7 +151,7 @@ impl<'a> LoopPatternContext<'a> {
|
||||
|
||||
/// Phase 286 P2.2: Common helper for Plan line lowering
|
||||
///
|
||||
/// Extracts the common 3-line pattern used by Pattern6/7/4/1:
|
||||
/// Extracts the common 3-line pattern used by Plan-based routing:
|
||||
/// 1. Normalize DomainPlan → CorePlan
|
||||
/// 2. Verify CorePlan invariants (fail-fast)
|
||||
/// 3. Lower CorePlan → MIR
|
||||
@ -180,7 +180,7 @@ fn lower_via_plan(
|
||||
/// - Cons: Extraction can be expensive (but amortized over lowering)
|
||||
///
|
||||
/// ## StructureBased (Feature Classification)
|
||||
/// - Used by: Pattern4 (legacy)
|
||||
/// - Used by: Pattern6_NestedLoopMinimal (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)
|
||||
@ -188,7 +188,7 @@ fn lower_via_plan(
|
||||
/// ## Rationale for Dual Strategy:
|
||||
/// - Pattern6/7: Complex extraction logic (variable step, carrier tracking)
|
||||
/// → ExtractionBased avoids duplication between detect and extract
|
||||
/// - Other patterns: Simple structural features (continue)
|
||||
/// - Other patterns: Legacy structural features
|
||||
/// → StructureBased leverages centralized LoopFeatures classification
|
||||
///
|
||||
/// This documentation prevents bugs like Phase 272 P0.2's Pattern7 issue
|
||||
@ -201,7 +201,7 @@ pub(crate) enum CanLowerStrategy {
|
||||
ExtractionBased,
|
||||
|
||||
/// Structure-based detection: Check pattern_kind from LoopPatternContext
|
||||
/// Used by Pattern4
|
||||
/// Used by legacy patterns
|
||||
StructureBased,
|
||||
}
|
||||
|
||||
@ -222,18 +222,13 @@ pub(crate) struct LoopPatternEntry {
|
||||
///
|
||||
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
|
||||
/// Array order defines priority - earlier entries are tried first.
|
||||
/// Pattern6_NestedLoopMinimal → Pattern4
|
||||
/// Pattern6_NestedLoopMinimal (legacy)
|
||||
///
|
||||
/// # Current Patterns (Structure-based detection, established Phase 131-11+)
|
||||
///
|
||||
/// Pattern detection strategies (updated Phase 286):
|
||||
/// - Pattern6/7: ExtractionBased (Plan line, Phase 273+)
|
||||
/// - Pattern8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
|
||||
/// - Pattern4: StructureBased (LoopFeatures classification, legacy)
|
||||
///
|
||||
/// - Pattern 4: Loop with Continue (loop_continue_pattern4.hako)
|
||||
/// - Detection: pattern_kind == Pattern4Continue
|
||||
/// - Structure: has_continue && !has_break
|
||||
///
|
||||
/// - Pattern3: moved to plan-based routing (planner+composer SSOT)
|
||||
///
|
||||
@ -248,11 +243,6 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
detect: super::pattern6_nested_minimal::can_lower,
|
||||
lower: super::pattern6_nested_minimal::lower,
|
||||
},
|
||||
LoopPatternEntry {
|
||||
name: "Pattern4_WithContinue",
|
||||
detect: super::pattern4_with_continue::can_lower,
|
||||
lower: super::pattern4_with_continue::lower,
|
||||
},
|
||||
// Phase 273 P0.1: Pattern6 entry removed (migrated to Plan-based routing)
|
||||
// Pattern6_ScanWithInit now handled via extract_scan_with_init_plan() + PlanLowerer
|
||||
// Phase 273 P2: Pattern7 entry removed (migrated to Plan-based routing)
|
||||
@ -270,18 +260,18 @@ 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 (Pattern4): ctx.pattern_kind classification (legacy)
|
||||
/// - Structure-based (Pattern6_NestedLoopMinimal): 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 Pattern4 table for other patterns
|
||||
/// 3. Fall through to legacy Pattern6_NestedLoopMinimal table for other patterns
|
||||
///
|
||||
/// The Plan line (Extractor → Normalizer → Verifier → Lowerer) is the
|
||||
/// current operational SSOT for Pattern6/7. Legacy patterns (1/2/4/8/9) use
|
||||
/// the traditional LoopPatternContext-based routing.
|
||||
/// current operational SSOT for Pattern6/7. Legacy patterns
|
||||
/// (Pattern6_NestedLoopMinimal) use the traditional LoopPatternContext-based routing.
|
||||
///
|
||||
/// Plan-based architecture (Phase 273 P1-P3):
|
||||
/// - extract_*_plan() → DomainPlan (pure extraction, no builder)
|
||||
@ -292,7 +282,6 @@ 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)
|
||||
/// - Pattern4: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
|
||||
/// - Pattern6_NestedLoopMinimal: src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs
|
||||
pub(crate) fn route_loop_pattern(
|
||||
builder: &mut MirBuilder,
|
||||
|
||||
@ -23,6 +23,7 @@ run_filter "pattern2_subset" "phase29ai_pattern2_break_plan_subset_ok_min_vm" ||
|
||||
run_filter "pattern2_release_adopt_vm" "phase29ao_pattern2_release_adopt_vm" || exit 1
|
||||
run_filter "pattern3_ifphi_vm" "phase118_pattern3_if_sum_vm" || exit 1
|
||||
run_filter "pattern3_release_adopt_vm" "phase29ao_pattern3_release_adopt_vm" || exit 1
|
||||
run_filter "pattern4_continue_vm" "phase29ap_pattern4_continue_min_vm" || exit 1
|
||||
run_filter "pattern1_strict_shadow_vm" "phase29ao_pattern1_strict_shadow_vm" || exit 1
|
||||
run_filter "pattern1_subset_reject_extra_stmt_vm" "phase29ao_pattern1_subset_reject_extra_stmt_vm" || exit 1
|
||||
run_filter "pattern1_stringutils_tolower_vm" "phase29ap_stringutils_tolower_vm" || exit 1
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
# phase29ap_pattern4_continue_min_vm.sh - Pattern4 continue via plan routing (VM)
|
||||
|
||||
source "$(dirname "$0")/../../../lib/test_runner.sh"
|
||||
require_env || exit 2
|
||||
|
||||
FIXTURE="$NYASH_ROOT/apps/tests/phase29ap_pattern4_continue_min.hako"
|
||||
|
||||
output=$(run_nyash_vm "$FIXTURE")
|
||||
|
||||
expected=$(cat << 'TXT'
|
||||
6
|
||||
TXT
|
||||
)
|
||||
|
||||
compare_outputs "$expected" "$output" "phase29ap_pattern4_continue_min_vm" || exit 1
|
||||
Reference in New Issue
Block a user