diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 58e45651..657bcd8c 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -116,6 +116,17 @@ tools/ci_check_golden.sh # 代表ケースのMIR含有チェック - 差分検証: `execute_instruction`/`execute_binop` に挿入したデバッグ出力が未出力→実行経路の相違が濃厚。 - 対応: 実行バイナリの署名チェックとコードパスの網羅的ログ追加でルート確定→修正。 +### ✅ 小タスク完了(2025-08-25 深夜) +- Verifier: Barrierの軽い文脈診断を追加(`NYASH_VERIFY_BARRIER_STRICT=1`で有効)。 +- ResultBox移行TODOを追加(`docs/development/current/RESULTBOX_MIGRATION_TODO.md`)。 +- VM: 旧`box_trait::ResultBox`扱いのデプリケーション警告を最小抑制(完全移行までの暫定)。 + +### ▶ リファクタリング開始(控えめ) +- 方針: 肥大化防止のため`src/backend/vm.rs`を2分割のみ実施。 + - `vm_values.rs`: `execute_binary_op`/`execute_unary_op`/`execute_compare_op` を移動。 + - `vm_boxcall.rs`: `call_box_method` を移動(`call_unified_method`は現状のまま)。 +- 影響最小・挙動非変更で、可読性と責務分離を先行する。 + ### ⚠️ MIRビルダー引き継ぎポイント(ChatGPT5さんへ) - **状況**: MIRビルダーのモジュール化完了(Phase 1-8コミット済み) - **問題**: MIR命令構造の変更により、expressions.rsでエラー発生 diff --git a/docs/development/current/RESULTBOX_MIGRATION_TODO.md b/docs/development/current/RESULTBOX_MIGRATION_TODO.md new file mode 100644 index 00000000..4664bdf7 --- /dev/null +++ b/docs/development/current/RESULTBOX_MIGRATION_TODO.md @@ -0,0 +1,19 @@ +## ResultBox Migration TODO (Phase 9.78h follow-up) + +Goal: fully migrate from legacy `box_trait::ResultBox` to `boxes::result::NyashResultBox` (aka `boxes::ResultBox`). + +### Current usages (grep snapshot) +- src/backend/vm.rs + - Handles both new `NyashResultBox` and legacy `box_trait::ResultBox` for `.is_ok/.get_value/.get_error` (deprecation suppressed)。 + +### Proposed steps (small, incremental) +- Step 1: Keep dual handling but gate legacy path with feature flag or cfg for deprecation-only builds(任意)。 +- Step 2: Audit call sites that construct legacy ResultBox; replace with `boxes::result::NyashResultBox` constructors。 +- Step 3: Remove legacy path from VM once no legacy constructors remain。 +- Step 4: Delete/Archive legacy `box_trait::ResultBox`(テスト緑後)。 + +### Notes +- New API already aliased: `pub type ResultBox = NyashResultBox;` so external references may transparently resolve after migration。 +- Keep migration scoped: do not mix with unrelated refactors。 + +Last updated: 2025-08-25 diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 0dce6e72..a5dc1ab0 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -5,6 +5,8 @@ pub mod vm; pub mod vm_phi; pub mod vm_instructions; +pub mod vm_values; +pub mod vm_boxcall; #[cfg(feature = "wasm-backend")] pub mod wasm; @@ -22,4 +24,4 @@ pub use wasm::{WasmBackend, WasmError}; pub use aot::{AotBackend, AotError, AotConfig, AotStats}; #[cfg(feature = "llvm")] -pub use llvm::{compile_and_execute as llvm_compile_and_execute, compile_to_object as llvm_compile_to_object}; \ No newline at end of file +pub use llvm::{compile_and_execute as llvm_compile_and_execute, compile_to_object as llvm_compile_to_object}; diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 4ee5b5fb..e7180dd7 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -626,290 +626,6 @@ impl VM { self.values[index] = Some(value); } - /// Execute binary operation - pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result { - // Fast path: logical AND/OR accept any truthy via as_bool (supports BoxRef BoolBox/Void coercions) - if matches!(*op, BinaryOp::And | BinaryOp::Or) { - let l = left.as_bool()?; - let r = right.as_bool()?; - return Ok(VMValue::Bool(match *op { BinaryOp::And => l && r, BinaryOp::Or => l || r, _ => unreachable!() })); - } - - match (left, right) { - // Logical booleans (native) - (VMValue::Bool(l), VMValue::Bool(r)) => { - let result = match op { - BinaryOp::And => *l && *r, - BinaryOp::Or => *l || *r, - _ => return Err(VMError::TypeError(format!( - "Unsupported boolean operation: {:?}", op - ))), - }; - Ok(VMValue::Bool(result)) - } - - // Logical booleans (BoxRef BoolBox) - (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) - if lb.as_any().downcast_ref::().is_some() - && rb.as_any().downcast_ref::().is_some() => - { - let l = lb.as_any().downcast_ref::().unwrap().value; - let r = rb.as_any().downcast_ref::().unwrap().value; - let result = match op { - BinaryOp::And => l && r, - BinaryOp::Or => l || r, - _ => return Err(VMError::TypeError(format!( - "Unsupported boolean BoxRef operation: {:?}", op - ))), - }; - Ok(VMValue::Bool(result)) - } - - // Mixed boolean forms (BoxRef BoolBox with native Bool) - (VMValue::BoxRef(lb), VMValue::Bool(r)) if lb.as_any().downcast_ref::().is_some() => { - let l = lb.as_any().downcast_ref::().unwrap().value; - let result = match op { - BinaryOp::And => l && *r, - BinaryOp::Or => l || *r, - _ => return Err(VMError::TypeError(format!( - "Unsupported boolean operation: {:?}", op - ))), - }; - Ok(VMValue::Bool(result)) - } - (VMValue::Bool(l), VMValue::BoxRef(rb)) if rb.as_any().downcast_ref::().is_some() => { - let r = rb.as_any().downcast_ref::().unwrap().value; - let result = match op { - BinaryOp::And => *l && r, - BinaryOp::Or => *l || r, - _ => return Err(VMError::TypeError(format!( - "Unsupported boolean operation: {:?}", op - ))), - }; - Ok(VMValue::Bool(result)) - } - - // Arithmetic with BoxRef(IntegerBox) - (VMValue::BoxRef(li), VMValue::BoxRef(ri)) - if li.as_any().downcast_ref::().is_some() - && ri.as_any().downcast_ref::().is_some() => - { - let l = li.as_any().downcast_ref::().unwrap().value; - let r = ri.as_any().downcast_ref::().unwrap().value; - let res = match op { - BinaryOp::Add => l + r, - BinaryOp::Sub => l - r, - BinaryOp::Mul => l * r, - BinaryOp::Div => { - if r == 0 { return Err(VMError::DivisionByZero); } - l / r - } - _ => return Err(VMError::InvalidInstruction(format!( - "Unsupported integer BoxRef operation: {:?}", op - ))), - }; - Ok(VMValue::Integer(res)) - } - - // Mixed Integer forms: BoxRef with native Integer - (VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::().is_some() => { - let l = li.as_any().downcast_ref::().unwrap().value; - let res = match op { - BinaryOp::Add => l + *r, - BinaryOp::Sub => l - *r, - BinaryOp::Mul => l * *r, - BinaryOp::Div => { - if *r == 0 { return Err(VMError::DivisionByZero); } - l / *r - } - _ => return Err(VMError::InvalidInstruction(format!( - "Unsupported integer operation: {:?}", op - ))), - }; - Ok(VMValue::Integer(res)) - } - (VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::().is_some() => { - let r = ri.as_any().downcast_ref::().unwrap().value; - let res = match op { - BinaryOp::Add => *l + r, - BinaryOp::Sub => *l - r, - BinaryOp::Mul => *l * r, - BinaryOp::Div => { - if r == 0 { return Err(VMError::DivisionByZero); } - *l / r - } - _ => return Err(VMError::InvalidInstruction(format!( - "Unsupported integer operation: {:?}", op - ))), - }; - Ok(VMValue::Integer(res)) - } - (VMValue::Integer(l), VMValue::Integer(r)) => { - let result = match op { - BinaryOp::Add => *l + *r, - BinaryOp::Sub => *l - *r, - BinaryOp::Mul => *l * *r, - BinaryOp::Div => { - if *r == 0 { - return Err(VMError::DivisionByZero); - } - *l / *r - }, - _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), - }; - Ok(VMValue::Integer(result)) - }, - - (VMValue::String(l), VMValue::Integer(r)) => { - // String + Integer concatenation - match op { - BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), - _ => Err(VMError::TypeError("String-integer operations only support addition".to_string())), - } - }, - - (VMValue::String(l), VMValue::Bool(r)) => { - // String + Bool concatenation - match op { - BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), - _ => Err(VMError::TypeError("String-bool operations only support addition".to_string())), - } - }, - - (VMValue::String(l), VMValue::String(r)) => { - // String concatenation - match op { - BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), - _ => Err(VMError::TypeError("String operations only support addition".to_string())), - } - }, - - // String + BoxRef concatenation - (VMValue::String(l), VMValue::BoxRef(r)) => { - match op { - BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r.to_string_box().value))), - _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())), - } - }, - - // BoxRef + String concatenation - (VMValue::BoxRef(l), VMValue::String(r)) => { - match op { - BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l.to_string_box().value, r))), - _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())), - } - }, - - _ => Err(VMError::TypeError(format!("Unsupported binary operation [vm.rs updated]: {:?} on {:?} and {:?}", op, left, right))), - } - } - - /// Execute unary operation - pub(super) fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result { - match (op, operand) { - (UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)), - (UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)), - _ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))), - } - } - - /// Execute comparison operation - pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result { - match (left, right) { - // Numeric mixed comparisons (Integer/Float) - (VMValue::Integer(l), VMValue::Float(r)) => { - let l = *l as f64; - let r = *r; - let result = 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, - }; - Ok(result) - }, - (VMValue::Float(l), VMValue::Integer(r)) => { - let l = *l; - let r = *r as f64; - let result = 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, - }; - Ok(result) - }, - // Bool comparisons: support Eq/Ne only for now - (VMValue::Bool(l), VMValue::Bool(r)) => { - let result = match op { - CompareOp::Eq => l == r, - CompareOp::Ne => l != r, - _ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))), - }; - Ok(result) - }, - - // Void comparisons: only Eq/Ne are defined - (VMValue::Void, VMValue::Void) => { - let result = match op { - CompareOp::Eq => true, - CompareOp::Ne => false, - _ => return Err(VMError::TypeError("Cannot order Void".to_string())), - }; - Ok(result) - }, - (VMValue::Void, _) | (_, VMValue::Void) => { - let result = match op { - CompareOp::Eq => false, // void == X (X != void) is false - CompareOp::Ne => true, // void != X is true - _ => return Err(VMError::TypeError("Cannot order Void".to_string())), - }; - Ok(result) - }, - - (VMValue::Integer(l), VMValue::Integer(r)) => { - let result = 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, - }; - Ok(result) - }, - - (VMValue::Float(l), VMValue::Float(r)) => { - let result = 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, - }; - Ok(result) - }, - - (VMValue::String(l), VMValue::String(r)) => { - let result = 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, - }; - Ok(result) - }, - - _ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))), - } - } /// Record an instruction execution for statistics pub(super) fn record_instruction(&mut self, instruction: &MirInstruction) { @@ -1031,7 +747,7 @@ impl VM { fn call_unified_method(&self, box_value: Box, method: &str, args: Vec>) -> Result, VMError> { // For now, we use the simplified method dispatch // In a full implementation, this would check for InstanceBox and dispatch appropriately - self.call_box_method(box_value, method, args) + self.call_box_method_impl(box_value, method, args) } /// Call a method on a Box - simplified version of interpreter method dispatch diff --git a/src/backend/vm_boxcall.rs b/src/backend/vm_boxcall.rs new file mode 100644 index 00000000..30f0ca0e --- /dev/null +++ b/src/backend/vm_boxcall.rs @@ -0,0 +1,110 @@ +/*! + * VM BoxCall Dispatch - method calls on boxes + */ + +use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; +use super::vm::{VM, VMError}; + +impl VM { + /// Call a method on a Box - simplified version of interpreter method dispatch + pub(super) fn call_box_method_impl(&self, box_value: Box, method: &str, _args: Vec>) -> Result, VMError> { + // ResultBox (NyashResultBox - new) + if let Some(result_box) = box_value.as_any().downcast_ref::() { + match method { + "is_ok" | "isOk" => { return Ok(result_box.is_ok()); } + "get_value" | "getValue" => { return Ok(result_box.get_value()); } + "get_error" | "getError" => { return Ok(result_box.get_error()); } + _ => return Ok(Box::new(VoidBox::new())), + } + } + + // ResultBox (box_trait::ResultBox - legacy) + { + #[allow(deprecated)] + if let Some(result_box_legacy) = box_value.as_any().downcast_ref::() { + match method { + "is_ok" | "isOk" => { return Ok(result_box_legacy.is_ok()); } + "get_value" | "getValue" => { return Ok(result_box_legacy.get_value()); } + "get_error" | "getError" => { return Ok(result_box_legacy.get_error()); } + _ => return Ok(Box::new(VoidBox::new())), + } + } + } + + // Generic fallback: toString for any Box type + if method == "toString" { + return Ok(Box::new(StringBox::new(box_value.to_string_box().value))); + } + + // StringBox methods + if let Some(string_box) = box_value.as_any().downcast_ref::() { + match method { + "length" | "len" => { return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); } + "toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); } + _ => return Ok(Box::new(VoidBox::new())), + } + } + + // ArrayBox methods (minimal set) + if let Some(array_box) = box_value.as_any().downcast_ref::() { + match method { + "push" => { if let Some(v) = _args.get(0) { return Ok(array_box.push(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: push(value) requires 1 arg"))); } + "pop" => { return Ok(array_box.pop()); }, + "length" | "len" => { return Ok(array_box.length()); }, + "get" => { if let Some(i) = _args.get(0) { return Ok(array_box.get(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(index) requires 1 arg"))); } + "set" => { if _args.len() >= 2 { return Ok(array_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(index, value) requires 2 args"))); } + "remove" => { if let Some(i) = _args.get(0) { return Ok(array_box.remove(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: remove(index) requires 1 arg"))); } + "contains" => { if let Some(v) = _args.get(0) { return Ok(array_box.contains(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: contains(value) requires 1 arg"))); } + "indexOf" => { if let Some(v) = _args.get(0) { return Ok(array_box.indexOf(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: indexOf(value) requires 1 arg"))); } + "clear" => { return Ok(array_box.clear()); }, + "join" => { if let Some(sep) = _args.get(0) { return Ok(array_box.join(sep.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: join(sep) requires 1 arg"))); } + "sort" => { return Ok(array_box.sort()); }, + "reverse" => { return Ok(array_box.reverse()); }, + "slice" => { if _args.len() >= 2 { return Ok(array_box.slice(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: slice(start, end) requires 2 args"))); } + _ => return Ok(Box::new(VoidBox::new())), + } + } + + // MapBox methods (minimal set) + if let Some(map_box) = box_value.as_any().downcast_ref::() { + match method { + "set" => { if _args.len() >= 2 { return Ok(map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(key, value) requires 2 args"))); } + "get" => { if let Some(k) = _args.get(0) { return Ok(map_box.get(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg"))); } + "has" => { if let Some(k) = _args.get(0) { return Ok(map_box.has(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg"))); } + "delete" | "remove" => { if let Some(k) = _args.get(0) { return Ok(map_box.delete(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: delete(key) requires 1 arg"))); } + "keys" => { return Ok(map_box.keys()); }, + "values" => { return Ok(map_box.values()); }, + "size" => { return Ok(map_box.size()); }, + "clear" => { return Ok(map_box.clear()); }, + _ => return Ok(Box::new(VoidBox::new())), + } + } + + // SocketBox methods (minimal + timeouts) + if let Some(sock) = box_value.as_any().downcast_ref::() { + match method { + "bind" => { if _args.len() >= 2 { return Ok(sock.bind(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: bind(address, port) requires 2 args"))); } + "listen" => { if let Some(b) = _args.get(0) { return Ok(sock.listen(b.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: listen(backlog) requires 1 arg"))); } + "accept" => { return Ok(sock.accept()); }, + "acceptTimeout" | "accept_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.accept_timeout(ms.clone_or_share())); } return Ok(Box::new(crate::box_trait::VoidBox::new())); } + "connect" => { if _args.len() >= 2 { return Ok(sock.connect(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: connect(address, port) requires 2 args"))); } + "read" => { return Ok(sock.read()); }, + "recvTimeout" | "recv_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.recv_timeout(ms.clone_or_share())); } return Ok(Box::new(StringBox::new(""))); } + "write" => { if let Some(d) = _args.get(0) { return Ok(sock.write(d.clone_or_share())); } return Ok(Box::new(crate::box_trait::BoolBox::new(false))); } + "close" => { return Ok(sock.close()); }, + "isServer" | "is_server" => { return Ok(sock.is_server()); }, + "isConnected" | "is_connected" => { return Ok(sock.is_connected()); }, + _ => return Ok(Box::new(VoidBox::new())), + } + } + + // IntegerBox methods + if let Some(integer_box) = box_value.as_any().downcast_ref::() { match method { "toString" => { return Ok(Box::new(StringBox::new(integer_box.value.to_string()))); }, "abs" => { return Ok(Box::new(IntegerBox::new(integer_box.value.abs()))); }, _ => return Ok(Box::new(VoidBox::new())), } } + + // BoolBox methods + if let Some(bool_box) = box_value.as_any().downcast_ref::() { match method { "toString" => { return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); }, _ => return Ok(Box::new(VoidBox::new())), } } + + // Default: return void for any unrecognized box type or method + Ok(Box::new(VoidBox::new())) + } +} diff --git a/src/backend/vm_values.rs b/src/backend/vm_values.rs new file mode 100644 index 00000000..9fb5300f --- /dev/null +++ b/src/backend/vm_values.rs @@ -0,0 +1,141 @@ +/*! + * VM Value Operations - arithmetic, logical, and comparison helpers + */ + +use crate::mir::{BinaryOp, CompareOp, UnaryOp}; +use super::vm::{VM, VMError, VMValue}; + +impl VM { + /// Execute binary operation + pub(super) fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result { + // Fast path: logical AND/OR accept any truthy via as_bool + if matches!(*op, BinaryOp::And | BinaryOp::Or) { + let l = left.as_bool()?; + let r = right.as_bool()?; + return Ok(VMValue::Bool(match *op { BinaryOp::And => l && r, BinaryOp::Or => l || r, _ => unreachable!() })); + } + + match (left, right) { + (VMValue::Integer(l), VMValue::Integer(r)) => { + let result = match op { + BinaryOp::Add => *l + *r, + BinaryOp::Sub => *l - *r, + BinaryOp::Mul => *l * *r, + BinaryOp::Div => { + if *r == 0 { return Err(VMError::DivisionByZero); } + *l / *r + }, + _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), + }; + Ok(VMValue::Integer(result)) + }, + + (VMValue::String(l), VMValue::Integer(r)) => { + match op { + BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), + _ => Err(VMError::TypeError("String-integer operations only support addition".to_string())), + } + }, + + (VMValue::String(l), VMValue::Bool(r)) => { + match op { + BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), + _ => Err(VMError::TypeError("String-bool operations only support addition".to_string())), + } + }, + + (VMValue::String(l), VMValue::String(r)) => { + match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), _ => Err(VMError::TypeError("String operations only support addition".to_string())) } + }, + + // String + BoxRef concatenation + (VMValue::String(l), VMValue::BoxRef(r)) => { + match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r.to_string_box().value))), _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())) } + }, + + // BoxRef + String concatenation + (VMValue::BoxRef(l), VMValue::String(r)) => { + match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l.to_string_box().value, r))), _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())) } + }, + + // Arithmetic with BoxRef(IntegerBox) + (VMValue::BoxRef(li), VMValue::BoxRef(ri)) if li.as_any().downcast_ref::().is_some() && ri.as_any().downcast_ref::().is_some() => { + let l = li.as_any().downcast_ref::().unwrap().value; + let r = ri.as_any().downcast_ref::().unwrap().value; + let res = match op { + BinaryOp::Add => l + r, + BinaryOp::Sub => l - r, + BinaryOp::Mul => l * r, + BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } l / r }, + _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer BoxRef operation: {:?}", op))), + }; + Ok(VMValue::Integer(res)) + } + + // Mixed Integer forms + (VMValue::BoxRef(li), VMValue::Integer(r)) if li.as_any().downcast_ref::().is_some() => { + let l = li.as_any().downcast_ref::().unwrap().value; + let res = match op { + BinaryOp::Add => l + *r, + BinaryOp::Sub => l - *r, + BinaryOp::Mul => l * *r, + BinaryOp::Div => { if *r == 0 { return Err(VMError::DivisionByZero); } l / *r }, + _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), + }; + Ok(VMValue::Integer(res)) + } + (VMValue::Integer(l), VMValue::BoxRef(ri)) if ri.as_any().downcast_ref::().is_some() => { + let r = ri.as_any().downcast_ref::().unwrap().value; + let res = match op { + BinaryOp::Add => *l + r, + BinaryOp::Sub => *l - r, + BinaryOp::Mul => *l * r, + BinaryOp::Div => { if r == 0 { return Err(VMError::DivisionByZero); } *l / r }, + _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), + }; + Ok(VMValue::Integer(res)) + } + + _ => Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))), + } + } + + /// Execute unary operation + pub(super) fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result { + match (op, operand) { + (UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)), + (UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)), + _ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))), + } + } + + /// Execute comparison operation + pub(super) fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result { + match (left, right) { + // Mixed numeric + (VMValue::Integer(l), VMValue::Float(r)) => { + let l = *l as f64; let r = *r; 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 }) + } + (VMValue::Float(l), VMValue::Integer(r)) => { + let l = *l; let r = *r as f64; 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 }) + } + // Bool + (VMValue::Bool(l), VMValue::Bool(r)) => { + Ok(match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, _ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))) }) + } + // Void + (VMValue::Void, VMValue::Void) => { + Ok(match op { CompareOp::Eq => true, CompareOp::Ne => false, _ => return Err(VMError::TypeError("Cannot order Void".to_string())) }) + } + (VMValue::Void, _) | (_, VMValue::Void) => { + Ok(match op { CompareOp::Eq => false, CompareOp::Ne => true, _ => return Err(VMError::TypeError("Cannot order Void".to_string())) }) + } + // Homogeneous + (VMValue::Integer(l), VMValue::Integer(r)) => 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 }), + (VMValue::Float(l), VMValue::Float(r)) => 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 }), + (VMValue::String(l), VMValue::String(r)) => 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))), + } + } +} + diff --git a/src/mir/verification.rs b/src/mir/verification.rs index 8cfcbfac..7414f01b 100644 --- a/src/mir/verification.rs +++ b/src/mir/verification.rs @@ -68,6 +68,12 @@ pub enum VerificationError { instruction_index: usize, reason: String, }, + /// Barrier appears without nearby memory ops (diagnostic; strict mode only) + SuspiciousBarrierContext { + block: BasicBlockId, + instruction_index: usize, + note: String, + }, } /// MIR verifier for SSA form and semantic correctness @@ -131,6 +137,10 @@ impl MirVerifier { if let Err(mut weak_barrier_errors) = self.verify_weakref_and_barrier(function) { local_errors.append(&mut weak_barrier_errors); } + // 6. Light barrier-context diagnostic (strict mode only) + if let Err(mut barrier_ctx) = self.verify_barrier_context(function) { + local_errors.append(&mut barrier_ctx); + } if local_errors.is_empty() { Ok(()) @@ -221,6 +231,59 @@ impl MirVerifier { if errors.is_empty() { Ok(()) } else { Err(errors) } } + + /// Light diagnostic: Barrier should be near memory ops in the same block (best-effort) + /// Enabled only when NYASH_VERIFY_BARRIER_STRICT=1 + fn verify_barrier_context(&self, function: &MirFunction) -> Result<(), Vec> { + let strict = std::env::var("NYASH_VERIFY_BARRIER_STRICT").ok().as_deref() == Some("1"); + if !strict { return Ok(()); } + + use super::MirInstruction; + let mut errors = Vec::new(); + for (bid, block) in &function.blocks { + // Build a flat vec of (idx, &inst) including terminator (as last) + let mut insts: Vec<(usize, &MirInstruction)> = block.instructions.iter().enumerate().collect(); + if let Some(term) = &block.terminator { + insts.push((usize::MAX, term)); + } + for (idx, inst) in &insts { + let is_barrier = matches!(inst, + MirInstruction::Barrier { .. } | + MirInstruction::BarrierRead { .. } | + MirInstruction::BarrierWrite { .. } + ); + if !is_barrier { continue; } + + // Look around +-2 instructions for a memory op hint + let mut has_mem_neighbor = false; + for (j, other) in &insts { + if *j == *idx { continue; } + // integer distance (treat usize::MAX as distant) + let dist = if *idx == usize::MAX || *j == usize::MAX { 99 } else { idx.max(j) - idx.min(j) }; + if dist > 2 { continue; } + if matches!(other, + MirInstruction::Load { .. } | + MirInstruction::Store { .. } | + MirInstruction::ArrayGet { .. } | + MirInstruction::ArraySet { .. } | + MirInstruction::RefGet { .. } | + MirInstruction::RefSet { .. } + ) { + has_mem_neighbor = true; + break; + } + } + if !has_mem_neighbor { + errors.push(VerificationError::SuspiciousBarrierContext { + block: *bid, + instruction_index: *idx, + note: "barrier without nearby memory op (±2 inst)".to_string(), + }); + } + } + } + if errors.is_empty() { Ok(()) } else { Err(errors) } + } /// Verify SSA form properties fn verify_ssa_form(&self, function: &MirFunction) -> Result<(), Vec> {