diff --git a/src/mir/builder.rs b/src/mir/builder.rs index e1c341f0..c052dc1e 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -436,6 +436,9 @@ impl MirBuilder { pub(super) 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 .current_function @@ -448,6 +451,32 @@ impl MirBuilder { 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 } = 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 + }; + instruction = MirInstruction::Call { + dst: *dst, + func: crate::mir::ValueId::new(0), // Legacy compatibility + callee: Some(new_callee), + args: args.clone(), + effects: *effects, + }; + } + } + if let Some(ref mut function) = self.current_function { // Pre-capture branch/jump targets for predecessor update after we finish // mutably borrowing the current block. @@ -461,10 +490,35 @@ impl MirBuilder { 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 { diff --git a/src/mir/builder/builder_calls.rs b/src/mir/builder/builder_calls.rs index d59fa5b0..0056d615 100644 --- a/src/mir/builder/builder_calls.rs +++ b/src/mir/builder/builder_calls.rs @@ -224,15 +224,6 @@ impl super::MirBuilder { } } - // Safety: just before emitting the Call, re-materialize Method receiver in *current* block. - // This ensures the receiver has a definition in the same block as the Call instruction, - // even if the block changed between finalize_call_operands() and here. - // Critical for Stage-B and complex control flow where finalize and emit may be in different blocks. - if let Callee::Method { box_name, method, receiver: Some(r), certainty } = callee { - let r_local = crate::mir::builder::ssa::local::recv(self, r); - callee = Callee::Method { box_name, method, receiver: Some(r_local), certainty }; - } - // For Phase 2: Convert to legacy Call instruction with new callee field (use finalized operands) let legacy_call = MirInstruction::Call { dst: mir_call.dst, diff --git a/src/mir/builder/ssa/local.rs b/src/mir/builder/ssa/local.rs index 615cd40b..1e351edf 100644 --- a/src/mir/builder/ssa/local.rs +++ b/src/mir/builder/ssa/local.rs @@ -42,7 +42,14 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId // Stage-B 経路などでは current_block が割り当て済みでも、ブロック自体が // function にまだ追加されていない場合があり、そのまま emit_instruction すると // Copy が黙って落ちてしまう。ここで best-effort で作成しておく。 - let _ = builder.ensure_block_exists(bb); + // CRITICAL: Check for errors - if block creation fails, return original value. + if let Err(e) = builder.ensure_block_exists(bb) { + if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { + eprintln!("[local-ssa] ensure_block_exists FAILED bb={:?} kind={:?} v=%{} err={}", + bb, kind, v.0, e); + } + return v; + } // CRITICAL FIX: If `v` is from a pinned slot, check if there's a PHI value for that slot // in the current block's variable_map. If so, use the PHI value directly instead of @@ -69,11 +76,20 @@ pub fn ensure(builder: &mut MirBuilder, v: ValueId, kind: LocalKind) -> ValueId } let loc = builder.next_value_id(); - // Best-effort: errors are propagated by caller; we ignore here to keep helper infallible - let _ = builder.emit_instruction(crate::mir::MirInstruction::Copy { dst: loc, src: v }); + // CRITICAL: Check emit_instruction result - if Copy fails, return original value + // to avoid returning undefined ValueId. + if let Err(e) = builder.emit_instruction(crate::mir::MirInstruction::Copy { dst: loc, src: v }) { + if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { + eprintln!("[local-ssa] emit_instruction Copy FAILED bb={:?} kind={:?} v=%{} dst=%{} err={}", + bb, kind, v.0, loc.0, e); + } + // Failed to emit Copy - return original value instead of undefined dst + return v; + } if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") { eprintln!("[local-ssa] copy bb={:?} kind={:?} %{} -> %{}", bb, kind, v.0, loc.0); } + // Success: register metadata and cache if let Some(t) = builder.value_types.get(&v).cloned() { builder.value_types.insert(loc, t); }