Span trace utilities and runner source hint
This commit is contained in:
@ -71,10 +71,23 @@ impl MirInterpreter {
|
||||
steps += 1;
|
||||
// max_steps == 0 は「上限なし」を意味する(開発/診断専用)。
|
||||
if max_steps > 0 && steps > max_steps {
|
||||
return Err(VMError::InvalidInstruction(format!(
|
||||
"vm step budget exceeded (max_steps={})",
|
||||
max_steps
|
||||
)));
|
||||
let last_inst_str = self.last_inst.as_ref().map(|inst| format!("{:?}", inst));
|
||||
let target_bb = self.last_block.unwrap_or(cur);
|
||||
let span = func
|
||||
.blocks
|
||||
.get(&target_bb)
|
||||
.and_then(|block| self.lookup_span_for_inst(block, self.last_inst_index));
|
||||
return Err(VMError::StepBudgetExceeded {
|
||||
max_steps,
|
||||
steps,
|
||||
function: self.cur_fn.clone(),
|
||||
current_block: cur,
|
||||
last_block: self.last_block,
|
||||
last_inst: last_inst_str,
|
||||
last_inst_index: self.last_inst_index,
|
||||
span,
|
||||
source_file: func.metadata.source_file.clone(),
|
||||
});
|
||||
}
|
||||
let block = func
|
||||
.blocks
|
||||
@ -128,6 +141,21 @@ impl MirInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_span_for_inst(
|
||||
&self,
|
||||
block: &BasicBlock,
|
||||
inst_index: Option<usize>,
|
||||
) -> Option<crate::ast::Span> {
|
||||
let idx = inst_index?;
|
||||
if idx < block.instructions.len() {
|
||||
block.instruction_span(idx)
|
||||
} else if idx == block.instructions.len() {
|
||||
block.terminator_span()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_phi_nodes(
|
||||
&mut self,
|
||||
block: &BasicBlock,
|
||||
@ -140,7 +168,10 @@ impl MirInterpreter {
|
||||
block.id, last_pred, block.predecessors
|
||||
);
|
||||
}
|
||||
for inst in block.phi_instructions() {
|
||||
for (idx, inst) in block.phi_instructions().enumerate() {
|
||||
self.last_block = Some(block.id);
|
||||
self.last_inst_index = Some(idx);
|
||||
self.last_inst = Some(inst.clone());
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
let dst_id = *dst;
|
||||
if trace_phi {
|
||||
@ -302,8 +333,10 @@ impl MirInterpreter {
|
||||
}
|
||||
|
||||
fn execute_block_instructions(&mut self, block: &BasicBlock) -> Result<(), VMError> {
|
||||
for inst in block.non_phi_instructions() {
|
||||
let phi_count = block.phi_instructions().count();
|
||||
for (idx, inst) in block.non_phi_instructions().enumerate() {
|
||||
self.last_block = Some(block.id);
|
||||
self.last_inst_index = Some(phi_count + idx);
|
||||
self.last_inst = Some(inst.clone());
|
||||
if Self::trace_enabled() {
|
||||
eprintln!("[vm-trace] inst bb={:?} {:?}", block.id, inst);
|
||||
@ -319,6 +352,11 @@ impl MirInterpreter {
|
||||
}
|
||||
|
||||
fn handle_terminator(&mut self, block: &BasicBlock) -> Result<BlockOutcome, VMError> {
|
||||
if let Some(term) = &block.terminator {
|
||||
self.last_block = Some(block.id);
|
||||
self.last_inst_index = Some(block.instructions.len());
|
||||
self.last_inst = Some(term.clone());
|
||||
}
|
||||
match &block.terminator {
|
||||
Some(MirInstruction::Return { value }) => {
|
||||
let result = if let Some(v) = value {
|
||||
|
||||
@ -102,6 +102,12 @@ impl MirInterpreter {
|
||||
self.write_string(dst, recv_box.to_string_box().value);
|
||||
Ok(())
|
||||
} else {
|
||||
// Phase 25.1 diagnostic: Log unknown method details before error
|
||||
eprintln!(
|
||||
"[vm/method-dispatch/plugin-invoke] Unknown method on Box: box_type_name={}, method_name={}",
|
||||
recv_box.type_name(),
|
||||
method
|
||||
);
|
||||
Err(self.err_method_not_found(&recv_box.type_name(), method))
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,8 +36,10 @@ impl MirInterpreter {
|
||||
|
||||
// Phase 2.2: Show parsed StaticMethodId info
|
||||
if let Some(id) = StaticMethodId::parse(func_name) {
|
||||
eprintln!("[DEBUG/vm] Parsed: box='{}', method='{}', arity={:?}",
|
||||
id.box_name, id.method, id.arity);
|
||||
eprintln!(
|
||||
"[DEBUG/vm] Parsed: box='{}', method='{}', arity={:?}",
|
||||
id.box_name, id.method, id.arity
|
||||
);
|
||||
} else {
|
||||
eprintln!("[DEBUG/vm] Not a static method (builtin?)");
|
||||
}
|
||||
@ -53,7 +55,9 @@ impl MirInterpreter {
|
||||
} else {
|
||||
&canonical
|
||||
};
|
||||
let matching: Vec<_> = self.functions.keys()
|
||||
let matching: Vec<_> = self
|
||||
.functions
|
||||
.keys()
|
||||
.filter(|k| k.starts_with(prefix))
|
||||
.collect();
|
||||
if !matching.is_empty() {
|
||||
@ -204,7 +208,9 @@ impl MirInterpreter {
|
||||
&canonical
|
||||
};
|
||||
|
||||
let similar: Vec<_> = self.functions.keys()
|
||||
let similar: Vec<_> = self
|
||||
.functions
|
||||
.keys()
|
||||
.filter(|k| k.starts_with(prefix))
|
||||
.take(5)
|
||||
.collect();
|
||||
@ -218,7 +224,8 @@ impl MirInterpreter {
|
||||
}
|
||||
}
|
||||
|
||||
err_msg.push_str("\n\n🔍 Debug: NYASH_DEBUG_FUNCTION_LOOKUP=1 for full lookup trace");
|
||||
err_msg
|
||||
.push_str("\n\n🔍 Debug: NYASH_DEBUG_FUNCTION_LOOKUP=1 for full lookup trace");
|
||||
|
||||
// NamingBox SSOT: ここで canonical に失敗したら素直に Unknown とする。
|
||||
// レガシーフォールバック(functions.get(func_name) 再探索)は Phase 25.x で廃止済み。
|
||||
|
||||
@ -33,6 +33,7 @@ pub struct MirInterpreter {
|
||||
// Trace context (dev-only; enabled with NYASH_VM_TRACE=1)
|
||||
pub(super) last_block: Option<BasicBlockId>,
|
||||
pub(super) last_inst: Option<MirInstruction>,
|
||||
pub(super) last_inst_index: Option<usize>,
|
||||
// Static box singleton instances (persistent across method calls)
|
||||
pub(super) static_boxes: HashMap<String, crate::instance_v2::InstanceBox>,
|
||||
// Static box declarations (metadata for creating instances)
|
||||
@ -56,6 +57,7 @@ impl MirInterpreter {
|
||||
cur_fn: None,
|
||||
last_block: None,
|
||||
last_inst: None,
|
||||
last_inst_index: None,
|
||||
static_boxes: HashMap::new(),
|
||||
static_box_decls: HashMap::new(),
|
||||
inst_count: 0,
|
||||
|
||||
@ -5,8 +5,9 @@
|
||||
* Kept separate to thin vm.rs and allow reuse across helpers.
|
||||
*/
|
||||
|
||||
use crate::ast::Span;
|
||||
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
|
||||
use crate::mir::ConstValue;
|
||||
use crate::mir::{BasicBlockId, ConstValue};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// VM execution error
|
||||
@ -18,6 +19,17 @@ pub enum VMError {
|
||||
DivisionByZero,
|
||||
StackUnderflow,
|
||||
TypeError(String),
|
||||
StepBudgetExceeded {
|
||||
max_steps: u64,
|
||||
steps: u64,
|
||||
function: Option<String>,
|
||||
current_block: BasicBlockId,
|
||||
last_block: Option<BasicBlockId>,
|
||||
last_inst: Option<String>,
|
||||
last_inst_index: Option<usize>,
|
||||
span: Option<Span>,
|
||||
source_file: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VMError {
|
||||
@ -29,6 +41,48 @@ impl std::fmt::Display for VMError {
|
||||
VMError::DivisionByZero => write!(f, "Division by zero"),
|
||||
VMError::StackUnderflow => write!(f, "Stack underflow"),
|
||||
VMError::TypeError(msg) => write!(f, "Type error: {}", msg),
|
||||
VMError::StepBudgetExceeded {
|
||||
max_steps,
|
||||
steps,
|
||||
function,
|
||||
current_block,
|
||||
last_block,
|
||||
last_inst,
|
||||
last_inst_index,
|
||||
span,
|
||||
source_file,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"vm step budget exceeded (max_steps={}, steps={}) at bb={}",
|
||||
max_steps, steps, current_block
|
||||
)?;
|
||||
if let Some(fn_name) = function {
|
||||
write!(f, " fn={}", fn_name)?;
|
||||
}
|
||||
if let Some(idx) = last_inst_index {
|
||||
write!(f, " last_inst_idx={}", idx)?;
|
||||
}
|
||||
if let Some(bb) = last_block {
|
||||
write!(f, " last_inst_bb={}", bb)?;
|
||||
}
|
||||
if let Some(inst) = last_inst {
|
||||
write!(f, " last_inst={}", inst)?;
|
||||
}
|
||||
match (span, source_file) {
|
||||
(Some(span), Some(file)) => {
|
||||
write!(f, " ({}:{}:{})", file, span.line, span.column)?;
|
||||
}
|
||||
(Some(span), None) => {
|
||||
write!(f, " (line {}, col {})", span.line, span.column)?;
|
||||
}
|
||||
(None, Some(file)) => {
|
||||
write!(f, " ({})", file)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user