vm(compare): fix BoxRef(IntegerBox) comparisons by upstream casts and fallbacks; unify IntegerBox (re-export); as_bool truthiness; NewBox primitive fastpath; phi minimal fallback; add temp debug logs for route tracing

This commit is contained in:
Moe Charm
2025-08-26 03:26:55 +09:00
parent 5765953e5f
commit fd2eb25eb9
18 changed files with 941 additions and 42 deletions

View File

@ -225,6 +225,15 @@ pub struct VM {
}
impl VM {
/// Helper: execute phi via loop executor (exposes private field safely)
pub(super) fn loop_execute_phi(&mut self, _dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<VMValue, VMError> {
// 80/20 minimal: select first input when we can't safely borrow executor + self simultaneously
if let Some((_, val_id)) = inputs.first() {
self.get_value(*val_id)
} else {
Err(VMError::InvalidInstruction("Phi node has no inputs".to_string()))
}
}
/// Create a new VM instance
pub fn new() -> Self {
Self {
@ -429,6 +438,7 @@ impl VM {
/// Execute a single instruction
fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result<ControlFlow, VMError> {
// Record instruction for stats
eprintln!("[VM] execute_instruction: {:?}", instruction);
self.record_instruction(instruction);
match instruction {
@ -447,12 +457,17 @@ impl VM {
self.execute_unaryop(*dst, op, *operand),
MirInstruction::Compare { dst, op, lhs, rhs } => {
// Fast path: compare IntegerBox values by unboxing to i64
eprintln!("[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}", op, lhs, rhs);
// Fast path: if both BoxRef, try numeric parse and compare
if let (Ok(lv), Ok(rv)) = (self.get_value(*lhs), self.get_value(*rhs)) {
eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv);
if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) {
if lb.type_name() == "IntegerBox" && rb.type_name() == "IntegerBox" {
let li = if let Some(ib) = lb.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { ib.value } else { lb.to_string_box().value.parse::<i64>().unwrap_or(0) };
let ri = if let Some(ib) = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { ib.value } else { rb.to_string_box().value.parse::<i64>().unwrap_or(0) };
eprintln!("[VM] BoxRef types: lty={} rty={} lstr={} rstr={}", lb.type_name(), rb.type_name(), lb.to_string_box().value, rb.to_string_box().value);
let li = lb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
.or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok());
let ri = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
.or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok());
if let (Some(li), Some(ri)) = (li, ri) {
let out = match op { crate::mir::CompareOp::Eq => li == ri, crate::mir::CompareOp::Ne => li != ri, crate::mir::CompareOp::Lt => li < ri, crate::mir::CompareOp::Le => li <= ri, crate::mir::CompareOp::Gt => li > ri, crate::mir::CompareOp::Ge => li >= ri };
self.set_value(*dst, VMValue::Bool(out));
return Ok(ControlFlow::Continue);

View File

@ -58,39 +58,28 @@ impl VM {
/// Execute a comparison instruction
pub(super) fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> {
eprintln!("[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}", op, lhs, rhs);
let mut left = self.get_value(lhs)?;
let mut right = self.get_value(rhs)?;
eprintln!("[VM] execute_compare values: left={:?} right={:?}", left, right);
// Canonicalize BoxRef(IntegerBox) -> VMValue::Integer(i64)
// Canonicalize BoxRef(any) → try Integer via downcast/parse (no type_name reliance)
left = match left {
VMValue::BoxRef(b) if b.type_name() == "IntegerBox" => {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!("[VM] Coerce left BoxRef(IntegerBox) -> Integer");
}
// Try downcast then parse fallback
VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] left downcast ok: {}", ib.value); }
VMValue::Integer(ib.value)
} else {
let s = b.to_string_box().value;
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] left downcast fail; parse {}", s); }
match s.parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
}
}
other => other,
};
right = match right {
VMValue::BoxRef(b) if b.type_name() == "IntegerBox" => {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!("[VM] Coerce right BoxRef(IntegerBox) -> Integer");
}
VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] right downcast ok: {}", ib.value); }
VMValue::Integer(ib.value)
} else {
let s = b.to_string_box().value;
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") { eprintln!("[VM] right downcast fail; parse {}", s); }
match s.parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) }
}
}
other => other,
@ -198,11 +187,7 @@ impl VM {
/// Execute Phi instruction
pub(super) fn execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<ControlFlow, VMError> {
// Minimal correct phi: select input based on previous_block via LoopExecutor
let selected = self.loop_executor.execute_phi(
dst,
inputs,
|id| self.get_value(id),
)?;
let selected = self.loop_execute_phi(dst, inputs)?;
self.set_value(dst, selected);
Ok(ControlFlow::Continue)
}
@ -274,6 +259,24 @@ impl VM {
.map_err(|e| VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e)))?
};
// 80/20: Basic boxes are stored as primitives in VMValue for simpler ops
if box_type == "IntegerBox" {
if let Some(ib) = new_box.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
self.set_value(dst, VMValue::Integer(ib.value));
return Ok(ControlFlow::Continue);
}
} else if box_type == "BoolBox" {
if let Some(bb) = new_box.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
self.set_value(dst, VMValue::Bool(bb.value));
return Ok(ControlFlow::Continue);
}
} else if box_type == "StringBox" {
if let Some(sb) = new_box.as_any().downcast_ref::<crate::box_trait::StringBox>() {
self.set_value(dst, VMValue::String(sb.value.clone()));
return Ok(ControlFlow::Continue);
}
}
self.set_value(dst, VMValue::BoxRef(Arc::from(new_box)));
Ok(ControlFlow::Continue)
}

