Span trace utilities and runner source hint

This commit is contained in:
nyash-codex
2025-11-24 14:17:02 +09:00
parent 3154903121
commit 466e636af6
106 changed files with 4597 additions and 958 deletions

View File

@ -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 {

View File

@ -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))
}
}

View File

@ -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 で廃止済み。

View File

@ -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,

View File

@ -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(())
}
}
}
}