refactor(joinir): Phase 108 unify Pattern2 policy routing
This commit is contained in:
@ -57,6 +57,7 @@ pub(in crate::mir::builder) mod common_init;
|
||||
pub(in crate::mir::builder) mod loop_true_counter_extractor; // Phase 104: loop(true) counter extraction for Pattern2
|
||||
pub(in crate::mir::builder) mod read_digits_break_condition_box; // Phase 104: break cond normalization for read_digits(loop(true))
|
||||
pub(in crate::mir::builder) mod pattern2_break_condition_policy_router; // Phase 105: policy router box for Pattern2 break condition
|
||||
pub(in crate::mir::builder) mod pattern2_policy_router; // Phase 108: unified Pattern2 policy router (balanced/read_digits/default)
|
||||
pub(in crate::mir::builder) mod pattern2_inputs_facts_box; // Phase 105: Pattern2 input facts (analysis only)
|
||||
pub(in crate::mir::builder) mod pattern2_lowering_orchestrator; // Phase 105: Pattern2 orchestration (wiring/emission)
|
||||
pub(in crate::mir::builder) mod pattern2_steps; // Phase 106: Pattern2 step boxes (pipeline SSOT)
|
||||
|
||||
@ -94,7 +94,7 @@ pub(in crate::mir::builder) struct Pattern2Inputs {
|
||||
pub carrier_updates_override: Option<std::collections::BTreeMap<String, UpdateExpr>>,
|
||||
/// Phase 107: Post-loop early return plan for return-in-loop normalization.
|
||||
pub post_loop_early_return: Option<
|
||||
crate::mir::builder::control_flow::joinir::patterns::policies::balanced_depth_scan_policy::PostLoopEarlyReturnPlan,
|
||||
crate::mir::builder::control_flow::joinir::patterns::policies::post_loop_early_return_plan::PostLoopEarlyReturnPlan,
|
||||
>,
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
//! Pattern2 policy router (Phase 108)
|
||||
//!
|
||||
//! Responsibility (SSOT):
|
||||
//! - Decide which Pattern2 policy route applies (balanced depth-scan / loop(true) read-digits / default).
|
||||
//! - Normalize the outputs into a single routing struct consumed by ApplyPolicyStepBox.
|
||||
//!
|
||||
//! NOTE: This box does not emit JoinIR. It only provides "what to do" facts.
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlotBox;
|
||||
|
||||
use super::pattern2_break_condition_policy_router::Pattern2BreakConditionPolicyRouterBox;
|
||||
use super::pattern2_inputs_facts_box::BodyLocalHandlingPolicy;
|
||||
use super::policies::balanced_depth_scan_policy_box::BalancedDepthScanPolicyBox;
|
||||
use super::policies::PolicyDecision;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pattern2PolicyRouting {
|
||||
pub allowed_body_locals_for_conditions: Vec<String>,
|
||||
pub body_local_handling: BodyLocalHandlingPolicy,
|
||||
pub read_only_body_local_slot: Option<crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlot>,
|
||||
pub break_condition_node: ASTNode,
|
||||
pub is_loop_true_read_digits: bool,
|
||||
pub balanced_depth_scan_recipe:
|
||||
Option<crate::mir::join_ir::lowering::common::balanced_depth_scan_emitter::BalancedDepthScanRecipe>,
|
||||
pub carrier_updates_override: Option<
|
||||
std::collections::BTreeMap<String, crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr>,
|
||||
>,
|
||||
pub post_loop_early_return: Option<
|
||||
crate::mir::builder::control_flow::joinir::patterns::policies::post_loop_early_return_plan::PostLoopEarlyReturnPlan,
|
||||
>,
|
||||
}
|
||||
|
||||
pub(crate) struct Pattern2PolicyRouterBox;
|
||||
|
||||
impl Pattern2PolicyRouterBox {
|
||||
pub(crate) fn route(condition: &ASTNode, body: &[ASTNode]) -> Result<Pattern2PolicyRouting, String> {
|
||||
// Route 1 (Phase 107): balanced depth-scan (return-in-loop normalization).
|
||||
match BalancedDepthScanPolicyBox::decide(condition, body) {
|
||||
PolicyDecision::Use(result) => {
|
||||
return Ok(Pattern2PolicyRouting {
|
||||
allowed_body_locals_for_conditions: result.allowed_body_locals_for_conditions,
|
||||
body_local_handling: BodyLocalHandlingPolicy::SkipPromotion,
|
||||
read_only_body_local_slot: None,
|
||||
break_condition_node: result.break_condition_node,
|
||||
is_loop_true_read_digits: false,
|
||||
balanced_depth_scan_recipe: Some(result.derived_recipe),
|
||||
carrier_updates_override: Some(result.carrier_updates_override),
|
||||
post_loop_early_return: Some(result.post_loop_early_return),
|
||||
});
|
||||
}
|
||||
PolicyDecision::Reject(reason) => return Err(reason),
|
||||
PolicyDecision::None => {}
|
||||
}
|
||||
|
||||
// Route 2 (Phase 105): loop(true) read-digits family + default break-cond SSOT.
|
||||
let break_routing = Pattern2BreakConditionPolicyRouterBox::route(condition, body)?;
|
||||
|
||||
let read_only_body_local_slot = if break_routing.allowed_body_locals_for_conditions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ReadOnlyBodyLocalSlotBox::extract_single(
|
||||
&break_routing.allowed_body_locals_for_conditions,
|
||||
body,
|
||||
)?)
|
||||
};
|
||||
|
||||
Ok(Pattern2PolicyRouting {
|
||||
allowed_body_locals_for_conditions: break_routing.allowed_body_locals_for_conditions,
|
||||
body_local_handling: BodyLocalHandlingPolicy::DefaultPromotion,
|
||||
read_only_body_local_slot,
|
||||
break_condition_node: break_routing.break_condition_node,
|
||||
is_loop_true_read_digits: break_routing.is_loop_true_read_digits,
|
||||
balanced_depth_scan_recipe: None,
|
||||
carrier_updates_override: None,
|
||||
post_loop_early_return: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,60 +4,14 @@
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
use super::super::pattern2_break_condition_policy_router::Pattern2BreakConditionPolicyRouterBox;
|
||||
use super::super::pattern2_policy_router::Pattern2PolicyRouterBox;
|
||||
use super::super::pattern2_inputs_facts_box::{Pattern2Facts, Pattern2Inputs};
|
||||
|
||||
pub(crate) struct ApplyPolicyStepBox;
|
||||
|
||||
impl ApplyPolicyStepBox {
|
||||
pub(crate) fn apply(condition: &ASTNode, body: &[ASTNode], facts: Pattern2Facts) -> Result<Pattern2Inputs, String> {
|
||||
use super::super::policies::balanced_depth_scan_policy_box::BalancedDepthScanPolicyBox;
|
||||
use super::super::policies::PolicyDecision;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::pattern2_inputs_facts_box::BodyLocalHandlingPolicy;
|
||||
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlotBox;
|
||||
|
||||
// Phase 107: balanced depth-scan (return-in-loop) policy.
|
||||
// This route provides its own break-cond + derived recipe + post-loop early return plan.
|
||||
match BalancedDepthScanPolicyBox::decide(condition, body) {
|
||||
PolicyDecision::Use(result) => {
|
||||
return Ok(Pattern2Inputs {
|
||||
loop_var_name: facts.loop_var_name,
|
||||
loop_var_id: facts.loop_var_id,
|
||||
carrier_info: facts.carrier_info,
|
||||
scope: facts.scope,
|
||||
captured_env: facts.captured_env,
|
||||
join_value_space: facts.join_value_space,
|
||||
env: facts.env,
|
||||
condition_bindings: facts.condition_bindings,
|
||||
body_local_env: facts.body_local_env,
|
||||
allowed_body_locals_for_conditions: result.allowed_body_locals_for_conditions,
|
||||
body_local_handling: BodyLocalHandlingPolicy::SkipPromotion,
|
||||
read_only_body_local_slot: None,
|
||||
break_condition_node: result.break_condition_node,
|
||||
is_loop_true_read_digits: false,
|
||||
condition_only_recipe: None,
|
||||
body_local_derived_recipe: None,
|
||||
balanced_depth_scan_recipe: Some(result.derived_recipe),
|
||||
carrier_updates_override: Some(result.carrier_updates_override),
|
||||
post_loop_early_return: Some(result.post_loop_early_return),
|
||||
});
|
||||
}
|
||||
PolicyDecision::Reject(reason) => {
|
||||
return Err(reason);
|
||||
}
|
||||
PolicyDecision::None => {}
|
||||
}
|
||||
|
||||
let break_routing = Pattern2BreakConditionPolicyRouterBox::route(condition, body)?;
|
||||
|
||||
let read_only_body_local_slot = if break_routing.allowed_body_locals_for_conditions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ReadOnlyBodyLocalSlotBox::extract_single(
|
||||
&break_routing.allowed_body_locals_for_conditions,
|
||||
body,
|
||||
)?)
|
||||
};
|
||||
let policy = Pattern2PolicyRouterBox::route(condition, body)?;
|
||||
|
||||
Ok(Pattern2Inputs {
|
||||
loop_var_name: facts.loop_var_name,
|
||||
@ -69,16 +23,16 @@ impl ApplyPolicyStepBox {
|
||||
env: facts.env,
|
||||
condition_bindings: facts.condition_bindings,
|
||||
body_local_env: facts.body_local_env,
|
||||
allowed_body_locals_for_conditions: break_routing.allowed_body_locals_for_conditions,
|
||||
body_local_handling: BodyLocalHandlingPolicy::DefaultPromotion,
|
||||
read_only_body_local_slot,
|
||||
break_condition_node: break_routing.break_condition_node,
|
||||
is_loop_true_read_digits: break_routing.is_loop_true_read_digits,
|
||||
allowed_body_locals_for_conditions: policy.allowed_body_locals_for_conditions,
|
||||
body_local_handling: policy.body_local_handling,
|
||||
read_only_body_local_slot: policy.read_only_body_local_slot,
|
||||
break_condition_node: policy.break_condition_node,
|
||||
is_loop_true_read_digits: policy.is_loop_true_read_digits,
|
||||
condition_only_recipe: None,
|
||||
body_local_derived_recipe: None,
|
||||
balanced_depth_scan_recipe: None,
|
||||
carrier_updates_override: None,
|
||||
post_loop_early_return: None,
|
||||
balanced_depth_scan_recipe: policy.balanced_depth_scan_recipe,
|
||||
carrier_updates_override: policy.carrier_updates_override,
|
||||
post_loop_early_return: policy.post_loop_early_return,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
//! early-return guard. This keeps JoinIR lowering "break-only" while preserving
|
||||
//! source semantics for the recognized family.
|
||||
|
||||
use crate::ast::{ASTNode, BinaryOperator, Span};
|
||||
use crate::ast::{ASTNode, Span};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
pub(crate) struct PostLoopEarlyReturnStepBox;
|
||||
@ -13,35 +13,18 @@ impl PostLoopEarlyReturnStepBox {
|
||||
pub(crate) fn maybe_emit(
|
||||
builder: &mut MirBuilder,
|
||||
plan: Option<
|
||||
&crate::mir::builder::control_flow::joinir::patterns::policies::balanced_depth_scan_policy::PostLoopEarlyReturnPlan,
|
||||
&crate::mir::builder::control_flow::joinir::patterns::policies::post_loop_early_return_plan::PostLoopEarlyReturnPlan,
|
||||
>,
|
||||
) -> Result<(), String> {
|
||||
let Some(plan) = plan else { return Ok(()); };
|
||||
|
||||
// if i < n { return i }
|
||||
let cond = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: plan.loop_counter_name.clone(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Variable {
|
||||
name: plan.bound_name.clone(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let ret_stmt = ASTNode::Return {
|
||||
value: Some(Box::new(ASTNode::Variable {
|
||||
name: plan.loop_counter_name.clone(),
|
||||
span: Span::unknown(),
|
||||
})),
|
||||
value: Some(Box::new(plan.ret_expr.clone())),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
builder.build_statement(ASTNode::If {
|
||||
condition: Box::new(cond),
|
||||
condition: Box::new(plan.cond.clone()),
|
||||
then_body: vec![ret_stmt],
|
||||
else_body: None,
|
||||
span: Span::unknown(),
|
||||
|
||||
@ -12,14 +12,9 @@ use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}
|
||||
use crate::mir::join_ir::BinOpKind;
|
||||
|
||||
use super::PolicyDecision;
|
||||
use super::post_loop_early_return_plan::PostLoopEarlyReturnPlan;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PostLoopEarlyReturnPlan {
|
||||
pub loop_counter_name: String,
|
||||
pub bound_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct BalancedDepthScanPolicyResult {
|
||||
pub break_condition_node: ASTNode,
|
||||
@ -101,8 +96,13 @@ fn classify_balanced_depth_scan(
|
||||
depth_next_name,
|
||||
},
|
||||
post_loop_early_return: PostLoopEarlyReturnPlan {
|
||||
loop_counter_name,
|
||||
bound_name,
|
||||
cond: ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(var(&loop_counter_name)),
|
||||
right: Box::new(var(&bound_name)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ret_expr: var(&loop_counter_name),
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -618,8 +618,28 @@ mod tests {
|
||||
other => panic!("expected Use, got {:?}", other),
|
||||
};
|
||||
|
||||
assert_eq!(result.post_loop_early_return.loop_counter_name, "i");
|
||||
assert_eq!(result.post_loop_early_return.bound_name, "n");
|
||||
assert!(
|
||||
matches!(
|
||||
&result.post_loop_early_return.cond,
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} if matches!(left.as_ref(), ASTNode::Variable { name, .. } if name == "i")
|
||||
&& matches!(right.as_ref(), ASTNode::Variable { name, .. } if name == "n")
|
||||
),
|
||||
"post-loop cond must be `i < n`, got {:?}",
|
||||
result.post_loop_early_return.cond
|
||||
);
|
||||
assert!(
|
||||
matches!(
|
||||
&result.post_loop_early_return.ret_expr,
|
||||
ASTNode::Variable { name, .. } if name == "i"
|
||||
),
|
||||
"post-loop ret_expr must be `i`, got {:?}",
|
||||
result.post_loop_early_return.ret_expr
|
||||
);
|
||||
assert!(result.allowed_body_locals_for_conditions.contains(&"ch".to_string()));
|
||||
assert!(result.allowed_body_locals_for_conditions.contains(&"depth_next".to_string()));
|
||||
assert!(result.carrier_updates_override.contains_key("i"));
|
||||
|
||||
@ -35,3 +35,4 @@ pub(in crate::mir::builder) mod trim_policy;
|
||||
pub(in crate::mir::builder) mod loop_true_read_digits_policy;
|
||||
pub(in crate::mir::builder) mod balanced_depth_scan_policy;
|
||||
pub(in crate::mir::builder) mod balanced_depth_scan_policy_box;
|
||||
pub(in crate::mir::builder) mod post_loop_early_return_plan;
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
//! Post-loop early return plan (policy-level SSOT)
|
||||
//!
|
||||
//! Responsibility:
|
||||
//! - Describe a post-loop guard that emulates an in-loop `return` without making
|
||||
//! Pattern2 lowering itself return-in-loop aware.
|
||||
//! - Keep the plan policy-agnostic so multiple Pattern2 families can reuse it.
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PostLoopEarlyReturnPlan {
|
||||
pub cond: ASTNode,
|
||||
pub ret_expr: ASTNode,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user