fix(rewrite): toString normalization to BoxCall(slot #0) - Phase 287 P4
Root cause: toString/stringify/str were being rewritten to Global/Method calls with class inference, causing Main.toString/0 to be called for primitives. Fix (Box-First + Legacy Deletion): 1. ✅ MIR Builder - toString normalization (special.rs) - ALWAYS emit BoxCall with method_id=0 for toString/stringify/str - Do NOT rewrite to Global(Class.str/0) or Method calls - DELETED 70+ lines of complex class inference logic - Primitive guard with method name filter (known.rs) 2. ✅ JSON Serializer - method_id output (mir_json_emit.rs) - Include method_id field in BoxCall JSON for LLVM 3. ✅ LLVM Backend - universal slot #0 support - Extract method_id from JSON (instruction_lower.py) - Box primitives via nyash.box.from_i64 (boxcall.py) - Invoke toString via plugin system with method_id=0 - ⚠️ TODO: Add nyash.integer.tostring_h to kernel Test Results: ✅ VM: local x = 1; print(x.toString()) → "1" (PASS) ✅ VM: array_length test (boxed Integer) → PASS ⚠️ LLVM: Compiles successfully, needs kernel function SSOT: slot_registry - toString is ALWAYS universal slot #0 Legacy Deleted: - special.rs: Complex class inference rewrite (~70 lines) - special.rs: Unique suffix fallback for toString - special.rs: Main box special handling Files changed: - src/mir/builder/rewrite/special.rs (try_early_str_like_to_dst) - src/mir/builder/rewrite/known.rs (primitive guards x4) - src/runner/mir_json_emit.rs (method_id serialization x2) - src/llvm_py/builders/instruction_lower.py (method_id extraction) - src/llvm_py/instructions/boxcall.py (slot #0 handler) - docs/reference/language/quick-reference.md (toString SSOT) 🎊 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -541,6 +541,34 @@ pub extern "C" fn nyash_any_length_h_export(handle: i64) -> i64 {
|
||||
0
|
||||
}
|
||||
|
||||
// Any.toString_h(handle) -> handle (StringBox)
|
||||
//
|
||||
// Universal display conversion for LLVM/JIT paths where method dispatch may not
|
||||
// have plugin slots for builtin boxes. This should match the VM's expectation
|
||||
// that `toString()` is always available (universal slot #0).
|
||||
#[export_name = "nyash.any.toString_h"]
|
||||
pub extern "C" fn nyash_any_to_string_h_export(handle: i64) -> i64 {
|
||||
use nyash_rust::{
|
||||
box_trait::{NyashBox, StringBox},
|
||||
runtime::host_handles as handles,
|
||||
};
|
||||
// Treat <=0 as the null/void handle in AOT paths.
|
||||
if handle <= 0 {
|
||||
let s = "null".to_string();
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s.clone()));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
|
||||
return handles::to_handle_arc(arc) as i64;
|
||||
}
|
||||
let obj = match handles::get(handle as u64) {
|
||||
Some(o) => o,
|
||||
None => return 0,
|
||||
};
|
||||
let s = obj.to_string_box().value;
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s.clone()));
|
||||
nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
|
||||
handles::to_handle_arc(arc) as i64
|
||||
}
|
||||
|
||||
// Any.is_empty_h(handle) -> i64 (0/1)
|
||||
#[export_name = "nyash.any.is_empty_h"]
|
||||
pub extern "C" fn nyash_any_is_empty_h_export(handle: i64) -> i64 {
|
||||
|
||||
Reference in New Issue
Block a user