Skip to content

Commit 6c173be

Browse files
nyash-codexclaude
andcommitted
fix(vm): partial equals() fix - pointer equality support
Partial fix for VM equality comparison bug. Adds infrastructure for semantic equality but keeps user-defined equals() dispatch disabled. **What This Fixes**: - ✅ Primitive comparisons (int, string, bool) - unchanged, still work - ✅ Pointer equality (same instance) - fast path with Arc::ptr_eq - ✅ VoidBox/MissingBox special cases - handled correctly - ✅ Infrastructure for eval_equals() method dispatch **What's Still Broken**: - ❌ User-defined equals() methods - dispatch disabled (line 61: if false) - ❌ Box semantic equality - falls back to reference inequality **Root Cause** (from investigation): - Bug is NOT in eval.rs:224 (Compare instruction) - Bug is in abi_util.rs:75 (eq_vm uses Arc::ptr_eq for ALL boxes) - Calling exec_function_inner() from eval_cmp() causes re-entrant stack overflow **Next Steps** (root fix required): - Option 1: Transform `box1 == box2` → `box1.equals(box2)` at MIR level - Option 2: Use existing BoxCall mechanism instead of Compare instruction - Option 3: Deferred execution queue to avoid re-entrant calls **Files Modified**: - src/backend/mir_interpreter/helpers/eval.rs - Added eval_equals() method (lines 23-77) - Modified eval_cmp() to take &mut self (line 209) - Changed Compare Eq/Ne to use eval_equals() (lines 306-307) - Added InstanceBox, Arc imports **Test Results**: - ✅ Primitives: ALL PASS - ✅ Pointer equality: PASS - ❌ User-defined equals(): DISABLED (would stack overflow) **Issue**: This is a PARTIAL fix. Full semantic equality requires root fix. See: docs/development/issues/equals-stack-overflow.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 980bc2c commit 6c173be

1 file changed

Lines changed: 61 additions & 3 deletions

File tree

  • src/backend/mir_interpreter/helpers

src/backend/mir_interpreter/helpers/eval.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use super::super::*;
22
use crate::backend::mir_interpreter::VmConfig;
33
use crate::box_trait::VoidBox;
4+
use crate::instance_v2::InstanceBox;
45
use std::string::String as StdString;
6+
use std::sync::Arc;
57

68
impl MirInterpreter {
79
#[inline]
@@ -18,6 +20,62 @@ impl MirInterpreter {
1820
}
1921
}
2022

23+
/// Smart equality comparison that supports user-defined equals() methods.
24+
///
25+
/// Strategy:
26+
/// 1. Fast path: Pointer equality for same instance (Arc::ptr_eq)
27+
/// 2. Smart path: Call user-defined equals() method if exists
28+
/// 3. Fallback: Reference inequality for different instances
29+
/// 4. Special cases: VoidBox/MissingBox comparisons
30+
pub(in crate::backend::mir_interpreter) fn eval_equals(
31+
&mut self,
32+
a: &VMValue,
33+
b: &VMValue
34+
) -> Result<bool, VMError> {
35+
use VMValue::*;
36+
37+
match (a, b) {
38+
(BoxRef(ax), BoxRef(bx)) => {
39+
// Fast path: pointer equality short-circuit
40+
if Arc::ptr_eq(ax, bx) {
41+
return Ok(true);
42+
}
43+
44+
// VoidBox special case - treat as equal to each other
45+
let a_is_void = ax.as_any().downcast_ref::<VoidBox>().is_some();
46+
let b_is_void = bx.as_any().downcast_ref::<VoidBox>().is_some();
47+
if a_is_void && b_is_void {
48+
return Ok(true);
49+
}
50+
51+
// MissingBox special case - treat as equal to each other
52+
let a_is_missing = ax.as_any().downcast_ref::<crate::boxes::missing_box::MissingBox>().is_some();
53+
let b_is_missing = bx.as_any().downcast_ref::<crate::boxes::missing_box::MissingBox>().is_some();
54+
if a_is_missing && b_is_missing {
55+
return Ok(true);
56+
}
57+
58+
// TODO: Method dispatch for InstanceBox with user-defined equals()
59+
// Temporarily disabled due to stack overflow issue - needs investigation
60+
// See: https://github.com/hakorune/hakorune/issues/XXX
61+
if false {
62+
if let Some(inst) = ax.as_any().downcast_ref::<InstanceBox>() {
63+
let class_name = &inst.class_name;
64+
let method_sig = format!("{}.equals/1", class_name);
65+
if let Some(_func) = self.functions.get(&method_sig) {
66+
eprintln!("[vm-warn] equals() method found for {} but dispatch is disabled", class_name);
67+
}
68+
}
69+
}
70+
71+
// Fallback: different instances without equals() method
72+
Ok(false)
73+
}
74+
// Primitives and other types: use existing eq_vm logic
75+
_ => Ok(eq_vm(a, b))
76+
}
77+
}
78+
2179
pub(in crate::backend::mir_interpreter) fn reg_load(&self, id: ValueId) -> Result<VMValue, VMError> {
2280
match self.regs.get(&id).cloned() {
2381
Some(v) => Ok(v),
@@ -124,7 +182,7 @@ impl MirInterpreter {
124182
})
125183
}
126184

127-
pub(in crate::backend::mir_interpreter) fn eval_cmp(&self, op: CompareOp, a: VMValue, b: VMValue) -> Result<bool, VMError> {
185+
pub(in crate::backend::mir_interpreter) fn eval_cmp(&mut self, op: CompareOp, a: VMValue, b: VMValue) -> Result<bool, VMError> {
128186
use CompareOp::*;
129187
use VMValue::*;
130188
// Dev-time: normalize BoxRef(VoidBox) → VMValue::Void when tolerance is enabled or in --dev.
@@ -221,8 +279,8 @@ impl MirInterpreter {
221279
} else { (a4.clone(), b4.clone()) };
222280

223281
let result = match (op, &a5, &b5) {
224-
(Eq, _, _) => eq_vm(&a5, &b5),
225-
(Ne, _, _) => !eq_vm(&a5, &b5),
282+
(Eq, _, _) => self.eval_equals(&a5, &b5)?,
283+
(Ne, _, _) => !self.eval_equals(&a5, &b5)?,
226284
(Lt, Integer(x), Integer(y)) => x < y,
227285
(Le, Integer(x), Integer(y)) => x <= y,
228286
(Gt, Integer(x), Integer(y)) => x > y,

0 commit comments

Comments
 (0)