diff --git a/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs b/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs index 09f7e5b5..73ab7f20 100644 --- a/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs +++ b/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs @@ -89,10 +89,12 @@ impl ExitMetaCollector { // Look up host slot from variable_map if let Some(&host_slot) = builder.variable_map.get(carrier_name) { + use crate::mir::join_ir::lowering::carrier_info::CarrierRole; let binding = LoopExitBinding { carrier_name: carrier_name.clone(), join_exit_value: *join_exit_value, host_slot, + role: CarrierRole::LoopState, // Phase 227: Default to LoopState (caller should update if needed) }; eprintln!( diff --git a/src/mir/builder/control_flow/joinir/merge/expr_result_resolver.rs b/src/mir/builder/control_flow/joinir/merge/expr_result_resolver.rs index 4ef71612..21486eaa 100644 --- a/src/mir/builder/control_flow/joinir/merge/expr_result_resolver.rs +++ b/src/mir/builder/control_flow/joinir/merge/expr_result_resolver.rs @@ -134,6 +134,7 @@ mod tests { carrier_name: "sum".to_string(), join_exit_value: ValueId(18), host_slot: ValueId(5), + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }; let remapper = JoinIrIdRemapper::new(); diff --git a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs index dd43a618..fe4c7233 100644 --- a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs +++ b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs @@ -607,8 +607,19 @@ pub(super) fn merge_and_rewrite( // // For each carrier, use the header PHI dst instead of // the undefined exit binding value. + // + // Phase 227: Filter out ConditionOnly carriers from exit PHI if let Some(b) = boundary { for binding in &b.exit_bindings { + // Phase 227: Skip ConditionOnly carriers (they don't need exit PHI) + if binding.role == crate::mir::join_ir::lowering::carrier_info::CarrierRole::ConditionOnly { + eprintln!( + "[DEBUG-177] Phase 227: Skipping ConditionOnly carrier '{}' from exit PHI", + binding.carrier_name + ); + continue; + } + if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(&binding.carrier_name) { carrier_inputs.entry(binding.carrier_name.clone()) .or_insert_with(Vec::new) diff --git a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs index 891da2a6..2cf2bde7 100644 --- a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs +++ b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs @@ -82,6 +82,7 @@ impl LoopHeaderPhiBuilder { phi_dst: loop_var_phi_dst, entry_incoming: (entry_block, loop_var_init), latch_incoming: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Loop var is always LoopState }, ); // Phase 177-STRUCT-2: Record insertion order @@ -103,6 +104,7 @@ impl LoopHeaderPhiBuilder { phi_dst, entry_incoming: (entry_block, *init_value), latch_incoming: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default to LoopState (will be updated later if needed) }, ); // Phase 177-STRUCT-2: Record insertion order diff --git a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs index eed3449f..05bb6f10 100644 --- a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs +++ b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs @@ -11,6 +11,7 @@ //! - ExitLineReconnector: to update variable_map with final values use crate::mir::{BasicBlockId, ValueId}; +use crate::mir::join_ir::lowering::carrier_info::CarrierRole; use std::collections::BTreeMap; /// Information about loop header PHIs @@ -55,6 +56,9 @@ pub struct CarrierPhiEntry { /// Latch edge: (from_block, updated_value) - set after instruction rewrite pub latch_incoming: Option<(BasicBlockId, ValueId)>, + + /// Phase 227: Role of this carrier (LoopState or ConditionOnly) + pub role: CarrierRole, } impl LoopHeaderPhiInfo { @@ -152,6 +156,7 @@ mod tests { phi_dst: ValueId(100), entry_incoming: (BasicBlockId(1), ValueId(5)), latch_incoming: None, + role: CarrierRole::LoopState, }, ); diff --git a/src/mir/builder/control_flow/joinir/patterns/common_init.rs b/src/mir/builder/control_flow/joinir/patterns/common_init.rs index 05f59c1a..091f472d 100644 --- a/src/mir/builder/control_flow/joinir/patterns/common_init.rs +++ b/src/mir/builder/control_flow/joinir/patterns/common_init.rs @@ -162,6 +162,7 @@ impl CommonPatternInitializer { name: name.clone(), host_id: ValueId(0), // Dummy join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default }) } else { None diff --git a/src/mir/builder/control_flow/joinir/patterns/exit_binding.rs b/src/mir/builder/control_flow/joinir/patterns/exit_binding.rs index e47ef1c6..4f9274fb 100644 --- a/src/mir/builder/control_flow/joinir/patterns/exit_binding.rs +++ b/src/mir/builder/control_flow/joinir/patterns/exit_binding.rs @@ -142,6 +142,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -181,11 +182,13 @@ mod tests { name: "printed".to_string(), host_id: ValueId(11), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, CarrierVar { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, ], ); @@ -228,6 +231,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -256,6 +260,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -284,6 +289,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -315,6 +321,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); diff --git a/src/mir/builder/control_flow/joinir/patterns/exit_binding_applicator.rs b/src/mir/builder/control_flow/joinir/patterns/exit_binding_applicator.rs index 3437c1bb..27ef22b4 100644 --- a/src/mir/builder/control_flow/joinir/patterns/exit_binding_applicator.rs +++ b/src/mir/builder/control_flow/joinir/patterns/exit_binding_applicator.rs @@ -52,6 +52,7 @@ pub fn apply_exit_bindings_to_boundary( carrier_name: carrier.name.clone(), host_slot: post_loop_id, join_exit_value: join_exit_id, + role: carrier.role, // Phase 227: Propagate role from CarrierInfo }); join_outputs.push(join_exit_id); @@ -83,10 +84,12 @@ pub fn apply_exit_bindings_to_boundary( /// /// LoopExitBinding for the loop variable pub 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(), join_exit_value: carrier_info.loop_var_id, // Loop var maps to itself host_slot: carrier_info.loop_var_id, + role: CarrierRole::LoopState, // Phase 227: Loop var is always LoopState } } @@ -104,6 +107,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); diff --git a/src/mir/builder/control_flow/joinir/patterns/exit_binding_constructor.rs b/src/mir/builder/control_flow/joinir/patterns/exit_binding_constructor.rs index 8de56a38..aee8e7f7 100644 --- a/src/mir/builder/control_flow/joinir/patterns/exit_binding_constructor.rs +++ b/src/mir/builder/control_flow/joinir/patterns/exit_binding_constructor.rs @@ -40,6 +40,7 @@ pub fn build_loop_exit_bindings( carrier_name: carrier.name.clone(), join_exit_value: join_exit_id, host_slot: carrier.host_id, + role: carrier.role, // Phase 227: Propagate role from CarrierInfo }); // Allocate new ValueId for post-loop carrier value @@ -90,6 +91,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -127,11 +129,13 @@ mod tests { name: "printed".to_string(), host_id: ValueId(11), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, CarrierVar { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, ], ); diff --git a/src/mir/builder/control_flow/joinir/patterns/exit_binding_validator.rs b/src/mir/builder/control_flow/joinir/patterns/exit_binding_validator.rs index f93b3a04..42b18b8b 100644 --- a/src/mir/builder/control_flow/joinir/patterns/exit_binding_validator.rs +++ b/src/mir/builder/control_flow/joinir/patterns/exit_binding_validator.rs @@ -69,6 +69,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -88,11 +89,13 @@ mod tests { name: "printed".to_string(), host_id: ValueId(11), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, CarrierVar { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, ], ); @@ -115,6 +118,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -135,6 +139,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); @@ -155,6 +160,7 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }], ); diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs index a506016f..cc648adb 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs @@ -475,9 +475,13 @@ impl MirBuilder { // Issue: CommonPatternInitializer includes all variables in variable_map as carriers, // but only variables with updates in the loop body are true carriers. // Condition-only variables (like 'len', 's') should be excluded. + // + // Phase 227: Keep ConditionOnly carriers even if they don't have updates + // (they're used in loop/break conditions, not updated) let original_carrier_count = carrier_info.carriers.len(); carrier_info.carriers.retain(|carrier| { - carrier_updates.contains_key(&carrier.name) + use crate::mir::join_ir::lowering::carrier_info::CarrierRole; + carrier_updates.contains_key(&carrier.name) || carrier.role == CarrierRole::ConditionOnly }); eprintln!( diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern4_carrier_analyzer.rs b/src/mir/builder/control_flow/joinir/patterns/pattern4_carrier_analyzer.rs index 0f574a69..9a6f5ea0 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern4_carrier_analyzer.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern4_carrier_analyzer.rs @@ -276,16 +276,19 @@ mod tests { name: "i".to_string(), host_id: ValueId(1), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, CarrierVar { name: "sum".to_string(), host_id: ValueId(2), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, CarrierVar { name: "M".to_string(), host_id: ValueId(3), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, ], trim_helper: None, diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs b/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs index b10a594d..9b3b4812 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs @@ -396,11 +396,13 @@ mod tests { name: "sum".to_string(), host_id: ValueId(10), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, CarrierVar { name: "count".to_string(), host_id: ValueId(11), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }, ], trim_helper: None, diff --git a/src/mir/join_ir/lowering/carrier_info.rs b/src/mir/join_ir/lowering/carrier_info.rs index 90b6ca50..2e8f8662 100644 --- a/src/mir/join_ir/lowering/carrier_info.rs +++ b/src/mir/join_ir/lowering/carrier_info.rs @@ -18,6 +18,42 @@ use crate::mir::ValueId; use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism +/// Phase 227: CarrierRole - Distinguishes loop state carriers from condition-only carriers +/// +/// When LoopBodyLocal variables are promoted to carriers, we need to know whether +/// they carry loop state (need exit PHI) or are only used in conditions (no exit PHI). +/// +/// # Example +/// +/// ```ignore +/// // LoopState carrier: sum needs exit PHI (value persists after loop) +/// loop(i < n) { +/// sum = sum + i; // sum updated in loop body +/// } +/// print(sum); // sum used after loop +/// +/// // ConditionOnly carrier: is_digit_pos does NOT need exit PHI +/// loop(p < s.length()) { +/// local digit_pos = digits.indexOf(s.substring(p, p+1)); +/// if digit_pos < 0 { break; } // Only used in condition +/// num_str = num_str + ch; +/// p = p + 1; +/// } +/// // digit_pos not used after loop +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CarrierRole { + /// Value needed after loop (sum, result, count, p, num_str) + /// - Participates in header PHI (loop iteration) + /// - Participates in exit PHI (final value after loop) + LoopState, + + /// Only used for loop condition (is_digit_pos, is_whitespace) + /// - Participates in header PHI (loop iteration) + /// - Does NOT participate in exit PHI (not needed after loop) + ConditionOnly, +} + /// Phase 224-D: Alias for promoted LoopBodyLocal in condition expressions /// /// When a LoopBodyLocal variable is promoted to a carrier, the original variable @@ -44,7 +80,7 @@ pub struct ConditionAlias { /// Information about a single carrier variable #[derive(Debug, Clone)] pub struct CarrierVar { - /// Variable name (e.g., "sum", "printed") + /// Variable name (e.g., "sum", "printed", "is_digit_pos") pub name: String, /// Host ValueId for this variable (MIR側) pub host_id: ValueId, @@ -56,6 +92,36 @@ pub struct CarrierVar { /// - `Some(vid)`: Header PHI生成後にセットされる /// - `None`: まだPHI生成前、または該当なし pub join_id: Option, + /// Phase 227: Role of this carrier (LoopState or ConditionOnly) + /// + /// - `LoopState`: Value needed after loop (participates in exit PHI) + /// - `ConditionOnly`: Only used for loop condition (no exit PHI) + pub role: CarrierRole, +} + +impl CarrierVar { + /// Create a new CarrierVar with default LoopState role + /// + /// This is the primary constructor for CarrierVar. Use this instead of + /// struct literal syntax to ensure role defaults to LoopState. + pub fn new(name: String, host_id: ValueId) -> Self { + Self { + name, + host_id, + join_id: None, + role: CarrierRole::LoopState, + } + } + + /// Create a CarrierVar with explicit role + pub fn with_role(name: String, host_id: ValueId, role: CarrierRole) -> Self { + Self { + name, + host_id, + join_id: None, + role, + } + } } /// Complete carrier information for a loop @@ -130,6 +196,7 @@ impl CarrierInfo { name: name.clone(), host_id: id, join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation + role: CarrierRole::LoopState, // Phase 227: Default to LoopState }) .collect(); @@ -189,6 +256,7 @@ impl CarrierInfo { name, host_id, join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation + role: CarrierRole::LoopState, // Phase 227: Default to LoopState }); } @@ -507,6 +575,7 @@ mod tests { name: name.to_string(), host_id: ValueId(id), join_id: None, // Phase 177-STRUCT-1 + role: CarrierRole::LoopState, // Phase 227: Default to LoopState } } diff --git a/src/mir/join_ir/lowering/carrier_update_emitter.rs b/src/mir/join_ir/lowering/carrier_update_emitter.rs index b168f208..d961e6c3 100644 --- a/src/mir/join_ir/lowering/carrier_update_emitter.rs +++ b/src/mir/join_ir/lowering/carrier_update_emitter.rs @@ -421,6 +421,7 @@ mod tests { name: name.to_string(), host_id: ValueId(host_id), join_id: None, // Phase 177-STRUCT-1 + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, } } diff --git a/src/mir/join_ir/lowering/inline_boundary.rs b/src/mir/join_ir/lowering/inline_boundary.rs index 1f29f21c..c9d8320b 100644 --- a/src/mir/join_ir/lowering/inline_boundary.rs +++ b/src/mir/join_ir/lowering/inline_boundary.rs @@ -43,6 +43,7 @@ //! ``` use crate::mir::ValueId; +use super::carrier_info::CarrierRole; /// Explicit binding between JoinIR exit value and host variable /// @@ -68,13 +69,13 @@ use crate::mir::ValueId; /// /// ```text /// vec![ -/// LoopExitBinding { carrier_name: "sum", join_exit_value: ValueId(18), host_slot: ValueId(5) }, -/// LoopExitBinding { carrier_name: "count", join_exit_value: ValueId(19), host_slot: ValueId(6) }, +/// LoopExitBinding { carrier_name: "sum", join_exit_value: ValueId(18), host_slot: ValueId(5), role: LoopState }, +/// LoopExitBinding { carrier_name: "count", join_exit_value: ValueId(19), host_slot: ValueId(6), role: LoopState }, /// ] /// ``` #[derive(Debug, Clone)] pub struct LoopExitBinding { - /// Carrier variable name (e.g., "sum", "count") + /// Carrier variable name (e.g., "sum", "count", "is_digit_pos") /// /// This is the variable name in the host's variable_map that should /// receive the exit value. @@ -91,6 +92,13 @@ pub struct LoopExitBinding { /// This is the host function's ValueId for the variable that should be /// updated with the exit PHI result. pub host_slot: ValueId, + + /// Phase 227: Role of this carrier (LoopState or ConditionOnly) + /// + /// Determines whether this carrier should participate in exit PHI: + /// - LoopState: Needs exit PHI (value used after loop) + /// - ConditionOnly: No exit PHI (only used in loop condition) + pub role: CarrierRole, } /// Boundary information for inlining a JoinIR fragment into a host function diff --git a/src/mir/join_ir/lowering/inline_boundary_builder.rs b/src/mir/join_ir/lowering/inline_boundary_builder.rs index f49da08e..24a5ce5a 100644 --- a/src/mir/join_ir/lowering/inline_boundary_builder.rs +++ b/src/mir/join_ir/lowering/inline_boundary_builder.rs @@ -27,6 +27,7 @@ use crate::mir::ValueId; use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding}; use super::condition_to_joinir::ConditionBinding; +use super::carrier_info::CarrierRole; /// Role of a parameter in JoinIR lowering (Phase 200-A) /// @@ -299,6 +300,7 @@ mod tests { carrier_name: "sum".to_string(), join_exit_value: ValueId(18), host_slot: ValueId(5), + role: CarrierRole::LoopState, }; let boundary = JoinInlineBoundaryBuilder::new() @@ -346,6 +348,7 @@ mod tests { carrier_name: "sum".to_string(), join_exit_value: ValueId(18), host_slot: ValueId(101), + role: CarrierRole::LoopState, } ]) .with_loop_var_name(Some("i".to_string())) @@ -372,11 +375,13 @@ mod tests { carrier_name: "i".to_string(), join_exit_value: ValueId(11), host_slot: ValueId(100), + role: CarrierRole::LoopState, }, LoopExitBinding { carrier_name: "sum".to_string(), join_exit_value: ValueId(20), host_slot: ValueId(101), + role: CarrierRole::LoopState, } ]) .with_loop_var_name(Some("i".to_string())) diff --git a/src/mir/join_ir/lowering/loop_update_analyzer.rs b/src/mir/join_ir/lowering/loop_update_analyzer.rs index 91b83f58..a47be65d 100644 --- a/src/mir/join_ir/lowering/loop_update_analyzer.rs +++ b/src/mir/join_ir/lowering/loop_update_analyzer.rs @@ -298,6 +298,7 @@ mod tests { name: "count".to_string(), host_id: crate::mir::ValueId(0), join_id: None, // Phase 177-STRUCT-1 + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }]; let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers); @@ -355,6 +356,7 @@ mod tests { name: "result".to_string(), host_id: crate::mir::ValueId(0), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }]; let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers); @@ -413,6 +415,7 @@ mod tests { name: "result".to_string(), host_id: crate::mir::ValueId(0), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }]; let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers); @@ -471,6 +474,7 @@ mod tests { name: "result".to_string(), host_id: crate::mir::ValueId(0), join_id: None, + role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, }]; let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers); diff --git a/src/mir/join_ir/lowering/loop_with_break_minimal.rs b/src/mir/join_ir/lowering/loop_with_break_minimal.rs index 17d031a7..41eca883 100644 --- a/src/mir/join_ir/lowering/loop_with_break_minimal.rs +++ b/src/mir/join_ir/lowering/loop_with_break_minimal.rs @@ -377,6 +377,23 @@ pub(crate) fn lower_loop_with_break_minimal( for (idx, carrier) in carrier_info.carriers.iter().enumerate() { let carrier_name = &carrier.name; + // Phase 227: ConditionOnly carriers don't have update expressions + // They just pass through their current value unchanged + use crate::mir::join_ir::lowering::carrier_info::CarrierRole; + if carrier.role == CarrierRole::ConditionOnly { + // ConditionOnly carrier: just pass through the current value + // The carrier's ValueId from env is passed unchanged + let current_value = env.get(carrier_name).ok_or_else(|| { + format!("ConditionOnly carrier '{}' not found in env", carrier_name) + })?; + updated_carrier_values.push(current_value); + eprintln!( + "[loop/carrier_update] Phase 227: ConditionOnly carrier '{}' passthrough: {:?}", + carrier_name, current_value + ); + continue; + } + // Get the update expression for this carrier let update_expr = carrier_updates.get(carrier_name).ok_or_else(|| { format!( diff --git a/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs b/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs index 15e5561a..ac9c1216 100644 --- a/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs +++ b/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs @@ -183,11 +183,12 @@ impl DigitPosPromoter { // For DigitPos pattern, we add a NEW carrier (not replace loop_var) let carrier_name = format!("is_{}", var_in_cond); - use crate::mir::join_ir::lowering::carrier_info::CarrierVar; + use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole}; let promoted_carrier = CarrierVar { name: carrier_name.clone(), host_id: ValueId(0), // Placeholder (will be remapped) join_id: None, // Will be allocated later + role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only }; // Create CarrierInfo with a dummy loop_var_name (will be ignored during merge)