fix(mir/builder): improve LocalSSA error handling and add Copy/Call traces

Changes:
- src/mir/builder/ssa/local.rs:
  - Fix LocalSSA::ensure() to check emit_instruction() errors
  - Return original value instead of undefined ValueId on failure
  - Add NYASH_LOCAL_SSA_TRACE logging for failures

- src/mir/builder.rs:
  - Add Copy instruction trace with NYASH_LOCAL_SSA_TRACE=1
  - Add Call instruction trace for Method calls
  - Helps debug receiver materialization issues

- src/mir/builder/builder_calls.rs:
  - Remove duplicate receiver materialization (moved to builder.rs)
  - Rely on emit_instruction for final receiver handling

Related to Stage-B ValueId(21) undefined error investigation.
Next: Fix Nyash MirBuilder receiver handling in basic_lower_box.hako

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-17 08:41:50 +09:00
parent 06159da58b
commit f7d218190e
3 changed files with 73 additions and 12 deletions

View File

@ -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 {