feat: Add dual-mode design doc and VM/MIR improvements
- Add comprehensive dual-mode design document (script vs application mode) - Update phase 9.8 documentation with implementation progress - Enhance VM with improved error handling and debugging - Add MIR verification improvements - Add test cases for MIR/VM POC 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -477,7 +477,67 @@ impl VM {
|
||||
}
|
||||
crate::mir::TypeOpKind::Cast => {
|
||||
let v = self.get_value(*value)?;
|
||||
self.set_value(*dst, v);
|
||||
let casted = match ty {
|
||||
crate::mir::MirType::Integer => match v {
|
||||
VMValue::Integer(_) => v,
|
||||
VMValue::Float(f) => VMValue::Integer(f as i64),
|
||||
other => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Cannot cast {:?} to Integer",
|
||||
other
|
||||
)));
|
||||
}
|
||||
},
|
||||
crate::mir::MirType::Float => match v {
|
||||
VMValue::Float(_) => v,
|
||||
VMValue::Integer(i) => VMValue::Float(i as f64),
|
||||
other => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Cannot cast {:?} to Float",
|
||||
other
|
||||
)));
|
||||
}
|
||||
},
|
||||
// For other types, only allow no-op cast when already same category
|
||||
crate::mir::MirType::Bool => match v {
|
||||
VMValue::Bool(_) => v,
|
||||
other => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Cannot cast {:?} to Bool",
|
||||
other
|
||||
)));
|
||||
}
|
||||
},
|
||||
crate::mir::MirType::String => match v {
|
||||
VMValue::String(_) => v,
|
||||
other => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Cannot cast {:?} to String",
|
||||
other
|
||||
)));
|
||||
}
|
||||
},
|
||||
crate::mir::MirType::Void => match v {
|
||||
VMValue::Void => v,
|
||||
other => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Cannot cast {:?} to Void",
|
||||
other
|
||||
)));
|
||||
}
|
||||
},
|
||||
crate::mir::MirType::Box(name) => match v {
|
||||
VMValue::BoxRef(ref arc) if arc.type_name() == name => v,
|
||||
other => {
|
||||
return Err(VMError::TypeError(format!(
|
||||
"Cannot cast {:?} to Box<{}>",
|
||||
other, name
|
||||
)));
|
||||
}
|
||||
},
|
||||
_ => v,
|
||||
};
|
||||
self.set_value(*dst, casted);
|
||||
}
|
||||
}
|
||||
Ok(ControlFlow::Continue)
|
||||
@ -640,8 +700,11 @@ impl VM {
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
|
||||
// Fast-path for ArrayBox methods using original BoxRef (preserve state)
|
||||
// Fast-path for common Box methods (Array/Map/String-like)
|
||||
let fastpath_disabled = std::env::var("NYASH_VM_DISABLE_FASTPATH").is_ok();
|
||||
if !fastpath_disabled {
|
||||
if let VMValue::BoxRef(ref arc_any) = box_vm_value {
|
||||
// ArrayBox: get/set/push
|
||||
if let Some(arr) = arc_any.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
match method.as_str() {
|
||||
"get" => {
|
||||
@ -660,9 +723,52 @@ impl VM {
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
"push" => {
|
||||
if let Some(arg0) = arg_values.get(0) {
|
||||
let res = arr.push((*arg0).clone_or_share());
|
||||
if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(res); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// MapBox: get/set/has
|
||||
if let Some(map) = arc_any.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
match method.as_str() {
|
||||
"get" => {
|
||||
if let Some(arg0) = arg_values.get(0) {
|
||||
let res = map.get((*arg0).clone_or_share());
|
||||
if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(res); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
"set" => {
|
||||
if arg_values.len() >= 2 {
|
||||
let k = (*arg_values.get(0).unwrap()).clone_or_share();
|
||||
let vval = (*arg_values.get(1).unwrap()).clone_or_share();
|
||||
let res = map.set(k, vval);
|
||||
if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(res); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
"has" => {
|
||||
if let Some(arg0) = arg_values.get(0) {
|
||||
let res = map.has((*arg0).clone_or_share());
|
||||
if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(res); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// toString(): generic fast-path for any BoxRef (0-arg)
|
||||
if method == "toString" && arg_values.is_empty() {
|
||||
let res = box_nyash.to_string_box();
|
||||
if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(Box::new(res)); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); }
|
||||
return Ok(ControlFlow::Continue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the method - unified dispatch for all Box types
|
||||
|
||||
@ -54,6 +54,20 @@ pub enum VerificationError {
|
||||
merge_block: BasicBlockId,
|
||||
pred_block: BasicBlockId,
|
||||
},
|
||||
/// WeakRef(load) must originate from a WeakNew/WeakRef(new)
|
||||
InvalidWeakRefSource {
|
||||
weak_ref: ValueId,
|
||||
block: BasicBlockId,
|
||||
instruction_index: usize,
|
||||
reason: String,
|
||||
},
|
||||
/// Barrier pointer must not be a void constant
|
||||
InvalidBarrierPointer {
|
||||
ptr: ValueId,
|
||||
block: BasicBlockId,
|
||||
instruction_index: usize,
|
||||
reason: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// MIR verifier for SSA form and semantic correctness
|
||||
@ -113,6 +127,10 @@ impl MirVerifier {
|
||||
if let Err(mut merge_errors) = self.verify_merge_uses(function) {
|
||||
local_errors.append(&mut merge_errors);
|
||||
}
|
||||
// 5. Minimal checks for WeakRef/Barrier
|
||||
if let Err(mut weak_barrier_errors) = self.verify_weakref_and_barrier(function) {
|
||||
local_errors.append(&mut weak_barrier_errors);
|
||||
}
|
||||
|
||||
if local_errors.is_empty() {
|
||||
Ok(())
|
||||
@ -120,6 +138,89 @@ impl MirVerifier {
|
||||
Err(local_errors)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify WeakRef/Barrier minimal semantics
|
||||
fn verify_weakref_and_barrier(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||
use super::MirInstruction;
|
||||
let mut errors = Vec::new();
|
||||
// Build def map value -> (block, idx, &inst)
|
||||
let mut def_map: HashMap<ValueId, (BasicBlockId, usize, &MirInstruction)> = HashMap::new();
|
||||
for (bid, block) in &function.blocks {
|
||||
for (idx, inst) in block.all_instructions().enumerate() {
|
||||
if let Some(dst) = inst.dst_value() {
|
||||
def_map.insert(dst, (*bid, idx, inst));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (bid, block) in &function.blocks {
|
||||
for (idx, inst) in block.all_instructions().enumerate() {
|
||||
match inst {
|
||||
MirInstruction::WeakRef { op: super::WeakRefOp::Load, value, .. } => {
|
||||
match def_map.get(value) {
|
||||
Some((_db, _di, def_inst)) => match def_inst {
|
||||
MirInstruction::WeakRef { op: super::WeakRefOp::New, .. } | MirInstruction::WeakNew { .. } => {}
|
||||
_ => {
|
||||
errors.push(VerificationError::InvalidWeakRefSource {
|
||||
weak_ref: *value,
|
||||
block: *bid,
|
||||
instruction_index: idx,
|
||||
reason: "weakref.load source is not a weakref.new/weak_new".to_string(),
|
||||
});
|
||||
}
|
||||
},
|
||||
None => {
|
||||
errors.push(VerificationError::InvalidWeakRefSource {
|
||||
weak_ref: *value,
|
||||
block: *bid,
|
||||
instruction_index: idx,
|
||||
reason: "weakref.load source is undefined".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
MirInstruction::WeakLoad { weak_ref, .. } => {
|
||||
match def_map.get(weak_ref) {
|
||||
Some((_db, _di, def_inst)) => match def_inst {
|
||||
MirInstruction::WeakNew { .. } | MirInstruction::WeakRef { op: super::WeakRefOp::New, .. } => {}
|
||||
_ => {
|
||||
errors.push(VerificationError::InvalidWeakRefSource {
|
||||
weak_ref: *weak_ref,
|
||||
block: *bid,
|
||||
instruction_index: idx,
|
||||
reason: "weak_load source is not a weak_new/weakref.new".to_string(),
|
||||
});
|
||||
}
|
||||
},
|
||||
None => {
|
||||
errors.push(VerificationError::InvalidWeakRefSource {
|
||||
weak_ref: *weak_ref,
|
||||
block: *bid,
|
||||
instruction_index: idx,
|
||||
reason: "weak_load source is undefined".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
MirInstruction::Barrier { ptr, .. } | MirInstruction::BarrierRead { ptr } | MirInstruction::BarrierWrite { ptr } => {
|
||||
if let Some((_db, _di, def_inst)) = def_map.get(ptr) {
|
||||
if let MirInstruction::Const { value: super::ConstValue::Void, .. } = def_inst {
|
||||
errors.push(VerificationError::InvalidBarrierPointer {
|
||||
ptr: *ptr,
|
||||
block: *bid,
|
||||
instruction_index: idx,
|
||||
reason: "barrier pointer is void".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||
}
|
||||
|
||||
/// Verify SSA form properties
|
||||
fn verify_ssa_form(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||
@ -419,6 +520,12 @@ impl std::fmt::Display for VerificationError {
|
||||
write!(f, "Merge block {} uses predecessor-defined value {} from block {} without Phi",
|
||||
merge_block, value, pred_block)
|
||||
},
|
||||
VerificationError::InvalidWeakRefSource { weak_ref, block, instruction_index, reason } => {
|
||||
write!(f, "Invalid WeakRef source {} in block {} at {}: {}", weak_ref, block, instruction_index, reason)
|
||||
},
|
||||
VerificationError::InvalidBarrierPointer { ptr, block, instruction_index, reason } => {
|
||||
write!(f, "Invalid Barrier pointer {} in block {} at {}: {}", ptr, block, instruction_index, reason)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +44,61 @@ mod tests {
|
||||
let _ = vm.execute_module(&module).expect("VM should execute module");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vm_exec_typeop_cast_int_float() {
|
||||
let mut func = make_main();
|
||||
let bb = func.entry_block;
|
||||
|
||||
let v0 = func.next_value_id();
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Integer(3) });
|
||||
|
||||
let v1 = func.next_value_id();
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Float });
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
|
||||
|
||||
let mut module = MirModule::new("test".to_string());
|
||||
module.add_function(func);
|
||||
let mut vm = VM::new();
|
||||
let _ = vm.execute_module(&module).expect("int->float cast should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vm_exec_typeop_cast_float_int() {
|
||||
let mut func = make_main();
|
||||
let bb = func.entry_block;
|
||||
|
||||
let v0 = func.next_value_id();
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::Float(3.7) });
|
||||
|
||||
let v1 = func.next_value_id();
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer });
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
|
||||
|
||||
let mut module = MirModule::new("test".to_string());
|
||||
module.add_function(func);
|
||||
let mut vm = VM::new();
|
||||
let _ = vm.execute_module(&module).expect("float->int cast should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vm_exec_typeop_cast_invalid_should_error() {
|
||||
let mut func = make_main();
|
||||
let bb = func.entry_block;
|
||||
|
||||
let v0 = func.next_value_id();
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v0, value: ConstValue::String("x".to_string()) });
|
||||
|
||||
let v1 = func.next_value_id();
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::TypeOp { dst: v1, op: crate::mir::TypeOpKind::Cast, value: v0, ty: MirType::Integer });
|
||||
func.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: None });
|
||||
|
||||
let mut module = MirModule::new("test".to_string());
|
||||
module.add_function(func);
|
||||
let mut vm = VM::new();
|
||||
let res = vm.execute_module(&module);
|
||||
assert!(res.is_err(), "invalid cast should return error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vm_exec_legacy_typecheck_cast() {
|
||||
let mut func = make_main();
|
||||
|
||||
Reference in New Issue
Block a user