byteflow/vm/fault.rs
1use std::fmt;
2
3/// Anything that can go wrong *inside* a running Flow's VM.
4///
5/// A `Fault` is never a Rust panic — panics are reserved for genuine host
6/// bugs and are caught at the worker boundary (see
7/// `crate::scheduler::worker`) precisely so that one Flow's
8/// bug (division by zero, a corrupt jump target that slipped past the
9/// verifier, an out-of-range register) can never take down a worker thread,
10/// let alone the whole runtime. A `Fault` instead becomes
11/// [`crate::FlowOutcome::Failed`] and is handed to the Flow's supervisor, which
12/// decides whether to restart it (design notes §15-16).
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub enum Fault {
15 DivideByZero,
16 RegisterOutOfRange { reg: u8, frame_size: u8 },
17 /// An instruction's register operand plus the offset it gathers at does
18 /// not fit the register index space *at all* — e.g. `Spawn a=255`, which
19 /// reads its arguments from `a+1..`.
20 ///
21 /// Distinct from [`Fault::RegisterOutOfRange`], which is about an index
22 /// that is perfectly representable and merely absent from this frame.
23 /// Kept separate so the fault cannot lie: reporting "register 255 is out
24 /// of range" for a request that was really for register 256 would send
25 /// whoever reads it looking in the wrong place.
26 RegisterIndexOverflow { base: u8, offset: u8 },
27 BadConstant { index: u32, pool_size: u32 },
28 BadFunction { index: u32, table_size: u32 },
29 BadOpcodeByte(u8),
30 /// Function call nesting exceeded `Vm::MAX_CALL_DEPTH`. Bytecode has no
31 /// native stack overflow (frames are heap-allocated `Vec<Value>`s), so
32 /// this is a deliberate, checked limit rather than a segfault.
33 CallStackOverflow { depth: usize },
34 TypeMismatch { expected: &'static str, got: &'static str },
35 /// `CallNative` referenced a slot outside the runtime's registered
36 /// native function table (design notes §30-31). Distinct from
37 /// `BadFunction`, which is about the *bytecode* function table baked
38 /// into the chunk — natives are supplied by the embedder at `Vm`
39 /// construction time and can't be range-checked by
40 /// `crate::bytecode::verify`, which has no visibility into them.
41 BadNative { index: u32, table_size: u32 },
42 /// A native function returned an error (host-side failure — I/O,
43 /// invalid argument the Rust side rejected, capability denied, etc).
44 /// The message is native-function-defined.
45 /// Host-side native refused the call (FFI error string).
46 NativeError(String),
47 /// S7: CALL_NATIVE failed the allowlist / NATIVE-right / revocation check
48 /// before the function pointer was touched.
49 NativeDenied(String),
50 /// Explicit `Trap` opcode, e.g. an assertion emitted by a compiler.
51 Explicit(i32),
52 /// Broken VM invariant (e.g. empty frame stack while running). Category D
53 /// in the error model — surfaced as a Flow fault, never as `unwrap`.
54 Invariant(&'static str),
55}
56
57impl fmt::Display for Fault {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match self {
60 Fault::DivideByZero => write!(f, "division by zero"),
61 Fault::RegisterOutOfRange { reg, frame_size } => {
62 write!(f, "register r{reg} out of range (frame has {frame_size} registers)")
63 }
64 Fault::BadConstant { index, pool_size } => {
65 write!(f, "constant index {index} out of range (pool size {pool_size})")
66 }
67 Fault::BadFunction { index, table_size } => {
68 write!(f, "function index {index} out of range (table size {table_size})")
69 }
70 Fault::RegisterIndexOverflow { base, offset } => write!(
71 f,
72 "register index r{base}+{offset} overflows the register index space (max r255)"
73 ),
74 Fault::BadOpcodeByte(b) => write!(f, "unknown opcode byte 0x{b:02X}"),
75 Fault::CallStackOverflow { depth } => write!(f, "call stack overflow at depth {depth}"),
76 Fault::TypeMismatch { expected, got } => {
77 write!(f, "type mismatch: expected {expected}, got {got}")
78 }
79 Fault::BadNative { index, table_size } => {
80 write!(f, "native function index {index} out of range (table size {table_size})")
81 }
82 Fault::NativeError(msg) => write!(f, "native function error: {msg}"),
83 Fault::NativeDenied(msg) => write!(f, "{msg}"),
84 Fault::Explicit(code) => write!(f, "explicit trap (code {code})"),
85 Fault::Invariant(msg) => write!(f, "vm invariant broken: {msg}"),
86 }
87 }
88}
89
90impl std::error::Error for Fault {}