fix(mir/builder): use function-local ValueId throughout MIR builder
Phase 25.1b: Complete SSA fix - eliminate all global ValueId usage in function contexts. Root cause: ~75 locations throughout MIR builder were using global value generator (self.value_gen.next()) instead of function-local allocator (f.next_value_id()), causing SSA verification failures and runtime "use of undefined value" errors. Solution: - Added next_value_id() helper that automatically chooses correct allocator - Fixed 19 files with ~75 occurrences of ValueId allocation - All function-context allocations now use function-local IDs Files modified: - src/mir/builder/utils.rs: Added next_value_id() helper, fixed 8 locations - src/mir/builder/builder_calls.rs: 17 fixes - src/mir/builder/ops.rs: 8 fixes - src/mir/builder/stmts.rs: 7 fixes - src/mir/builder/emission/constant.rs: 6 fixes - src/mir/builder/rewrite/*.rs: 10 fixes - + 13 other files Verification: - cargo build --release: SUCCESS - Simple tests with NYASH_VM_VERIFY_MIR=1: Zero undefined errors - Multi-parameter static methods: All working Known remaining: ValueId(22) in Stage-B (separate issue to investigate) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -25,6 +25,19 @@ pub(super) fn builder_debug_log(msg: &str) {
|
||||
}
|
||||
|
||||
impl super::MirBuilder {
|
||||
// ---- Value ID allocation (function-local or module-global) ----
|
||||
/// Allocate a new ValueId in the appropriate context
|
||||
/// - Inside function: uses function-local allocator
|
||||
/// - Outside function: uses module-global allocator
|
||||
#[inline]
|
||||
pub(super) fn next_value_id(&mut self) -> super::ValueId {
|
||||
if let Some(ref mut f) = self.current_function {
|
||||
f.next_value_id() // Function context
|
||||
} else {
|
||||
self.value_gen.next() // Module context
|
||||
}
|
||||
}
|
||||
|
||||
// ---- LocalSSA convenience (readability helpers) ----
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
@ -72,7 +85,7 @@ impl super::MirBuilder {
|
||||
for name in names.iter() {
|
||||
if !name.starts_with("__pin$") { continue; }
|
||||
if let Some(&src) = self.variable_map.get(name) {
|
||||
let dst = self.value_gen.next();
|
||||
let dst = self.next_value_id();
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src })?;
|
||||
crate::mir::builder::metadata::propagate::propagate(self, src, dst);
|
||||
self.variable_map.insert(name.clone(), dst);
|
||||
@ -201,7 +214,7 @@ impl super::MirBuilder {
|
||||
value: super::ValueId,
|
||||
expected_type: String,
|
||||
) -> Result<super::ValueId, String> {
|
||||
let dst = self.value_gen.next();
|
||||
let dst = self.next_value_id();
|
||||
self.emit_instruction(super::MirInstruction::TypeOp {
|
||||
dst,
|
||||
op: TypeOpKind::Check,
|
||||
@ -217,7 +230,7 @@ impl super::MirBuilder {
|
||||
value: super::ValueId,
|
||||
target_type: super::MirType,
|
||||
) -> Result<super::ValueId, String> {
|
||||
let dst = self.value_gen.next();
|
||||
let dst = self.next_value_id();
|
||||
self.emit_instruction(super::MirInstruction::TypeOp {
|
||||
dst,
|
||||
op: TypeOpKind::Cast,
|
||||
@ -235,7 +248,7 @@ impl super::MirBuilder {
|
||||
if crate::config::env::mir_core13_pure() {
|
||||
return Ok(box_val);
|
||||
}
|
||||
let dst = self.value_gen.next();
|
||||
let dst = self.next_value_id();
|
||||
self.emit_instruction(super::MirInstruction::WeakRef {
|
||||
dst,
|
||||
op: WeakRefOp::New,
|
||||
@ -252,7 +265,7 @@ impl super::MirBuilder {
|
||||
if crate::config::env::mir_core13_pure() {
|
||||
return Ok(weak_ref);
|
||||
}
|
||||
let dst = self.value_gen.next();
|
||||
let dst = self.next_value_id();
|
||||
self.emit_instruction(super::MirInstruction::WeakRef {
|
||||
dst,
|
||||
op: WeakRefOp::Load,
|
||||
@ -282,7 +295,12 @@ impl super::MirBuilder {
|
||||
pub(crate) fn pin_to_slot(&mut self, v: super::ValueId, hint: &str) -> Result<super::ValueId, String> {
|
||||
self.temp_slot_counter = self.temp_slot_counter.wrapping_add(1);
|
||||
let slot_name = format!("__pin${}${}", self.temp_slot_counter, hint);
|
||||
let dst = self.value_gen.next();
|
||||
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
|
||||
let dst = if let Some(ref mut f) = self.current_function {
|
||||
f.next_value_id() // Function context: use local ID
|
||||
} else {
|
||||
self.value_gen.next() // Module context: use global ID
|
||||
};
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
|
||||
if super::utils::builder_debug_enabled() || std::env::var("NYASH_PIN_TRACE").ok().as_deref() == Some("1") {
|
||||
super::utils::builder_debug_log(&format!("pin slot={} src={} dst={}", slot_name, v.0, dst.0));
|
||||
@ -295,7 +313,12 @@ impl super::MirBuilder {
|
||||
|
||||
/// Ensure a value has a local definition in the current block by inserting a Copy.
|
||||
pub(crate) fn materialize_local(&mut self, v: super::ValueId) -> Result<super::ValueId, String> {
|
||||
let dst = self.value_gen.next();
|
||||
// Phase 25.1b: Use function-local ID allocator to avoid SSA verification failures
|
||||
let dst = if let Some(ref mut f) = self.current_function {
|
||||
f.next_value_id() // Function context: use local ID
|
||||
} else {
|
||||
self.value_gen.next() // Module context: use global ID
|
||||
};
|
||||
self.emit_instruction(super::MirInstruction::Copy { dst, src: v })?;
|
||||
// Propagate metadata (type/origin) from source to the new local copy
|
||||
crate::mir::builder::metadata::propagate::propagate(self, v, dst);
|
||||
|
||||
Reference in New Issue
Block a user