use super::{origin, observe, utils}; use super::{BasicBlockId, MirBuilder, MirInstruction, ValueId}; impl MirBuilder { /// Emit an instruction to the current basic block pub(in crate::mir) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> { let block_id = self.current_block.ok_or("No current basic block")?; // Make instruction mutable for potential receiver materialization let mut instruction = instruction; // Precompute debug metadata to avoid borrow conflicts later let _dbg_fn_name = self .scope_ctx .current_function .as_ref() .map(|f| f.signature.name.clone()); let _dbg_region_id = self.debug_current_region_id(); // P0: PHI の軽量補強と観測は、関数ブロック取得前に実施して借用競合を避ける if let MirInstruction::Phi { dst, inputs, .. } = &instruction { origin::phi::propagate_phi_meta(self, *dst, inputs); observe::ssa::emit_phi(self, *dst, inputs); } // CRITICAL: Final receiver materialization for MethodCall // This ensures the receiver has an in-block definition in the same block as the Call. // Must happen BEFORE function mutable borrow to avoid borrowck conflicts. if let MirInstruction::Call { callee: Some(callee), dst, args, effects, .. } = &instruction { use crate::mir::definitions::call_unified::Callee; if let Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind, } = callee.clone() { // LocalSSA: ensure receiver has a Copy in current_block let r_local = crate::mir::builder::ssa::local::recv(self, r); // Update instruction with materialized receiver let new_callee = Callee::Method { box_name: box_name.clone(), method: method.clone(), receiver: Some(r_local), certainty, box_kind, }; instruction = MirInstruction::Call { dst: *dst, func: crate::mir::ValueId::INVALID, // Legacy dummy (not a real SSA use) callee: Some(new_callee), args: args.clone(), effects: *effects, }; } } if let Some(ref mut function) = self.scope_ctx.current_function { // Pre-capture branch/jump targets for predecessor update after we finish // mutably borrowing the current block. let (then_t, else_t, jump_t) = match &instruction { MirInstruction::Branch { then_bb, else_bb, .. } => (Some(*then_bb), Some(*else_bb), None), MirInstruction::Jump { target, .. } => (None, None, Some(*target)), _ => (None, None, None), }; // Extract function name before mutable borrow to avoid borrowck error let current_fn_name = function.signature.name.clone(); if let Some(block) = function.get_block_mut(block_id) { // CRITICAL: Copy専用トレース(LocalSSA調査用) if let MirInstruction::Copy { dst, src } = &instruction { if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!( "[emit-inst] fn={} bb={:?} COPY %{} <- %{}", current_fn_name, self.current_block.map(|b| b.0).unwrap_or(0), dst.0, src.0 ); } } // Invariant: Call must always carry a Callee (unified path). if let MirInstruction::Call { callee, .. } = &instruction { if callee.is_none() { return Err("builder invariant violated: MirInstruction::Call.callee must be Some (unified call)".into()); } else if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { use crate::mir::definitions::call_unified::Callee; if let Some(Callee::Method { box_name, method, receiver: Some(r), .. }) = callee { eprintln!( "[emit-inst] fn={} bb={:?} Call {}.{} recv=%{}", current_fn_name, self.current_block.map(|b| b.0).unwrap_or(0), box_name, method, r.0 ); } } else if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") { use crate::mir::definitions::call_unified::Callee; if let Some(Callee::Method { box_name, method, receiver: Some(r), .. }) = callee { let names: Vec = self .variable_ctx .variable_map .iter() .filter(|(_, &vid)| vid == *r) .map(|(k, _)| k.clone()) .collect(); eprintln!( "[builder/recv-trace] fn={} bb={:?} method={}.{} recv=%{} aliases={:?}", current_fn_name, self.current_block, box_name, method, r.0, names ); } } } if utils::builder_debug_enabled() { eprintln!( "[BUILDER] emit @bb{} -> {}", block_id, match &instruction { MirInstruction::TypeOp { dst, op, value, ty } => format!("typeop {:?} {} {:?} -> {}", op, value, ty, dst), MirInstruction::Print { value, .. } => format!("print {}", value), MirInstruction::BoxCall { box_val, method, method_id, args, dst, .. } => { if let Some(mid) = method_id { format!( "boxcall {}.{}[#{}]({:?}) -> {:?}", box_val, method, mid, args, dst ) } else { format!( "boxcall {}.{}({:?}) -> {:?}", box_val, method, args, dst ) } } MirInstruction::Call { func, args, dst, .. } => format!("call {}({:?}) -> {:?}", func, args, dst), MirInstruction::NewBox { dst, box_type, args, } => format!("new {}({:?}) -> {}", box_type, args, dst), MirInstruction::Const { dst, value } => format!("const {:?} -> {}", value, dst), MirInstruction::Branch { condition, then_bb, else_bb, .. } => format!("br {}, {}, {}", condition, then_bb, else_bb), MirInstruction::Jump { target, .. } => format!("br {}", target), _ => format!("{:?}", instruction), } ); } // Phase 136 Step 6/7: Use metadata_ctx for span block.add_instruction_with_span( instruction.clone(), self.metadata_ctx.current_span(), ); // Drop the mutable borrow of `block` before updating other blocks } // Update predecessor sets for branch/jump immediately so that // debug_verify_phi_inputs can observe a consistent CFG without // requiring a full function.update_cfg() pass. if let Some(t) = then_t { if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); } } if let Some(t) = else_t { if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); } } if let Some(t) = jump_t { if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); } } Ok(()) } else { Err(format!("Basic block {} does not exist", block_id)) } } /// Update an existing PHI instruction's inputs (for loop sealing) /// Used by LoopFormBuilder to complete incomplete PHI nodes #[allow(dead_code)] pub(super) fn update_phi_instruction( &mut self, block: BasicBlockId, phi_id: ValueId, new_inputs: Vec<(BasicBlockId, ValueId)>, ) -> Result<(), String> { if let Some(ref mut function) = self.scope_ctx.current_function { if let Some(block_data) = function.get_block_mut(block) { // Find PHI instruction with matching dst for inst in &mut block_data.instructions { if let MirInstruction::Phi { dst, inputs, .. } = inst { if *dst == phi_id { *inputs = new_inputs; return Ok(()); } } } Err(format!( "PHI instruction {} not found in block {}", phi_id, block )) } else { Err(format!("Block {} not found", block)) } } else { Err("No current function".to_string()) } } }