diff --git a/src/backend/mir_interpreter/handlers/boxes.rs b/src/backend/mir_interpreter/handlers/boxes.rs index 84f44699..0ff28bee 100644 --- a/src/backend/mir_interpreter/handlers/boxes.rs +++ b/src/backend/mir_interpreter/handlers/boxes.rs @@ -168,9 +168,7 @@ impl MirInterpreter { _ => {} } if super::boxes_object_fields::try_handle_object_fields(self, dst, box_val, method, args)? { - if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm-trace] length dispatch handler=object_fields"); - } + trace_dispatch!(method, "object_fields"); return Ok(()); } // Policy gate: user InstanceBox BoxCall runtime fallback @@ -201,36 +199,26 @@ impl MirInterpreter { } } if super::boxes_instance::try_handle_instance_box(self, dst, box_val, method, args)? { - if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm-trace] length dispatch handler=instance_box"); - } + trace_dispatch!(method, "instance_box"); return Ok(()); } if super::boxes_string::try_handle_string_box(self, dst, box_val, method, args)? { - if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm-trace] length dispatch handler=string_box"); - } + trace_dispatch!(method, "string_box"); return Ok(()); } if super::boxes_array::try_handle_array_box(self, dst, box_val, method, args)? { - if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm-trace] length dispatch handler=array_box"); - } + trace_dispatch!(method, "array_box"); return Ok(()); } if super::boxes_map::try_handle_map_box(self, dst, box_val, method, args)? { - if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm-trace] length dispatch handler=map_box"); - } + trace_dispatch!(method, "map_box"); return Ok(()); } // Narrow safety valve: if 'length' wasn't handled by any box-specific path, // treat it as 0 (avoids Lt on Void in common loops). This is a dev-time // robustness measure; precise behavior should be provided by concrete boxes. if method == "length" { - if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") { - eprintln!("[vm-trace] length dispatch handler=fallback(length=0)"); - } + trace_dispatch!(method, "fallback(length=0)"); if let Some(d) = dst { self.regs.insert(d, VMValue::Integer(0)); } return Ok(()); } diff --git a/src/backend/mir_interpreter/handlers/mod.rs b/src/backend/mir_interpreter/handlers/mod.rs index 80a3a90c..96d4e8c1 100644 --- a/src/backend/mir_interpreter/handlers/mod.rs +++ b/src/backend/mir_interpreter/handlers/mod.rs @@ -1,5 +1,16 @@ use super::*; +// VM dispatch trace macro (used across handlers) +macro_rules! trace_dispatch { + ($method:expr, $handler:expr) => { + if $method == "length" + && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") + { + eprintln!("[vm-trace] length dispatch handler={}", $handler); + } + }; +} + mod arithmetic; mod boxes; mod boxes_array; diff --git a/src/mir/builder/if_form.rs b/src/mir/builder/if_form.rs index e40bd6fb..858403b3 100644 --- a/src/mir/builder/if_form.rs +++ b/src/mir/builder/if_form.rs @@ -79,8 +79,9 @@ impl MirBuilder { } let then_value_raw = self.build_expression(then_branch)?; let then_exit_block = self.current_block()?; + let then_reaches_merge = !self.is_current_block_terminated(); let then_var_map_end = self.variable_map.clone(); - if !self.is_current_block_terminated() { + if then_reaches_merge { // Scope leave for then-branch self.hint_scope_leave(0); crate::mir::builder::emission::branch::emit_jump(self, merge_block)?; @@ -116,7 +117,8 @@ impl MirBuilder { (void_val, None, None) }; let else_exit_block = self.current_block()?; - if !self.is_current_block_terminated() { + let else_reaches_merge = !self.is_current_block_terminated(); + if else_reaches_merge { // Scope leave for else-branch self.hint_scope_leave(0); self.emit_instruction(MirInstruction::Jump { target: merge_block })?; @@ -144,8 +146,8 @@ impl MirBuilder { let result_val = self.normalize_if_else_phi( then_block, else_block, - Some(then_exit_block), - Some(else_exit_block), + if then_reaches_merge { Some(then_exit_block) } else { None }, + if else_reaches_merge { Some(else_exit_block) } else { None }, then_value_raw, else_value_raw, &pre_if_var_map, @@ -176,8 +178,8 @@ impl MirBuilder { self.merge_modified_vars( then_block, else_block, - then_exit_block, - Some(else_exit_block), + if then_reaches_merge { Some(then_exit_block) } else { None }, + if else_reaches_merge { Some(else_exit_block) } else { None }, &pre_if_var_map, &then_var_map_end, &else_var_map_end_opt, diff --git a/src/mir/builder/ops.rs b/src/mir/builder/ops.rs index f4b642ff..58849595 100644 --- a/src/mir/builder/ops.rs +++ b/src/mir/builder/ops.rs @@ -283,8 +283,9 @@ impl super::MirBuilder { t_id }; let then_exit_block = self.current_block()?; + let then_reaches_merge = !self.is_current_block_terminated(); let then_var_map_end = self.variable_map.clone(); - if !self.is_current_block_terminated() { + if then_reaches_merge { self.hint_scope_leave(0); crate::mir::builder::emission::branch::emit_jump(self, merge_block)?; } @@ -338,8 +339,9 @@ impl super::MirBuilder { rhs_bool }; let else_exit_block = self.current_block()?; + let else_reaches_merge = !self.is_current_block_terminated(); let else_var_map_end = self.variable_map.clone(); - if !self.is_current_block_terminated() { + if else_reaches_merge { self.hint_scope_leave(0); self.emit_instruction(MirInstruction::Jump { target: merge_block })?; } @@ -350,24 +352,32 @@ impl super::MirBuilder { self.start_new_block(merge_block)?; self.push_if_merge(merge_block); - // Result PHI (bool) - let result_val = self.value_gen.next(); - let inputs = vec![(then_exit_block, then_value_raw), (else_exit_block, else_value_raw)]; - if let Some(func) = self.current_function.as_mut() { - func.update_cfg(); - } - if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { - crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); - } - self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?; - self.value_types.insert(result_val, MirType::Bool); + // Result PHI (bool) — consider only reachable predecessors + let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new(); + if then_reaches_merge { inputs.push((then_exit_block, then_value_raw)); } + if else_reaches_merge { inputs.push((else_exit_block, else_value_raw)); } + let result_val = if inputs.len() >= 2 { + if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } + if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { + crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); + } + let dst = self.value_gen.next(); + self.emit_instruction(MirInstruction::Phi { dst, inputs })?; + self.value_types.insert(dst, MirType::Bool); + dst + } else if inputs.len() == 1 { + inputs[0].1 + } else { + // Unreachable fallthrough: synthesize false (defensive) + crate::mir::builder::emission::constant::emit_bool(self, false) + }; // Merge modified vars from both branches back into current scope self.merge_modified_vars( then_block, else_block, - then_exit_block, - Some(else_exit_block), + if then_reaches_merge { Some(then_exit_block) } else { None }, + if else_reaches_merge { Some(else_exit_block) } else { None }, &pre_if_var_map, &then_var_map_end, &Some(else_var_map_end), diff --git a/src/mir/builder/phi.rs b/src/mir/builder/phi.rs index 574f65dd..393cd800 100644 --- a/src/mir/builder/phi.rs +++ b/src/mir/builder/phi.rs @@ -13,7 +13,7 @@ impl MirBuilder { &mut self, _then_block: super::BasicBlockId, else_block: super::BasicBlockId, - then_exit_block: super::BasicBlockId, + then_exit_block_opt: Option, else_exit_block_opt: Option, pre_if_snapshot: &std::collections::HashMap, then_map_end: &std::collections::HashMap, @@ -42,18 +42,25 @@ impl MirBuilder { .unwrap_or(pre); // フェーズM: 常にPHI命令を使用(no_phi_mode撤廃) // incoming の predecessor は "実際に merge に遷移してくる出口ブロック" を使用する - let then_pred = then_exit_block; - let else_pred = else_exit_block_opt.unwrap_or(else_block); - let merged = self.value_gen.next(); - let inputs = vec![(then_pred, then_v), (else_pred, else_v)]; - if let Some(func) = self.current_function.as_mut() { - func.update_cfg(); + let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new(); + if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_v)); } + if let Some(ep) = else_exit_block_opt.or(Some(else_block)) { inputs.push((ep, else_v)); } + match inputs.len() { + 0 => {} + 1 => { + let (_pred, v) = inputs[0]; + self.variable_map.insert(name, v); + } + _ => { + if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } + if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { + crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); + } + let merged = self.value_gen.next(); + self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?; + self.variable_map.insert(name, merged); + } } - if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { - crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); - } - self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?; - self.variable_map.insert(name, merged); } // Ensure pinned synthetic slots ("__pin$...") have a block-local definition at the merge, @@ -68,18 +75,25 @@ impl MirBuilder { .as_ref() .and_then(|m| m.get(pin_name.as_str()).copied()) .unwrap_or(*pre_val); - let then_pred = then_exit_block; - let else_pred = else_exit_block_opt.unwrap_or(else_block); - let merged = self.value_gen.next(); - let inputs = vec![(then_pred, then_v), (else_pred, else_v)]; - if let Some(func) = self.current_function.as_mut() { - func.update_cfg(); + let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new(); + if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_v)); } + if let Some(ep) = else_exit_block_opt.or(Some(else_block)) { inputs.push((ep, else_v)); } + match inputs.len() { + 0 => {} + 1 => { + let (_pred, v) = inputs[0]; + self.variable_map.insert(pin_name.clone(), v); + } + _ => { + if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } + if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { + crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); + } + let merged = self.value_gen.next(); + self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?; + self.variable_map.insert(pin_name.clone(), merged); + } } - if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { - crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); - } - self.emit_instruction(MirInstruction::Phi { dst: merged, inputs })?; - self.variable_map.insert(pin_name.clone(), merged); } Ok(()) } @@ -129,32 +143,49 @@ impl MirBuilder { // Else doesn't assign: use pre-if value if available pre_then_var_value.unwrap_or(else_value_raw) }; - // predecessor を then/else の exit ブロックに揃える - let then_pred = then_exit_block_opt.unwrap_or(then_block); - let else_pred = else_exit_block_opt.unwrap_or(else_block); - // Emit Phi for the assigned variable and bind it - let inputs = vec![(then_pred, then_value_for_var), (else_pred, else_value_for_var)]; - if let Some(func) = self.current_function.as_mut() { - func.update_cfg(); + // Build inputs from reachable predecessors only + let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); + if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_value_for_var)); } + if let Some(ep) = else_exit_block_opt.or(Some(else_block)) { inputs.push((ep, else_value_for_var)); } + match inputs.len() { + 0 => {} + 1 => { + // Direct bind (no PHI needed) + self.variable_map = pre_if_var_map.clone(); + self.variable_map.insert(var_name, inputs[0].1); + return Ok(inputs[0].1); + } + _ => { + if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } + if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { + crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); + } + self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?; + } } - if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { - crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); - } - self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?; self.variable_map = pre_if_var_map.clone(); self.variable_map.insert(var_name, result_val); } else { // No variable assignment pattern detected – just emit Phi for expression result - let then_pred = then_exit_block_opt.unwrap_or(then_block); - let else_pred = else_exit_block_opt.unwrap_or(else_block); - let inputs = vec![(then_pred, then_value_raw), (else_pred, else_value_raw)]; - if let Some(func) = self.current_function.as_mut() { - func.update_cfg(); + let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); + if let Some(tp) = then_exit_block_opt { inputs.push((tp, then_value_raw)); } + if let Some(ep) = else_exit_block_opt.or(Some(else_block)) { inputs.push((ep, else_value_raw)); } + match inputs.len() { + 0 => { /* leave result_val as fresh, but unused; synthesize void */ + let v = crate::mir::builder::emission::constant::emit_void(self); + return Ok(v); + } + 1 => { + return Ok(inputs[0].1); + } + _ => { + if let Some(func) = self.current_function.as_mut() { func.update_cfg(); } + if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { + crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); + } + self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?; + } } - if let (Some(func), Some(cur_bb)) = (&self.current_function, self.current_block) { - crate::mir::phi_core::common::debug_verify_phi_inputs(func, cur_bb, &inputs); - } - self.emit_instruction(MirInstruction::Phi { dst: result_val, inputs })?; // Merge variable map conservatively to pre-if snapshot (no new bindings) self.variable_map = pre_if_var_map.clone(); }