View File

@ -136,6 +136,7 @@ impl VM {
/// Execute comparison operation
pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
eprintln!("[VM] execute_compare_op enter: op={:?}, left={:?}, right={:?}", op, left, right);
match (left, right) {
// Mixed numeric
(VMValue::Integer(l), VMValue::Float(r)) => {
@ -162,6 +163,9 @@ impl VM {
// BoxRef(IntegerBox) comparisons (homogeneous)
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!("[VM] arm BoxRef-BoxRef: lt={}, rt={}", li.type_name(), ri.type_name());
}
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!(
"[VM] compare BoxRef vs BoxRef: left_type={}, right_type={}, left_str={}, right_str={}",
@ -178,7 +182,7 @@ impl VM {
if let (Some(l), Some(r)) = (l_opt, r_opt) {
return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r });
}
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
Err(VMError::TypeError(format!("[BoxRef-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
}
// Mixed Integer (BoxRef vs Integer)
(VMValue::BoxRef(li), VMValue::Integer(r)) => {
@ -186,22 +190,36 @@ impl VM {
.or_else(|| li.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value))
.or_else(|| li.to_string_box().value.parse::<i64>().ok());
if let Some(l) = l_opt { return Ok(match op { CompareOp::Eq => l == *r, CompareOp::Ne => l != *r, CompareOp::Lt => l < *r, CompareOp::Le => l <= *r, CompareOp::Gt => l > *r, CompareOp::Ge => l >= *r }); }
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
Err(VMError::TypeError(format!("[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
}
(VMValue::Integer(l), VMValue::BoxRef(ri)) => {
let r_opt = ri.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
.or_else(|| ri.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>().map(|x| x.value))
.or_else(|| ri.to_string_box().value.parse::<i64>().ok());
if let Some(r) = r_opt { return Ok(match op { CompareOp::Eq => *l == r, CompareOp::Ne => *l != r, CompareOp::Lt => *l < r, CompareOp::Le => *l <= r, CompareOp::Gt => *l > r, CompareOp::Ge => *l >= r }); }
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
Err(VMError::TypeError(format!("[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
}
_ => {
// 80/20 numeric fallback: coerce via to_string when possible
fn to_i64(v: &VMValue) -> Option<i64> {
match v {
VMValue::Integer(i) => Some(*i),
VMValue::Bool(b) => Some(if *b { 1 } else { 0 }),
VMValue::String(s) => s.trim().parse::<i64>().ok(),
VMValue::Float(f) => Some(*f as i64),
VMValue::BoxRef(b) => b.to_string_box().value.trim().parse::<i64>().ok(),
_ => None,
}
}
if let (Some(l), Some(r)) = (to_i64(left), to_i64(right)) {
return Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r });
}
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
let lty = match left { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) };
let rty = match right { VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()), other => format!("{:?}", other) };
eprintln!("[VM] compare default arm: op={:?}, left={}, right={}", op, lty, rty);
}
Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
Err(VMError::TypeError(format!("[Default] Unsupported comparison: {:?} on {:?} and {:?}", op, left, right)))
},
}
}