joinir: clean pattern visibility and refactor pattern2 pipeline
This commit is contained in:
@ -20,8 +20,8 @@
|
||||
//! - Delegates to specialized analyzers for break/continue logic
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::loop_pattern_detection::LoopFeatures;
|
||||
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
|
||||
use crate::mir::loop_pattern_detection::LoopFeatures;
|
||||
|
||||
/// Detect if a loop body contains continue statements
|
||||
///
|
||||
@ -37,7 +37,7 @@ use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakCondition
|
||||
///
|
||||
/// This is a simple recursive scan that doesn't handle nested loops perfectly,
|
||||
/// but is sufficient for initial pattern detection.
|
||||
pub fn detect_continue_in_body(body: &[ASTNode]) -> bool {
|
||||
pub(crate) fn detect_continue_in_body(body: &[ASTNode]) -> bool {
|
||||
for stmt in body {
|
||||
if has_continue_node(stmt) {
|
||||
return true;
|
||||
@ -55,7 +55,7 @@ pub fn detect_continue_in_body(body: &[ASTNode]) -> bool {
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` if at least one break statement is found in the body or nested structures
|
||||
pub fn detect_break_in_body(body: &[ASTNode]) -> bool {
|
||||
pub(crate) fn detect_break_in_body(body: &[ASTNode]) -> bool {
|
||||
for stmt in body {
|
||||
if has_break_node(stmt) {
|
||||
return true;
|
||||
@ -78,11 +78,7 @@ pub fn detect_break_in_body(body: &[ASTNode]) -> bool {
|
||||
/// # Returns
|
||||
///
|
||||
/// A LoopFeatures struct containing all detected structural characteristics
|
||||
pub fn extract_features(
|
||||
body: &[ASTNode],
|
||||
has_continue: bool,
|
||||
has_break: bool,
|
||||
) -> LoopFeatures {
|
||||
pub(crate) fn extract_features(body: &[ASTNode], has_continue: bool, has_break: bool) -> LoopFeatures {
|
||||
// Phase 212.5: Detect ANY if statement in loop body (structural detection)
|
||||
let has_if = detect_if_in_body(body);
|
||||
|
||||
@ -147,8 +143,12 @@ fn detect_if_else_phi_in_body(body: &[ASTNode]) -> bool {
|
||||
} = node
|
||||
{
|
||||
// Check if both branches have assignments
|
||||
let then_has_assign = then_body.iter().any(|n| matches!(n, ASTNode::Assignment { .. }));
|
||||
let else_has_assign = else_body.iter().any(|n| matches!(n, ASTNode::Assignment { .. }));
|
||||
let then_has_assign = then_body
|
||||
.iter()
|
||||
.any(|n| matches!(n, ASTNode::Assignment { .. }));
|
||||
let else_has_assign = else_body
|
||||
.iter()
|
||||
.any(|n| matches!(n, ASTNode::Assignment { .. }));
|
||||
if then_has_assign && else_has_assign {
|
||||
return true;
|
||||
}
|
||||
@ -193,7 +193,11 @@ fn count_carriers_in_body(body: &[ASTNode]) -> usize {
|
||||
}
|
||||
}
|
||||
// Return at least 1 if we have assignments, otherwise 0
|
||||
if count > 0 { 1 } else { 0 }
|
||||
if count > 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursive helper to check if AST node contains continue
|
||||
@ -248,7 +252,7 @@ fn has_break_node(node: &ASTNode) -> bool {
|
||||
/// # Returns
|
||||
///
|
||||
/// `true` if an `if ... else { break }` pattern is found
|
||||
pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
|
||||
pub(crate) fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
|
||||
BreakConditionAnalyzer::has_break_in_else_clause(body)
|
||||
}
|
||||
|
||||
@ -286,7 +290,7 @@ pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
|
||||
/// // <- Returns the "!(ch == " ")" condition (negated)
|
||||
/// }
|
||||
/// ```
|
||||
pub fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> {
|
||||
pub(crate) fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> {
|
||||
BreakConditionAnalyzer::extract_break_condition(body).ok()
|
||||
}
|
||||
|
||||
@ -296,13 +300,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_detect_continue_simple() {
|
||||
let continue_node = ASTNode::Continue { span: crate::ast::Span::unknown() };
|
||||
let continue_node = ASTNode::Continue {
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
assert!(has_continue_node(&continue_node));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_break_simple() {
|
||||
let break_node = ASTNode::Break { span: crate::ast::Span::unknown() };
|
||||
let break_node = ASTNode::Break {
|
||||
span: crate::ast::Span::unknown(),
|
||||
};
|
||||
assert!(has_break_node(&break_node));
|
||||
}
|
||||
|
||||
|
||||
@ -32,13 +32,13 @@
|
||||
//! This module is now a thin wrapper around `CarrierInfo::from_variable_map()`.
|
||||
//! The primary logic lives in `carrier_info.rs` for consistency across MIR and JoinIR contexts.
|
||||
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct CommonPatternInitializer;
|
||||
pub(crate) struct CommonPatternInitializer;
|
||||
|
||||
impl CommonPatternInitializer {
|
||||
/// Initialize pattern context: extract loop var, build CarrierInfo
|
||||
@ -84,28 +84,24 @@ impl CommonPatternInitializer {
|
||||
) -> Result<(String, ValueId, CarrierInfo), String> {
|
||||
// Step 1: Extract loop variable from condition
|
||||
let loop_var_name = builder.extract_loop_variable_from_condition(condition)?;
|
||||
let loop_var_id = variable_map
|
||||
.get(&loop_var_name)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"[common_init] Loop variable '{}' not found in variable_map",
|
||||
loop_var_name
|
||||
)
|
||||
})?;
|
||||
let loop_var_id = variable_map.get(&loop_var_name).copied().ok_or_else(|| {
|
||||
format!(
|
||||
"[common_init] Loop variable '{}' not found in variable_map",
|
||||
loop_var_name
|
||||
)
|
||||
})?;
|
||||
|
||||
// Phase 183-2: Delegate to CarrierInfo::from_variable_map for consistency
|
||||
// Phase 222.5-D: Direct BTreeMap usage (no conversion needed)
|
||||
|
||||
// Step 2: Use CarrierInfo::from_variable_map as primary initialization method
|
||||
let mut carrier_info = CarrierInfo::from_variable_map(
|
||||
loop_var_name.clone(),
|
||||
variable_map,
|
||||
)?;
|
||||
let mut carrier_info = CarrierInfo::from_variable_map(loop_var_name.clone(), variable_map)?;
|
||||
|
||||
// Step 3: Apply exclusions if provided (Pattern 2 specific)
|
||||
if let Some(excluded) = exclude_carriers {
|
||||
carrier_info.carriers.retain(|c| !excluded.contains(&c.name.as_str()));
|
||||
carrier_info
|
||||
.carriers
|
||||
.retain(|c| !excluded.contains(&c.name.as_str()));
|
||||
}
|
||||
|
||||
Ok((loop_var_name, loop_var_id, carrier_info))
|
||||
@ -150,28 +146,33 @@ impl CommonPatternInitializer {
|
||||
_loop_var_name: &str,
|
||||
_variable_map: &BTreeMap<String, ValueId>,
|
||||
) -> bool {
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr, UpdateRhs};
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{
|
||||
LoopUpdateAnalyzer, UpdateExpr, UpdateRhs,
|
||||
};
|
||||
|
||||
// Create dummy carriers from body assignment targets for analysis
|
||||
let dummy_carriers: Vec<CarrierVar> = body.iter().filter_map(|node| {
|
||||
match node {
|
||||
ASTNode::Assignment { target, .. } => {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
Some(CarrierVar {
|
||||
let dummy_carriers: Vec<CarrierVar> = body
|
||||
.iter()
|
||||
.filter_map(|node| {
|
||||
match node {
|
||||
ASTNode::Assignment { target, .. } => {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
Some(CarrierVar {
|
||||
name: name.clone(),
|
||||
host_id: ValueId(0), // Dummy
|
||||
join_id: None,
|
||||
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228: Default
|
||||
})
|
||||
} else {
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(body, &dummy_carriers);
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct ConditionEnvBuilder;
|
||||
pub(crate) struct ConditionEnvBuilder;
|
||||
|
||||
impl ConditionEnvBuilder {
|
||||
/// Phase 201: Build ConditionEnv using JoinValueSpace (disjoint ValueId regions)
|
||||
@ -65,10 +65,8 @@ impl ConditionEnvBuilder {
|
||||
space: &mut JoinValueSpace,
|
||||
) -> Result<(ConditionEnv, Vec<ConditionBinding>, ValueId), String> {
|
||||
// Extract all variables used in the condition (excluding loop parameter)
|
||||
let condition_var_names = extract_condition_variables(
|
||||
break_condition,
|
||||
&[loop_var_name.to_string()],
|
||||
);
|
||||
let condition_var_names =
|
||||
extract_condition_variables(break_condition, &[loop_var_name.to_string()]);
|
||||
|
||||
let mut env = ConditionEnv::new();
|
||||
let mut bindings = Vec::new();
|
||||
@ -79,16 +77,13 @@ impl ConditionEnvBuilder {
|
||||
|
||||
// For each condition variable, allocate JoinIR-local ValueId and build binding
|
||||
for var_name in &condition_var_names {
|
||||
let host_id = variable_map
|
||||
.get(var_name)
|
||||
.copied()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Condition variable '{}' not found in variable_map. \
|
||||
let host_id = variable_map.get(var_name).copied().ok_or_else(|| {
|
||||
format!(
|
||||
"Condition variable '{}' not found in variable_map. \
|
||||
Loop condition references undefined variable.",
|
||||
var_name
|
||||
)
|
||||
})?;
|
||||
var_name
|
||||
)
|
||||
})?;
|
||||
|
||||
// Phase 201: Allocate from Param region to avoid collision with locals
|
||||
let join_id = space.alloc_param();
|
||||
@ -108,7 +103,10 @@ impl ConditionEnvBuilder {
|
||||
///
|
||||
/// Uses JoinValueSpace to allocate the loop parameter ValueId.
|
||||
#[allow(dead_code)]
|
||||
pub fn build_loop_param_only_v2(loop_var_name: &str, space: &mut JoinValueSpace) -> (ConditionEnv, ValueId) {
|
||||
pub fn build_loop_param_only_v2(
|
||||
loop_var_name: &str,
|
||||
space: &mut JoinValueSpace,
|
||||
) -> (ConditionEnv, ValueId) {
|
||||
let mut env = ConditionEnv::new();
|
||||
let loop_var_join_id = space.alloc_param();
|
||||
env.insert(loop_var_name.to_string(), loop_var_join_id);
|
||||
@ -177,7 +175,10 @@ impl ConditionEnvBuilder {
|
||||
let debug = env::var("NYASH_CAPTURE_DEBUG").is_ok();
|
||||
|
||||
if debug {
|
||||
eprintln!("[capture/env_builder] Building ConditionEnv with {} captured vars", captured.vars.len());
|
||||
eprintln!(
|
||||
"[capture/env_builder] Building ConditionEnv with {} captured vars",
|
||||
captured.vars.len()
|
||||
);
|
||||
}
|
||||
|
||||
// Step 1: Build base ConditionEnv with loop params using v2 API (Phase 222.5-B)
|
||||
@ -197,17 +198,25 @@ impl ConditionEnvBuilder {
|
||||
};
|
||||
|
||||
// 2b: Add to boundary with Condition role
|
||||
boundary.add_param_with_role(&var.name, host_id, crate::mir::join_ir::lowering::inline_boundary_builder::ParamRole::Condition);
|
||||
boundary.add_param_with_role(
|
||||
&var.name,
|
||||
host_id,
|
||||
crate::mir::join_ir::lowering::inline_boundary_builder::ParamRole::Condition,
|
||||
);
|
||||
|
||||
// 2c: Get JoinIR ValueId from boundary
|
||||
let join_id = boundary.get_condition_binding(&var.name)
|
||||
let join_id = boundary
|
||||
.get_condition_binding(&var.name)
|
||||
.expect("captured var should be in boundary after add_param_with_role");
|
||||
|
||||
// 2d: Add to ConditionEnv.captured map
|
||||
env.captured.insert(var.name.clone(), join_id);
|
||||
|
||||
if debug {
|
||||
eprintln!("[capture/env_builder] Added captured var '{}': host={:?}, join={:?}", var.name, host_id, join_id);
|
||||
eprintln!(
|
||||
"[capture/env_builder] Added captured var '{}': host={:?}, join={:?}",
|
||||
var.name, host_id, join_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,7 +233,11 @@ impl ConditionEnvBuilder {
|
||||
|
||||
if debug {
|
||||
let param_count = env.iter().count();
|
||||
eprintln!("[capture/env_builder] Final ConditionEnv: {} params, {} captured", param_count, env.captured.len());
|
||||
eprintln!(
|
||||
"[capture/env_builder] Final ConditionEnv: {} params, {} captured",
|
||||
param_count,
|
||||
env.captured.len()
|
||||
);
|
||||
}
|
||||
|
||||
(env, loop_var_join_id)
|
||||
@ -351,9 +364,7 @@ mod tests {
|
||||
|
||||
// Should return error
|
||||
assert!(result.is_err());
|
||||
assert!(result
|
||||
.unwrap_err()
|
||||
.contains("undefined_var"));
|
||||
assert!(result.unwrap_err().contains("undefined_var"));
|
||||
}
|
||||
|
||||
/// Phase 201: Test that v2 API uses JoinValueSpace correctly
|
||||
@ -410,7 +421,8 @@ mod tests {
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
|
||||
let mut space = JoinValueSpace::new();
|
||||
let (env, loop_var_join_id) = ConditionEnvBuilder::build_loop_param_only_v2("i", &mut space);
|
||||
let (env, loop_var_join_id) =
|
||||
ConditionEnvBuilder::build_loop_param_only_v2("i", &mut space);
|
||||
|
||||
// Phase 201: Should use Param region
|
||||
assert_eq!(loop_var_join_id, ValueId(100));
|
||||
|
||||
@ -3,11 +3,9 @@
|
||||
//! Applies exit bindings to JoinInlineBoundary.
|
||||
//! Single-responsibility box for boundary application logic.
|
||||
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::{
|
||||
JoinInlineBoundary, LoopExitBinding,
|
||||
};
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
/// Apply bindings to JoinInlineBoundary
|
||||
@ -27,7 +25,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
|
||||
/// # Returns
|
||||
///
|
||||
/// Success or error if boundary cannot be updated
|
||||
pub fn apply_exit_bindings_to_boundary(
|
||||
pub(crate) fn apply_exit_bindings_to_boundary(
|
||||
carrier_info: &CarrierInfo,
|
||||
exit_meta: &ExitMeta,
|
||||
variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
@ -40,13 +38,14 @@ pub fn apply_exit_bindings_to_boundary(
|
||||
let mut join_outputs = vec![carrier_info.loop_var_id]; // legacy field for compatibility
|
||||
|
||||
for carrier in &carrier_info.carriers {
|
||||
let post_loop_id = variable_map.get(&carrier.name).copied().ok_or_else(|| {
|
||||
format!("Post-loop ValueId not found for carrier '{}'", carrier.name)
|
||||
})?;
|
||||
let post_loop_id = variable_map
|
||||
.get(&carrier.name)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("Post-loop ValueId not found for carrier '{}'", carrier.name))?;
|
||||
|
||||
let join_exit_id = exit_meta.find_binding(&carrier.name).ok_or_else(|| {
|
||||
format!("Exit value not found for carrier '{}'", carrier.name)
|
||||
})?;
|
||||
let join_exit_id = exit_meta
|
||||
.find_binding(&carrier.name)
|
||||
.ok_or_else(|| format!("Exit value not found for carrier '{}'", carrier.name))?;
|
||||
|
||||
bindings.push(LoopExitBinding {
|
||||
carrier_name: carrier.name.clone(),
|
||||
@ -83,7 +82,7 @@ pub fn apply_exit_bindings_to_boundary(
|
||||
/// # Returns
|
||||
///
|
||||
/// LoopExitBinding for the loop variable
|
||||
pub fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
|
||||
pub(crate) fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||
LoopExitBinding {
|
||||
carrier_name: carrier_info.loop_var_name.clone(),
|
||||
@ -126,16 +125,16 @@ mod tests {
|
||||
let mut boundary = JoinInlineBoundary {
|
||||
host_inputs: vec![],
|
||||
join_inputs: vec![],
|
||||
exit_bindings: vec![], // Phase 171: Add missing field
|
||||
exit_bindings: vec![], // Phase 171: Add missing field
|
||||
#[allow(deprecated)]
|
||||
host_outputs: vec![], // legacy, unused in new assertions
|
||||
host_outputs: vec![], // legacy, unused in new assertions
|
||||
join_outputs: vec![],
|
||||
#[allow(deprecated)]
|
||||
condition_inputs: vec![], // Phase 171: Add missing field
|
||||
condition_bindings: vec![], // Phase 171-fix: Add missing field
|
||||
expr_result: None, // Phase 33-14: Add missing field
|
||||
loop_var_name: None, // Phase 33-16: Add missing field
|
||||
carrier_info: None, // Phase 228: Add missing field
|
||||
condition_inputs: vec![], // Phase 171: Add missing field
|
||||
condition_bindings: vec![], // Phase 171-fix: Add missing field
|
||||
expr_result: None, // Phase 33-14: Add missing field
|
||||
loop_var_name: None, // Phase 33-16: Add missing field
|
||||
carrier_info: None, // Phase 228: Add missing field
|
||||
};
|
||||
|
||||
apply_exit_bindings_to_boundary(&carrier_info, &exit_meta, &variable_map, &mut boundary)
|
||||
@ -155,11 +154,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_loop_var_exit_binding() {
|
||||
let carrier_info = CarrierInfo::with_carriers(
|
||||
"i".to_string(),
|
||||
ValueId(5),
|
||||
vec![],
|
||||
);
|
||||
let carrier_info = CarrierInfo::with_carriers("i".to_string(), ValueId(5), vec![]);
|
||||
|
||||
let binding = create_loop_var_exit_binding(&carrier_info);
|
||||
assert_eq!(binding.carrier_name, "i");
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
//! Constructs loop exit bindings and allocates post-loop ValueIds.
|
||||
//! Single-responsibility box for binding construction logic.
|
||||
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
/// Generate loop exit bindings
|
||||
@ -24,7 +24,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
|
||||
/// # Returns
|
||||
///
|
||||
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name
|
||||
pub fn build_loop_exit_bindings(
|
||||
pub(crate) fn build_loop_exit_bindings(
|
||||
carrier_info: &CarrierInfo,
|
||||
exit_meta: &ExitMeta,
|
||||
variable_map: &mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
@ -33,7 +33,8 @@ pub fn build_loop_exit_bindings(
|
||||
|
||||
// Process each carrier in sorted order
|
||||
for carrier in &carrier_info.carriers {
|
||||
let join_exit_id = exit_meta.find_binding(&carrier.name)
|
||||
let join_exit_id = exit_meta
|
||||
.find_binding(&carrier.name)
|
||||
.ok_or_else(|| format!("Carrier '{}' missing in ExitMeta", carrier.name))?;
|
||||
|
||||
bindings.push(LoopExitBinding {
|
||||
@ -64,12 +65,10 @@ pub fn build_loop_exit_bindings(
|
||||
/// # Returns
|
||||
///
|
||||
/// Newly allocated ValueId
|
||||
pub fn allocate_new_value_id(variable_map: &BTreeMap<String, ValueId>) -> ValueId { // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
pub(crate) fn allocate_new_value_id(variable_map: &BTreeMap<String, ValueId>) -> ValueId {
|
||||
// Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
// Find the maximum ValueId in current variable_map
|
||||
let max_id = variable_map.values()
|
||||
.map(|v| v.0)
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let max_id = variable_map.values().map(|v| v.0).max().unwrap_or(0);
|
||||
|
||||
// Allocate next sequential ID
|
||||
// Note: This is a temporary strategy and should be replaced with
|
||||
@ -131,14 +130,14 @@ mod tests {
|
||||
host_id: ValueId(11),
|
||||
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
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
},
|
||||
CarrierVar {
|
||||
name: "sum".to_string(),
|
||||
host_id: ValueId(10),
|
||||
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
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
@ -20,7 +20,7 @@ use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
|
||||
/// # Returns
|
||||
///
|
||||
/// Ok(()) if validation passes, Err with descriptive message if validation fails
|
||||
pub fn validate_exit_binding(
|
||||
pub(crate) fn validate_exit_binding(
|
||||
carrier_info: &CarrierInfo,
|
||||
exit_meta: &ExitMeta,
|
||||
) -> Result<(), String> {
|
||||
@ -44,10 +44,7 @@ pub fn validate_exit_binding(
|
||||
// Validate that all carriers in CarrierInfo have exit values
|
||||
for carrier in &carrier_info.carriers {
|
||||
if exit_meta.find_binding(&carrier.name).is_none() {
|
||||
return Err(format!(
|
||||
"Carrier '{}' missing in ExitMeta",
|
||||
carrier.name
|
||||
));
|
||||
return Err(format!("Carrier '{}' missing in ExitMeta", carrier.name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,14 +88,14 @@ mod tests {
|
||||
host_id: ValueId(11),
|
||||
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
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
},
|
||||
CarrierVar {
|
||||
name: "sum".to_string(),
|
||||
host_id: ValueId(10),
|
||||
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
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
//! Pattern 1: Simple While Loop minimal lowerer
|
||||
|
||||
use super::super::trace;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
use super::super::trace;
|
||||
|
||||
/// Phase 194: Detection function for Pattern 1
|
||||
///
|
||||
@ -11,7 +11,7 @@ use super::super::trace;
|
||||
///
|
||||
/// Pattern 1 matches:
|
||||
/// - Pattern kind is Pattern1SimpleWhile (no break, no continue, no if-else PHI)
|
||||
pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
ctx.pattern_kind == LoopPatternKind::Pattern1SimpleWhile
|
||||
}
|
||||
@ -19,7 +19,7 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
/// Phase 194: Lowering function for Pattern 1
|
||||
///
|
||||
/// Wrapper around cf_loop_pattern1_minimal to match router signature
|
||||
pub fn lower(
|
||||
pub(crate) fn lower(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &super::router::LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
@ -51,12 +51,7 @@ impl MirBuilder {
|
||||
|
||||
// Phase 179-B: Use PatternPipelineContext for unified preprocessing
|
||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||
let ctx = build_pattern_context(
|
||||
self,
|
||||
condition,
|
||||
body,
|
||||
PatternVariant::Pattern1,
|
||||
)?;
|
||||
let ctx = build_pattern_context(self, condition, body, PatternVariant::Pattern1)?;
|
||||
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().varmap("pattern1_start", &self.variable_map);
|
||||
@ -85,7 +80,7 @@ impl MirBuilder {
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable)
|
||||
vec![ctx.loop_var_id], // Host's loop variable
|
||||
)
|
||||
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||
.with_loop_var_name(Some(ctx.loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||
.build();
|
||||
|
||||
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow
|
||||
@ -116,7 +111,10 @@ impl MirBuilder {
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(self);
|
||||
|
||||
// Phase 195: Use unified trace
|
||||
trace::trace().debug("pattern1", &format!("Loop complete, returning Void {:?}", void_val));
|
||||
trace::trace().debug(
|
||||
"pattern1",
|
||||
&format!("Loop complete, returning Void {:?}", void_val),
|
||||
);
|
||||
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -15,11 +15,11 @@
|
||||
//! - Hardcoded loop condition (i <= 5), if condition (i % 2 == 1)
|
||||
//! - Kept for backward compatibility with existing tests
|
||||
|
||||
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
||||
use super::super::trace;
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
||||
use super::super::trace;
|
||||
|
||||
// Phase 213: Hardcoded ValueIds removed - now using ExitMeta-based exit binding generation
|
||||
// See: ExitMetaCollector usage below (lines 115-135)
|
||||
@ -32,7 +32,7 @@ use super::super::trace;
|
||||
/// - Pattern kind is Pattern3IfPhi (has if-else with PHI, no break/continue)
|
||||
///
|
||||
/// NOTE: Priority is now handled by pattern classification, not router order
|
||||
pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
ctx.pattern_kind == LoopPatternKind::Pattern3IfPhi
|
||||
}
|
||||
@ -40,7 +40,7 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
/// Phase 194: Lowering function for Pattern 3
|
||||
///
|
||||
/// Wrapper around cf_loop_pattern3_with_if_phi to match router signature
|
||||
pub fn lower(
|
||||
pub(crate) fn lower(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &super::router::LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
@ -72,22 +72,23 @@ impl MirBuilder {
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Phase 179-B: Use PatternPipelineContext for unified preprocessing
|
||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||
let ctx = build_pattern_context(
|
||||
self,
|
||||
condition,
|
||||
body,
|
||||
PatternVariant::Pattern3,
|
||||
)?;
|
||||
let ctx = build_pattern_context(self, condition, body, PatternVariant::Pattern3)?;
|
||||
|
||||
// Phase 213: AST-based if-sum pattern detection
|
||||
// Phase 242-EX-A: Legacy mode removed - all if-sum patterns now handled dynamically
|
||||
if !ctx.is_if_sum_pattern() {
|
||||
// Not an if-sum pattern → let router try other patterns or fall back
|
||||
trace::trace().debug("pattern3", "Not an if-sum pattern, returning None to try other patterns");
|
||||
trace::trace().debug(
|
||||
"pattern3",
|
||||
"Not an if-sum pattern, returning None to try other patterns",
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
trace::trace().debug("pattern3", "Detected if-sum pattern, using AST-based lowerer");
|
||||
trace::trace().debug(
|
||||
"pattern3",
|
||||
"Detected if-sum pattern, using AST-based lowerer",
|
||||
);
|
||||
self.lower_pattern3_if_sum(&ctx, condition, body, debug)
|
||||
}
|
||||
|
||||
@ -134,35 +135,34 @@ impl MirBuilder {
|
||||
for binding in &condition_bindings {
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!(" '{}': HOST {:?} → JoinIR {:?}", binding.name, binding.host_value, binding.join_value),
|
||||
&format!(
|
||||
" '{}': HOST {:?} → JoinIR {:?}",
|
||||
binding.name, binding.host_value, binding.join_value
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Call AST-based if-sum lowerer with ConditionEnv
|
||||
let (join_module, fragment_meta) = lower_if_sum_pattern(
|
||||
condition,
|
||||
if_stmt,
|
||||
body,
|
||||
&cond_env,
|
||||
&mut join_value_space,
|
||||
)?;
|
||||
let (join_module, fragment_meta) =
|
||||
lower_if_sum_pattern(condition, if_stmt, body, &cond_env, &mut join_value_space)?;
|
||||
|
||||
let exit_meta = &fragment_meta.exit_meta;
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!("ExitMeta: {} exit values", exit_meta.exit_values.len())
|
||||
&format!("ExitMeta: {} exit values", exit_meta.exit_values.len()),
|
||||
);
|
||||
for (carrier_name, join_value) in &exit_meta.exit_values {
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!(" {} → ValueId({})", carrier_name, join_value.0)
|
||||
&format!(" {} → ValueId({})", carrier_name, join_value.0),
|
||||
);
|
||||
}
|
||||
|
||||
// Build exit bindings using ExitMetaCollector
|
||||
// Phase 228-8: Pass carrier_info to include ConditionOnly carriers
|
||||
let exit_bindings = ExitMetaCollector::collect(self, exit_meta, Some(&ctx.carrier_info), debug);
|
||||
let exit_bindings =
|
||||
ExitMetaCollector::collect(self, exit_meta, Some(&ctx.carrier_info), debug);
|
||||
|
||||
// Build boundary with carrier inputs
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
@ -174,9 +174,7 @@ impl MirBuilder {
|
||||
|
||||
// Allocate join_inputs dynamically from JoinValueSpace
|
||||
// These ValueIds (0, 1, 2, ...) represent JoinIR function parameters
|
||||
let join_inputs: Vec<ValueId> = (0..total_inputs)
|
||||
.map(|i| ValueId(i as u32))
|
||||
.collect();
|
||||
let join_inputs: Vec<ValueId> = (0..total_inputs).map(|i| ValueId(i as u32)).collect();
|
||||
|
||||
// Build host_inputs: loop_var + exit_bindings (in order)
|
||||
let mut host_inputs = vec![ctx.loop_var_id];
|
||||
@ -197,15 +195,16 @@ impl MirBuilder {
|
||||
"pattern3/if-sum",
|
||||
&format!(
|
||||
"Boundary inputs: {} total (loop_var + {} exit bindings)",
|
||||
total_inputs, exit_bindings.len()
|
||||
)
|
||||
total_inputs,
|
||||
exit_bindings.len()
|
||||
),
|
||||
);
|
||||
|
||||
// Phase 215-2: Pass expr_result to boundary
|
||||
// Phase 220-D: Pass condition_bindings for variable remapping
|
||||
let mut boundary_builder = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs)
|
||||
.with_condition_bindings(condition_bindings) // Phase 220-D: Map condition-only vars
|
||||
.with_condition_bindings(condition_bindings) // Phase 220-D: Map condition-only vars
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(ctx.loop_var_name.clone()));
|
||||
|
||||
@ -213,7 +212,7 @@ impl MirBuilder {
|
||||
if let Some(expr_id) = fragment_meta.expr_result {
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!("Passing expr_result={:?} to boundary", expr_id)
|
||||
&format!("Passing expr_result={:?} to boundary", expr_id),
|
||||
);
|
||||
boundary_builder = boundary_builder.with_expr_result(Some(expr_id));
|
||||
}
|
||||
@ -234,7 +233,7 @@ impl MirBuilder {
|
||||
if let Some(expr_val) = merge_result {
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!("Loop complete, returning expr_result {:?}", expr_val)
|
||||
&format!("Loop complete, returning expr_result {:?}", expr_val),
|
||||
);
|
||||
Ok(Some(expr_val))
|
||||
} else {
|
||||
@ -243,7 +242,7 @@ impl MirBuilder {
|
||||
let void_val = constant::emit_void(self);
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!("Loop complete, returning Void {:?}", void_val)
|
||||
&format!("Loop complete, returning Void {:?}", void_val),
|
||||
);
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ use crate::mir::join_ir::lowering::continue_branch_normalizer::ContinueBranchNor
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
|
||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
pub struct Pattern4CarrierAnalyzer;
|
||||
pub(crate) struct Pattern4CarrierAnalyzer;
|
||||
|
||||
impl Pattern4CarrierAnalyzer {
|
||||
/// Analyze and filter carriers for continue pattern
|
||||
@ -54,10 +54,8 @@ impl Pattern4CarrierAnalyzer {
|
||||
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,
|
||||
);
|
||||
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
|
||||
@ -87,7 +85,8 @@ impl Pattern4CarrierAnalyzer {
|
||||
pub fn analyze_carrier_updates(
|
||||
loop_body: &[ASTNode],
|
||||
carriers: &[CarrierVar],
|
||||
) -> BTreeMap<String, UpdateExpr> { // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
) -> BTreeMap<String, UpdateExpr> {
|
||||
// Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers)
|
||||
}
|
||||
|
||||
@ -155,9 +154,9 @@ impl Pattern4CarrierAnalyzer {
|
||||
..
|
||||
} => {
|
||||
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))
|
||||
})
|
||||
|| 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,
|
||||
@ -331,9 +330,7 @@ mod tests {
|
||||
}),
|
||||
span: span.clone(),
|
||||
}],
|
||||
else_body: Some(vec![ASTNode::Continue {
|
||||
span: span.clone(),
|
||||
}]),
|
||||
else_body: Some(vec![ASTNode::Continue { span: span.clone() }]),
|
||||
span: span.clone(),
|
||||
}];
|
||||
|
||||
|
||||
@ -31,12 +31,12 @@
|
||||
//! - **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::ValueId;
|
||||
use super::super::trace;
|
||||
use crate::mir::loop_pattern_detection::error_messages;
|
||||
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 194+: Detection function for Pattern 4
|
||||
@ -62,9 +62,9 @@ use std::collections::BTreeMap;
|
||||
/// 3. **Phase 178**: No string/complex carrier updates (JoinIR doesn't support string concat)
|
||||
///
|
||||
/// If all conditions are met, Pattern 4 is detected.
|
||||
pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
use super::common_init::CommonPatternInitializer;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
|
||||
// Basic pattern check
|
||||
if ctx.pattern_kind != LoopPatternKind::Pattern4Continue {
|
||||
@ -97,17 +97,12 @@ pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
/// 4. Create JoinInlineBoundary for input/output mapping
|
||||
/// 5. Merge MIR blocks into current_function
|
||||
/// 6. Return loop result (first carrier value)
|
||||
pub fn lower(
|
||||
pub(crate) fn lower(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &super::router::LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Phase 33-19: Connect stub to actual implementation
|
||||
builder.cf_loop_pattern4_with_continue(
|
||||
ctx.condition,
|
||||
ctx.body,
|
||||
ctx.func_name,
|
||||
ctx.debug,
|
||||
)
|
||||
builder.cf_loop_pattern4_with_continue(ctx.condition, ctx.body, ctx.func_name, ctx.debug)
|
||||
}
|
||||
|
||||
impl MirBuilder {
|
||||
@ -170,10 +165,10 @@ fn prepare_pattern4_context(
|
||||
) -> Result<Pattern4Prepared, String> {
|
||||
use super::pattern4_carrier_analyzer::Pattern4CarrierAnalyzer;
|
||||
use super::pattern_pipeline::{build_pattern_context, PatternVariant};
|
||||
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
|
||||
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
|
||||
LoopBodyCondPromoter, ConditionPromotionRequest, ConditionPromotionResult,
|
||||
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);
|
||||
@ -195,28 +190,33 @@ fn prepare_pattern4_context(
|
||||
&normalized_body,
|
||||
&carrier_info_prelim.carriers,
|
||||
);
|
||||
let mut carrier_info = Pattern4CarrierAnalyzer::analyze_carriers(
|
||||
&normalized_body,
|
||||
&carrier_info_prelim,
|
||||
)?;
|
||||
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<_>>()
|
||||
)
|
||||
carrier_info
|
||||
.carriers
|
||||
.iter()
|
||||
.map(|c| &c.name)
|
||||
.collect::<Vec<_>>()
|
||||
),
|
||||
);
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("Analyzed {} carrier update expressions", carrier_updates.len())
|
||||
&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)
|
||||
&format!(" {} → {:?}", carrier_name, update_expr),
|
||||
);
|
||||
}
|
||||
|
||||
@ -228,18 +228,15 @@ fn prepare_pattern4_context(
|
||||
vec![condition]
|
||||
};
|
||||
|
||||
let cond_scope = LoopConditionScopeBox::analyze(
|
||||
&loop_var_name,
|
||||
&conditions_to_analyze,
|
||||
Some(&loop_scope),
|
||||
);
|
||||
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
|
||||
break_cond: None, // Pattern 4 has no break
|
||||
continue_cond,
|
||||
loop_body: &normalized_body,
|
||||
};
|
||||
@ -285,13 +282,15 @@ fn prepare_pattern4_context(
|
||||
} else {
|
||||
return Err(error_messages::format_error_pattern4_trim_not_safe(
|
||||
&helper.carrier_name,
|
||||
helper.whitespace_count()
|
||||
helper.whitespace_count(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
ConditionPromotionResult::CannotPromote { reason, vars } => {
|
||||
return Err(error_messages::format_error_pattern4_promotion_failed(&vars, &reason));
|
||||
return Err(error_messages::format_error_pattern4_promotion_failed(
|
||||
&vars, &reason,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,11 +310,11 @@ fn lower_pattern4_joinir(
|
||||
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 super::super::merge::exit_line::meta_collector::ExitMetaCollector;
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||
|
||||
trace::trace().varmap("pattern4_start", &builder.variable_map);
|
||||
|
||||
@ -338,12 +337,12 @@ fn lower_pattern4_joinir(
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("ExitMeta: {} exit bindings", exit_meta.exit_values.len())
|
||||
&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)
|
||||
&format!(" {} → ValueId({})", carrier_name, join_value.0),
|
||||
);
|
||||
}
|
||||
|
||||
@ -356,7 +355,9 @@ fn lower_pattern4_joinir(
|
||||
|
||||
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));
|
||||
return Err(error_messages::format_error_pattern4_carrier_not_found(
|
||||
&carrier.name,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,21 +369,27 @@ fn lower_pattern4_joinir(
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("host_inputs: {:?}", host_inputs.iter().map(|v| v.0).collect::<Vec<_>>())
|
||||
&format!(
|
||||
"host_inputs: {:?}",
|
||||
host_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
|
||||
),
|
||||
);
|
||||
|
||||
let mut join_inputs = vec![ValueId(0)]; // ValueId(0) = i_init in JoinIR
|
||||
let mut join_inputs = vec![ValueId(0)]; // ValueId(0) = i_init in JoinIR
|
||||
for idx in 0..prepared.carrier_info.carriers.len() {
|
||||
join_inputs.push(ValueId((idx + 1) as u32)); // ValueId(1..N) = carrier inits
|
||||
join_inputs.push(ValueId((idx + 1) as u32)); // ValueId(1..N) = carrier inits
|
||||
}
|
||||
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("join_inputs: {:?}", join_inputs.iter().map(|v| v.0).collect::<Vec<_>>())
|
||||
&format!(
|
||||
"join_inputs: {:?}",
|
||||
join_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
|
||||
),
|
||||
);
|
||||
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
|
||||
.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())
|
||||
@ -397,7 +404,10 @@ fn lower_pattern4_joinir(
|
||||
)?;
|
||||
|
||||
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
|
||||
trace::trace().debug("pattern4", &format!("Loop complete, returning Void {:?}", void_val));
|
||||
trace::trace().debug(
|
||||
"pattern4",
|
||||
&format!("Loop complete, returning Void {:?}", void_val),
|
||||
);
|
||||
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
|
||||
@ -34,14 +34,14 @@
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
||||
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
||||
use crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary; // Phase 213
|
||||
use crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary; // Phase 213
|
||||
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::BasicBlockId;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::{BTreeMap, BTreeSet}; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
use super::common_init::CommonPatternInitializer;
|
||||
@ -72,9 +72,8 @@ use super::loop_scope_shape_builder::LoopScopeShapeBuilder;
|
||||
/// let join_module = lower_simple_while_minimal(ctx.loop_scope)?;
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PatternPipelineContext {
|
||||
pub(crate) struct PatternPipelineContext {
|
||||
// === Common Data (All Patterns) ===
|
||||
|
||||
/// Loop variable name (e.g., "i")
|
||||
pub loop_var_name: String,
|
||||
|
||||
@ -88,7 +87,6 @@ pub struct PatternPipelineContext {
|
||||
pub loop_scope: LoopScopeShape,
|
||||
|
||||
// === Pattern 2/4: Break/Continue Condition ===
|
||||
|
||||
/// Condition environment (variable → JoinIR ValueId mapping)
|
||||
/// Used by Pattern 2 (break condition) and Pattern 4 (continue condition)
|
||||
#[allow(dead_code)]
|
||||
@ -105,21 +103,18 @@ pub struct PatternPipelineContext {
|
||||
pub carrier_updates: Option<BTreeMap<String, UpdateExpr>>, // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||
|
||||
// === Pattern 2/4: Trim Pattern Support ===
|
||||
|
||||
/// Trim loop helper (if Trim pattern detected during promotion)
|
||||
/// Used by Pattern 2 (string trim) - Pattern 4 support TBD
|
||||
#[allow(dead_code)]
|
||||
pub trim_helper: Option<TrimLoopHelper>,
|
||||
|
||||
// === Pattern 2: Break Condition ===
|
||||
|
||||
/// Effective break condition (may be modified for Trim pattern)
|
||||
/// Used only by Pattern 2
|
||||
#[allow(dead_code)]
|
||||
pub break_condition: Option<ASTNode>,
|
||||
|
||||
// === Pattern 3: If-Sum Generalization (Phase 213) ===
|
||||
|
||||
/// Loop condition AST node
|
||||
/// Used by Pattern 3 for dynamic loop condition lowering
|
||||
#[allow(dead_code)]
|
||||
@ -138,7 +133,7 @@ pub struct PatternPipelineContext {
|
||||
|
||||
/// Pattern variant selector
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PatternVariant {
|
||||
pub(crate) enum PatternVariant {
|
||||
/// Pattern 1: Simple while loop (no break, no continue, no if-else PHI)
|
||||
Pattern1,
|
||||
/// Pattern 2: Loop with break statement
|
||||
@ -193,7 +188,7 @@ impl PatternPipelineContext {
|
||||
// Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode
|
||||
if let Some(ASTNode::If { condition, .. }) = if_stmt {
|
||||
use crate::mir::join_ir::lowering::condition_pattern::{
|
||||
analyze_condition_pattern, normalize_comparison, ConditionPattern
|
||||
analyze_condition_pattern, normalize_comparison, ConditionPattern,
|
||||
};
|
||||
|
||||
// (a) Pattern check: must be SimpleComparison
|
||||
@ -213,7 +208,10 @@ impl PatternPipelineContext {
|
||||
// Phase 219: Use assignment-based carrier detection
|
||||
// (1 counter like "i" + 1-2 accumulators like "sum", "count")
|
||||
use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast;
|
||||
let carrier_names: Vec<String> = self.carrier_info.carriers.iter()
|
||||
let carrier_names: Vec<String> = self
|
||||
.carrier_info
|
||||
.carriers
|
||||
.iter()
|
||||
.map(|c| c.name.clone())
|
||||
.collect();
|
||||
|
||||
@ -232,9 +230,9 @@ impl PatternPipelineContext {
|
||||
///
|
||||
/// Returns the first if statement found in loop_body, if any.
|
||||
pub fn extract_if_statement(&self) -> Option<&ASTNode> {
|
||||
self.loop_body.as_ref().and_then(|body| {
|
||||
body.iter().find(|stmt| matches!(stmt, ASTNode::If { .. }))
|
||||
})
|
||||
self.loop_body
|
||||
.as_ref()
|
||||
.and_then(|body| body.iter().find(|stmt| matches!(stmt, ASTNode::If { .. })))
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,20 +261,19 @@ impl PatternPipelineContext {
|
||||
/// - Loop variable not found in variable_map
|
||||
/// - Condition variable not found (Pattern 2/4)
|
||||
/// - Trim pattern promotion fails (Pattern 2/4)
|
||||
pub fn build_pattern_context(
|
||||
pub(crate) fn build_pattern_context(
|
||||
builder: &mut MirBuilder,
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
variant: PatternVariant,
|
||||
) -> Result<PatternPipelineContext, String> {
|
||||
// Step 1: Common initialization (all patterns)
|
||||
let (loop_var_name, loop_var_id, carrier_info) =
|
||||
CommonPatternInitializer::initialize_pattern(
|
||||
builder,
|
||||
condition,
|
||||
&builder.variable_map,
|
||||
None, // No exclusions for now (Pattern 2/4 will filter carriers later)
|
||||
)?;
|
||||
let (loop_var_name, loop_var_id, carrier_info) = CommonPatternInitializer::initialize_pattern(
|
||||
builder,
|
||||
condition,
|
||||
&builder.variable_map,
|
||||
None, // No exclusions for now (Pattern 2/4 will filter carriers later)
|
||||
)?;
|
||||
|
||||
// Step 2: Build LoopScopeShape
|
||||
let loop_scope = match variant {
|
||||
@ -304,39 +301,46 @@ pub fn build_pattern_context(
|
||||
};
|
||||
|
||||
// Step 3: Pattern-specific preprocessing
|
||||
let (condition_env, condition_bindings, carrier_updates, trim_helper, break_condition,
|
||||
loop_condition, loop_body, loop_update_summary) =
|
||||
match variant {
|
||||
PatternVariant::Pattern1 => {
|
||||
// Pattern 1: No additional preprocessing needed
|
||||
(None, None, None, None, None, None, None, None)
|
||||
}
|
||||
PatternVariant::Pattern3 => {
|
||||
// Pattern 3: Phase 213 - Store loop condition and body for AST-based lowering
|
||||
(
|
||||
None, // No condition_env
|
||||
None, // No condition_bindings
|
||||
None, // No carrier_updates (old style)
|
||||
None, // No trim_helper
|
||||
None, // No break_condition
|
||||
Some(condition.clone()), // loop_condition (Phase 213)
|
||||
Some(body.to_vec()), // loop_body (Phase 213)
|
||||
None, // loop_update_summary (TODO: Phase 213-2-3)
|
||||
)
|
||||
}
|
||||
PatternVariant::Pattern2 | PatternVariant::Pattern4 => {
|
||||
// Pattern 2/4: Full preprocessing will be handled by existing code
|
||||
// For now, return empty values (will be populated by pattern-specific logic)
|
||||
//
|
||||
// Note: Pattern 2/4 have complex preprocessing that includes:
|
||||
// - Break/continue condition analysis
|
||||
// - Carrier update analysis
|
||||
// - Trim pattern promotion
|
||||
// These will remain in pattern2/pattern4.rs for now and will be
|
||||
// gradually migrated into this pipeline in future phases.
|
||||
(None, None, None, None, None, None, None, None)
|
||||
}
|
||||
};
|
||||
let (
|
||||
condition_env,
|
||||
condition_bindings,
|
||||
carrier_updates,
|
||||
trim_helper,
|
||||
break_condition,
|
||||
loop_condition,
|
||||
loop_body,
|
||||
loop_update_summary,
|
||||
) = match variant {
|
||||
PatternVariant::Pattern1 => {
|
||||
// Pattern 1: No additional preprocessing needed
|
||||
(None, None, None, None, None, None, None, None)
|
||||
}
|
||||
PatternVariant::Pattern3 => {
|
||||
// Pattern 3: Phase 213 - Store loop condition and body for AST-based lowering
|
||||
(
|
||||
None, // No condition_env
|
||||
None, // No condition_bindings
|
||||
None, // No carrier_updates (old style)
|
||||
None, // No trim_helper
|
||||
None, // No break_condition
|
||||
Some(condition.clone()), // loop_condition (Phase 213)
|
||||
Some(body.to_vec()), // loop_body (Phase 213)
|
||||
None, // loop_update_summary (TODO: Phase 213-2-3)
|
||||
)
|
||||
}
|
||||
PatternVariant::Pattern2 | PatternVariant::Pattern4 => {
|
||||
// Pattern 2/4: Full preprocessing will be handled by existing code
|
||||
// For now, return empty values (will be populated by pattern-specific logic)
|
||||
//
|
||||
// Note: Pattern 2/4 have complex preprocessing that includes:
|
||||
// - Break/continue condition analysis
|
||||
// - Carrier update analysis
|
||||
// - Trim pattern promotion
|
||||
// These will remain in pattern2/pattern4.rs for now and will be
|
||||
// gradually migrated into this pipeline in future phases.
|
||||
(None, None, None, None, None, None, None, None)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(PatternPipelineContext {
|
||||
loop_var_name,
|
||||
@ -348,9 +352,9 @@ pub fn build_pattern_context(
|
||||
carrier_updates,
|
||||
trim_helper,
|
||||
break_condition,
|
||||
loop_condition, // Phase 213
|
||||
loop_body, // Phase 213
|
||||
loop_update_summary, // Phase 213
|
||||
loop_condition, // Phase 213
|
||||
loop_body, // Phase 213
|
||||
loop_update_summary, // Phase 213
|
||||
})
|
||||
}
|
||||
|
||||
@ -398,14 +402,14 @@ mod tests {
|
||||
host_id: ValueId(10),
|
||||
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
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
},
|
||||
CarrierVar {
|
||||
name: "count".to_string(),
|
||||
host_id: ValueId(11),
|
||||
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
|
||||
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
|
||||
},
|
||||
],
|
||||
trim_helper: None,
|
||||
@ -423,9 +427,9 @@ mod tests {
|
||||
carrier_updates: None,
|
||||
trim_helper: None,
|
||||
break_condition: None,
|
||||
loop_condition: None, // Phase 213
|
||||
loop_body: None, // Phase 213
|
||||
loop_update_summary: None, // Phase 213
|
||||
loop_condition: None, // Phase 213
|
||||
loop_body: None, // Phase 213
|
||||
loop_update_summary: None, // Phase 213
|
||||
};
|
||||
|
||||
assert_eq!(ctx.carrier_count(), 2);
|
||||
@ -466,9 +470,9 @@ mod tests {
|
||||
whitespace_chars: vec![" ".to_string(), "\t".to_string()],
|
||||
}),
|
||||
break_condition: None,
|
||||
loop_condition: None, // Phase 213
|
||||
loop_body: None, // Phase 213
|
||||
loop_update_summary: None, // Phase 213
|
||||
loop_condition: None, // Phase 213
|
||||
loop_body: None, // Phase 213
|
||||
loop_update_summary: None, // Phase 213
|
||||
};
|
||||
|
||||
assert!(ctx.is_trim_pattern());
|
||||
|
||||
@ -30,7 +30,7 @@ use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};
|
||||
use super::ast_feature_extractor as ast_features;
|
||||
|
||||
/// Context passed to pattern detect/lower functions
|
||||
pub struct LoopPatternContext<'a> {
|
||||
pub(crate) struct LoopPatternContext<'a> {
|
||||
/// Loop condition AST node
|
||||
pub condition: &'a ASTNode,
|
||||
|
||||
@ -69,7 +69,7 @@ impl<'a> LoopPatternContext<'a> {
|
||||
/// Phase 194+: Automatically detects continue/break statements in body
|
||||
/// Phase 192: Extract features and classify pattern from AST
|
||||
/// Phase 193: Feature extraction delegated to ast_feature_extractor module
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
condition: &'a ASTNode,
|
||||
body: &'a [ASTNode],
|
||||
func_name: &'a str,
|
||||
@ -99,7 +99,7 @@ impl<'a> LoopPatternContext<'a> {
|
||||
}
|
||||
|
||||
/// Phase 200-C: Create context with fn_body for capture analysis
|
||||
pub fn with_fn_body(
|
||||
pub(crate) fn with_fn_body(
|
||||
condition: &'a ASTNode,
|
||||
body: &'a [ASTNode],
|
||||
func_name: &'a str,
|
||||
@ -117,19 +117,19 @@ impl<'a> LoopPatternContext<'a> {
|
||||
|
||||
/// Entry in the loop pattern router table.
|
||||
/// Each pattern registers a detect function and a lower function.
|
||||
pub struct LoopPatternEntry {
|
||||
pub(crate) struct LoopPatternEntry {
|
||||
/// Human-readable pattern name for debugging
|
||||
pub name: &'static str,
|
||||
pub(crate) name: &'static str,
|
||||
|
||||
/// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30
|
||||
#[allow(dead_code)]
|
||||
pub priority: u8,
|
||||
pub(crate) priority: u8,
|
||||
|
||||
/// Detection function: returns true if this pattern matches
|
||||
pub detect: fn(&MirBuilder, &LoopPatternContext) -> bool,
|
||||
pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool,
|
||||
|
||||
/// Lowering function: performs the actual JoinIR generation
|
||||
pub lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>,
|
||||
pub(crate) lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>,
|
||||
}
|
||||
|
||||
/// Static table of all registered loop patterns.
|
||||
@ -156,16 +156,16 @@ pub struct LoopPatternEntry {
|
||||
/// - Structure: has_break && !has_continue
|
||||
///
|
||||
/// Note: func_name is now only used for debug logging, not pattern detection
|
||||
pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
LoopPatternEntry {
|
||||
name: "Pattern4_WithContinue",
|
||||
priority: 5, // Highest priority - continue is most specific
|
||||
priority: 5, // Highest priority - continue is most specific
|
||||
detect: super::pattern4_with_continue::can_lower,
|
||||
lower: super::pattern4_with_continue::lower,
|
||||
},
|
||||
LoopPatternEntry {
|
||||
name: "Pattern3_WithIfPhi",
|
||||
priority: 30, // NOTE: Pattern 3 must be checked BEFORE Pattern 1 (both use "main")
|
||||
priority: 30, // NOTE: Pattern 3 must be checked BEFORE Pattern 1 (both use "main")
|
||||
detect: super::pattern3_with_if_phi::can_lower,
|
||||
lower: super::pattern3_with_if_phi::lower,
|
||||
},
|
||||
@ -195,7 +195,7 @@ pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
/// - Pattern detection: `ctx.pattern_kind` (from `loop_pattern_detection::classify`)
|
||||
/// - No redundant pattern detection in detect functions
|
||||
/// - All patterns use structure-based classification
|
||||
pub fn route_loop_pattern(
|
||||
pub(crate) fn route_loop_pattern(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
@ -217,7 +217,13 @@ pub fn route_loop_pattern(
|
||||
// No pattern matched - return None (caller will handle error)
|
||||
// Phase 187-2: Legacy LoopBuilder removed, all loops must use JoinIR
|
||||
if ctx.debug {
|
||||
trace::trace().debug("route", &format!("No pattern matched for function '{}' (pattern_kind={:?})", ctx.func_name, ctx.pattern_kind));
|
||||
trace::trace().debug(
|
||||
"route",
|
||||
&format!(
|
||||
"No pattern matched for function '{}' (pattern_kind={:?})",
|
||||
ctx.func_name, ctx.pattern_kind
|
||||
),
|
||||
);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ use crate::mir::ValueId;
|
||||
/// Trim pattern lowering orchestrator
|
||||
///
|
||||
/// Phase 180: Single entry point for all Trim/P5 lowering operations.
|
||||
pub struct TrimLoopLowerer;
|
||||
pub(crate) struct TrimLoopLowerer;
|
||||
|
||||
/// Result of successful Trim lowering preprocessing
|
||||
///
|
||||
@ -59,7 +59,7 @@ pub struct TrimLoopLowerer;
|
||||
/// - Updated carrier info with promoted carrier
|
||||
/// - Condition environment bindings
|
||||
/// - Trim helper for pattern-specific operations
|
||||
pub struct TrimLoweringResult {
|
||||
pub(crate) struct TrimLoweringResult {
|
||||
/// Replaced break condition (e.g., `!is_carrier`)
|
||||
///
|
||||
/// Pattern2/4 will use this instead of the original break condition
|
||||
@ -98,12 +98,14 @@ impl TrimLoopLowerer {
|
||||
Self::is_var_used_in_condition(var_name, left)
|
||||
|| Self::is_var_used_in_condition(var_name, right)
|
||||
}
|
||||
ASTNode::UnaryOp { operand, .. } => {
|
||||
Self::is_var_used_in_condition(var_name, operand)
|
||||
}
|
||||
ASTNode::MethodCall { object, arguments, .. } => {
|
||||
ASTNode::UnaryOp { operand, .. } => Self::is_var_used_in_condition(var_name, operand),
|
||||
ASTNode::MethodCall {
|
||||
object, arguments, ..
|
||||
} => {
|
||||
Self::is_var_used_in_condition(var_name, object)
|
||||
|| arguments.iter().any(|arg| Self::is_var_used_in_condition(var_name, arg))
|
||||
|| arguments
|
||||
.iter()
|
||||
.any(|arg| Self::is_var_used_in_condition(var_name, arg))
|
||||
}
|
||||
// Add other node types as needed
|
||||
_ => false,
|
||||
@ -173,11 +175,8 @@ impl TrimLoopLowerer {
|
||||
// TODO: Phase 180-3 will implement full logic from Pattern2
|
||||
|
||||
// Step 1: Check if condition references LoopBodyLocal variables
|
||||
let cond_scope = LoopConditionScopeBox::analyze(
|
||||
loop_var_name,
|
||||
&[loop_cond, break_cond],
|
||||
Some(scope),
|
||||
);
|
||||
let cond_scope =
|
||||
LoopConditionScopeBox::analyze(loop_var_name, &[loop_cond, break_cond], Some(scope));
|
||||
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Analyzing condition scope: {} variables",
|
||||
@ -194,7 +193,9 @@ impl TrimLoopLowerer {
|
||||
|
||||
// Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only)
|
||||
use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope;
|
||||
let condition_body_locals: Vec<_> = cond_scope.vars.iter()
|
||||
let condition_body_locals: Vec<_> = cond_scope
|
||||
.vars
|
||||
.iter()
|
||||
.filter(|v| v.scope == CondVarScope::LoopBodyLocal)
|
||||
.filter(|v| {
|
||||
// Check if variable is actually used in break condition
|
||||
@ -213,7 +214,10 @@ impl TrimLoopLowerer {
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Phase 183: Found {} condition LoopBodyLocal variables: {:?}",
|
||||
condition_body_locals.len(),
|
||||
condition_body_locals.iter().map(|v| &v.name).collect::<Vec<_>>()
|
||||
condition_body_locals
|
||||
.iter()
|
||||
.map(|v| &v.name)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// Step 2: Try promotion via LoopBodyCarrierPromoter
|
||||
@ -242,14 +246,12 @@ impl TrimLoopLowerer {
|
||||
);
|
||||
|
||||
// Step 4: Safety check via TrimLoopHelper
|
||||
let trim_helper = carrier_info
|
||||
.trim_helper()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"[TrimLoopLowerer] Promoted but no TrimLoopHelper attached (carrier: '{}')",
|
||||
trim_info.carrier_name
|
||||
)
|
||||
})?;
|
||||
let trim_helper = carrier_info.trim_helper().ok_or_else(|| {
|
||||
format!(
|
||||
"[TrimLoopLowerer] Promoted but no TrimLoopHelper attached (carrier: '{}')",
|
||||
trim_info.carrier_name
|
||||
)
|
||||
})?;
|
||||
|
||||
if !trim_helper.is_safe_trim() {
|
||||
return Err(format!(
|
||||
@ -262,15 +264,13 @@ impl TrimLoopLowerer {
|
||||
eprintln!("[TrimLoopLowerer] Safe Trim pattern detected, implementing lowering");
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
|
||||
trim_helper.carrier_name, trim_helper.original_var, trim_helper.whitespace_chars
|
||||
trim_helper.carrier_name,
|
||||
trim_helper.original_var,
|
||||
trim_helper.whitespace_chars
|
||||
);
|
||||
|
||||
// Step 5: Generate carrier initialization code
|
||||
Self::generate_carrier_initialization(
|
||||
builder,
|
||||
body,
|
||||
trim_helper,
|
||||
)?;
|
||||
Self::generate_carrier_initialization(builder, body, trim_helper)?;
|
||||
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Registered carrier '{}' in variable_map",
|
||||
@ -286,11 +286,8 @@ impl TrimLoopLowerer {
|
||||
);
|
||||
|
||||
// Step 7: Setup ConditionEnv bindings
|
||||
let condition_bindings = Self::setup_condition_env_bindings(
|
||||
builder,
|
||||
trim_helper,
|
||||
alloc_join_value,
|
||||
)?;
|
||||
let condition_bindings =
|
||||
Self::setup_condition_env_bindings(builder, trim_helper, alloc_join_value)?;
|
||||
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Added {} condition bindings",
|
||||
@ -333,13 +330,14 @@ impl TrimLoopLowerer {
|
||||
use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_validator::TrimPatternValidator;
|
||||
|
||||
// Extract substring pattern from body
|
||||
let (s_name, start_expr) = TrimPatternValidator::extract_substring_args(body, &trim_helper.original_var)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
let (s_name, start_expr) =
|
||||
TrimPatternValidator::extract_substring_args(body, &trim_helper.original_var)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"[TrimLoopLowerer] Failed to extract substring pattern for Trim carrier '{}'",
|
||||
trim_helper.carrier_name
|
||||
)
|
||||
})?;
|
||||
})?;
|
||||
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Extracted substring pattern: s='{}', start={:?}",
|
||||
@ -347,11 +345,10 @@ impl TrimLoopLowerer {
|
||||
);
|
||||
|
||||
// Get ValueIds for string and start
|
||||
let s_id = builder
|
||||
.variable_map
|
||||
.get(&s_name)
|
||||
.copied()
|
||||
.ok_or_else(|| format!("[TrimLoopLowerer] String variable '{}' not found", s_name))?;
|
||||
let s_id =
|
||||
builder.variable_map.get(&s_name).copied().ok_or_else(|| {
|
||||
format!("[TrimLoopLowerer] String variable '{}' not found", s_name)
|
||||
})?;
|
||||
|
||||
// Compile start expression to get ValueId
|
||||
let start_id = builder.build_expression_impl(*start_expr)?;
|
||||
@ -378,11 +375,17 @@ impl TrimLoopLowerer {
|
||||
vec![start_id, start_plus_1],
|
||||
)?;
|
||||
|
||||
eprintln!("[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}", ch0);
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}",
|
||||
ch0
|
||||
);
|
||||
|
||||
// Generate: is_ch_match0 = (ch0 == " " || ch0 == "\t" || ...)
|
||||
let is_ch_match0 =
|
||||
TrimPatternValidator::emit_whitespace_check(builder, ch0, &trim_helper.whitespace_chars)?;
|
||||
let is_ch_match0 = TrimPatternValidator::emit_whitespace_check(
|
||||
builder,
|
||||
ch0,
|
||||
&trim_helper.whitespace_chars,
|
||||
)?;
|
||||
|
||||
eprintln!(
|
||||
"[TrimLoopLowerer] Generated initial whitespace check: is_ch_match0 = {:?}",
|
||||
@ -486,7 +489,9 @@ mod tests {
|
||||
};
|
||||
|
||||
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &var_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &var_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||
"other", &var_node
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -506,7 +511,9 @@ mod tests {
|
||||
};
|
||||
|
||||
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &cond_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &cond_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||
"other", &cond_node
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -525,8 +532,13 @@ mod tests {
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
assert!(TrimLoopLowerer::is_var_used_in_condition("digit_pos", &cond_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &cond_node));
|
||||
assert!(TrimLoopLowerer::is_var_used_in_condition(
|
||||
"digit_pos",
|
||||
&cond_node
|
||||
));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||
"other", &cond_node
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -566,6 +578,8 @@ mod tests {
|
||||
};
|
||||
|
||||
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &or_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &or_node));
|
||||
assert!(!TrimLoopLowerer::is_var_used_in_condition(
|
||||
"other", &or_node
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user