Skip to main content

tidepool_codegen/
yield_type.rs

1/// Result of a single evaluation step.
2#[derive(Debug, PartialEq, Eq)]
3pub enum Yield {
4    /// Pure result — evaluation complete.
5    Done(*mut u8),
6    /// Effect request — stash continuation, dispatch to handler.
7    /// Fields: (union_tag: u64, request: *mut u8, continuation: *mut u8)
8    Request {
9        tag: u64,
10        request: *mut u8,
11        continuation: *mut u8,
12    },
13    /// Evaluation error.
14    Error(YieldError),
15}
16
17impl std::fmt::Display for Yield {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self {
20            Yield::Done(ptr) => write!(f, "Done({:p})", ptr),
21            Yield::Request {
22                tag,
23                request,
24                continuation,
25            } => {
26                write!(
27                    f,
28                    "Request(tag={}, req={:p}, cont={:p})",
29                    tag, request, continuation
30                )
31            }
32            Yield::Error(e) => write!(f, "Error({})", e),
33        }
34    }
35}
36
37#[derive(Debug, PartialEq, Eq, Clone)]
38pub enum YieldError {
39    /// Result HeapObject had unexpected tag byte.
40    UnexpectedTag(u8),
41    /// Result was Con but con_tag was neither Val nor E.
42    UnexpectedConTag(u64),
43    /// Val constructor had wrong number of fields.
44    BadValFields(u16),
45    /// E constructor had wrong number of fields.
46    BadEFields(u16),
47    /// Union constructor had wrong number of fields.
48    BadUnionFields(u16),
49    /// Null pointer encountered.
50    NullPointer,
51    /// Division by zero in JIT code.
52    DivisionByZero,
53    /// Arithmetic overflow in JIT code.
54    Overflow,
55    /// Haskell `error` called in JIT code.
56    UserError,
57    /// Haskell `error` called with a specific message.
58    UserErrorMsg(String),
59    /// Haskell `undefined` forced in JIT code.
60    Undefined,
61    /// GHC type metadata forced (should be dead code).
62    TypeMetadata,
63    /// Unresolved external variable forced.
64    UnresolvedVar(u64),
65    /// Application of null function pointer.
66    NullFunPtr,
67    /// Application of non-closure heap object.
68    BadFunPtrTag(u8),
69    /// Heap overflow after GC.
70    HeapOverflow,
71    /// Call depth exceeded (likely infinite list or unbounded recursion).
72    StackOverflow,
73    /// Fatal signal during JIT execution (SIGILL, SIGSEGV, SIGBUS, SIGTRAP).
74    Signal(i32),
75    /// Blackhole detected (infinite loop: thunk forced itself).
76    BlackHole,
77    /// Thunk encountered with an invalid evaluation state.
78    BadThunkState(u8),
79}
80
81impl std::fmt::Display for YieldError {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            YieldError::UnexpectedTag(tag) => write!(f, "unexpected heap tag: {}", tag),
85            YieldError::UnexpectedConTag(tag) => write!(f, "unexpected constructor tag: {}", tag),
86            YieldError::BadValFields(n) => {
87                write!(f, "Val constructor has {} fields, expected >= 1", n)
88            }
89            YieldError::BadEFields(n) => write!(f, "E constructor has {} fields, expected 2", n),
90            YieldError::BadUnionFields(n) => {
91                write!(f, "Union constructor has {} fields, expected 2", n)
92            }
93            YieldError::NullPointer => write!(f, "null pointer in effect result"),
94            YieldError::DivisionByZero => write!(f, "division by zero"),
95            YieldError::Overflow => write!(f, "arithmetic overflow"),
96            YieldError::UserError => write!(f, "Haskell error called"),
97            YieldError::UserErrorMsg(msg) => write!(f, "Haskell error: {}", msg),
98            YieldError::Undefined => write!(f, "Haskell undefined forced"),
99            YieldError::TypeMetadata => write!(f, "forced type metadata (should be dead code)"),
100            YieldError::UnresolvedVar(id) => {
101                let tag_char = (*id >> 56) as u8 as char;
102                let key = *id & ((1u64 << 56) - 1);
103                write!(
104                    f,
105                    "unresolved variable VarId({:#x}) [tag='{}', key={}]",
106                    id, tag_char, key
107                )
108            }
109            YieldError::NullFunPtr => write!(f, "application of null function pointer"),
110            YieldError::BadFunPtrTag(tag) => write!(f, "application of non-closure (tag={})", tag),
111            YieldError::HeapOverflow => write!(f, "heap overflow (nursery exhausted after GC)"),
112            YieldError::StackOverflow => write!(f, "stack overflow (likely infinite list or unbounded recursion — use zipWithIndex/imap/enumFromTo instead of [0..])"),
113            YieldError::BlackHole => write!(f, "blackhole detected (infinite loop: thunk forced itself)"),
114            YieldError::BadThunkState(state) => write!(f, "thunk has invalid evaluation state: {}", state),
115            YieldError::Signal(sig) => {
116                let ctx = crate::host_fns::get_exec_context();
117                #[cfg(unix)]
118                {
119                    let name = match *sig {
120                        libc::SIGILL => {
121                            "SIGILL (illegal instruction — likely exhausted case branch)"
122                        }
123                        libc::SIGSEGV => {
124                            "SIGSEGV (segmentation fault — likely invalid memory access)"
125                        }
126                        libc::SIGBUS => "SIGBUS (bus error)",
127                        libc::SIGTRAP => "SIGTRAP (trap — likely Cranelift trap instruction)",
128                        _ => {
129                            if !ctx.is_empty() {
130                                return write!(
131                                    f,
132                                    "JIT signal: signal {} (unknown, context: {})",
133                                    sig, ctx
134                                );
135                            } else {
136                                return write!(f, "JIT signal: signal {} (unknown)", sig);
137                            }
138                        }
139                    };
140                    if !ctx.is_empty() {
141                        write!(f, "JIT signal: {} (context: {})", name, ctx)
142                    } else {
143                        write!(f, "JIT signal: {}", name)
144                    }
145                }
146                #[cfg(not(unix))]
147                if !ctx.is_empty() {
148                    write!(f, "JIT signal: signal {} (context: {})", sig, ctx)
149                } else {
150                    write!(f, "JIT signal: signal {}", sig)
151                }
152            }
153        }
154    }
155}
156
157impl std::error::Error for YieldError {}
158
159impl From<crate::host_fns::RuntimeError> for YieldError {
160    fn from(err: crate::host_fns::RuntimeError) -> Self {
161        use crate::host_fns::RuntimeError;
162        match err {
163            RuntimeError::DivisionByZero => YieldError::DivisionByZero,
164            RuntimeError::Overflow => YieldError::Overflow,
165            RuntimeError::UserError => YieldError::UserError,
166            RuntimeError::UserErrorMsg(msg) => YieldError::UserErrorMsg(msg),
167            RuntimeError::Undefined => YieldError::Undefined,
168            RuntimeError::TypeMetadata => YieldError::TypeMetadata,
169            RuntimeError::UnresolvedVar(id) => YieldError::UnresolvedVar(id),
170            RuntimeError::NullFunPtr => YieldError::NullFunPtr,
171            RuntimeError::BadFunPtrTag(tag) => YieldError::BadFunPtrTag(tag),
172            RuntimeError::HeapOverflow => YieldError::HeapOverflow,
173            RuntimeError::StackOverflow => YieldError::StackOverflow,
174            RuntimeError::BlackHole => YieldError::BlackHole,
175            RuntimeError::BadThunkState(state) => YieldError::BadThunkState(state),
176        }
177    }
178}