diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_inputs_facts_box.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_inputs_facts_box.rs index 1251c2c2..0606c106 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_inputs_facts_box.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_inputs_facts_box.rs @@ -53,6 +53,12 @@ pub(in crate::mir::builder) struct Pattern2Facts { pub body_local_env: LoopBodyLocalEnv, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum BodyLocalHandlingPolicy { + DefaultPromotion, + SkipPromotion, +} + pub(in crate::mir::builder) struct Pattern2Inputs { pub loop_var_name: String, pub loop_var_id: ValueId, @@ -66,6 +72,8 @@ pub(in crate::mir::builder) struct Pattern2Inputs { /// Phase 92 P3: Allow-list of LoopBodyLocal variable names permitted in conditions. /// This must stay minimal (1 variable) and is validated by ReadOnlyBodyLocalSlotBox. pub allowed_body_locals_for_conditions: Vec, + /// Phase 107: For some policy-routed families, Pattern2 must not run promotion/slot heuristics. + pub body_local_handling: BodyLocalHandlingPolicy, /// Phase 92 P3: Diagnostics / debug metadata for the allow-listed variable. pub read_only_body_local_slot: Option, /// Policy-routed "break when true" condition node. diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/apply_policy_step_box.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/apply_policy_step_box.rs index 2a8cf29f..eff90f3e 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/apply_policy_step_box.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/apply_policy_step_box.rs @@ -13,6 +13,7 @@ impl ApplyPolicyStepBox { pub(crate) fn apply(condition: &ASTNode, body: &[ASTNode], facts: Pattern2Facts) -> Result { 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. @@ -30,6 +31,7 @@ impl ApplyPolicyStepBox { 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, @@ -68,6 +70,7 @@ impl ApplyPolicyStepBox { 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, diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/promote_step_box.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/promote_step_box.rs index d5b79321..82f71265 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/promote_step_box.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_steps/promote_step_box.rs @@ -166,10 +166,13 @@ impl PromoteStepBox { .collect(); if cond_scope.has_loop_body_local() { - // Phase 107: balanced depth-scan policy already provides an allow-list + derived recipe. - // Do not re-run Pattern2 body-local promotion/slot heuristics here (they assume break-guard shapes). - if inputs.balanced_depth_scan_recipe.is_some() { - // no-op: LoopBodyLocalInitLowerer + BalancedDepthScanEmitter will populate the env. + // Policy-controlled: some families must not run promotion/slot heuristics here. + // Example: balanced depth-scan uses derived vars and doesn't have a break-guard node. + if matches!( + inputs.body_local_handling, + crate::mir::builder::control_flow::joinir::patterns::pattern2_inputs_facts_box::BodyLocalHandlingPolicy::SkipPromotion + ) { + // no-op: lowerers will populate LoopBodyLocalEnv via init/derived emission. } else if !inputs.is_loop_true_read_digits { match classify_for_pattern2( builder,