1use thiserror::Error;
9
10pub type Result<T> = std::result::Result<T, Error>;
18
19#[derive(Debug, Clone, PartialEq, Eq, Error)]
21#[non_exhaustive]
22pub enum Error {
23 #[error(
25 "IR inlining cycle at operation `{op_id}`. Fix: remove the recursive Expr::Call chain or split the recursive algorithm into an explicit bounded Loop."
26 )]
27 InlineCycle {
28 op_id: String,
30 },
31
32 #[error(
34 "IR inlining could not resolve operation `{op_id}`. Fix: register a Category A operation with this id before lowering or replace the call with inline IR."
35 )]
36 InlineUnknownOp {
37 op_id: String,
39 },
40
41 #[error(
43 "IR inlining rejected non-inlinable operation `{op_id}`. Fix: this op processes buffer inputs and must be dispatched as a separate kernel, not composed via Expr::Call."
44 )]
45 InlineNonInlinable {
46 op_id: String,
48 },
49
50 #[error(
52 "IR inlining argument count mismatch for operation `{op_id}`: expected {expected}, got {got}. Fix: pass exactly one argument for each ReadOnly or Uniform input buffer declared by the callee program."
53 )]
54 InlineArgCountMismatch {
55 op_id: String,
57 expected: usize,
59 got: usize,
61 },
62
63 #[error(
65 "IR inlining found no output write for operation `{op_id}`. Fix: Ensure the op's program() body writes to its output buffer at least once."
66 )]
67 InlineNoOutput {
68 op_id: String,
70 },
71
72 #[error(
74 "IR inlining found {got} declared output buffers for operation `{op_id}`. Fix: mark exactly one result buffer with BufferDecl::output(...)."
75 )]
76 InlineOutputCountMismatch {
77 op_id: String,
79 got: usize,
81 },
82
83 #[error(
85 "Wire-format validation failed: {message}. Fix: recompile the frontend program set and ensure the compiler only emits valid instructions."
86 )]
87 WireFormatValidation {
88 message: String,
90 },
91
92 #[error(
94 "vyre target-text lowering: {message}. Fix: inspect the Program shape, backend capability report, and emitted shader diagnostics before retrying."
95 )]
96 Lowering {
97 message: String,
99 },
100
101 #[error(
103 "vyre reference interpreter: {message}. Fix: validate the Program and input buffer set before invoking the reference backend."
104 )]
105 Interp {
106 message: String,
108 },
109
110 #[error(
112 "GPU pipeline failed: {message}. Fix: verify a concrete driver is linked and the compiled buffers fit the target adapter limits."
113 )]
114 Gpu {
115 message: String,
117 },
118
119 #[error(
121 "Decode configuration failed: {message}. Fix: provide valid TOML and non-zero decode thresholds."
122 )]
123 DecodeConfig {
124 message: String,
126 },
127
128 #[error(
130 "Decode pipeline failed: {message}. Fix: inspect shader output sizing and source-region validation."
131 )]
132 Decode {
133 message: String,
135 },
136
137 #[error(
139 "Decompression pipeline failed: {message}. Fix: validate frame metadata, split oversized payloads, and inspect GPU decompression status words."
140 )]
141 Decompress {
142 message: String,
144 },
145
146 #[error(
148 "DFA pipeline failed: {message}. Fix: validate DFA transition tables, output links, and target adapter limits."
149 )]
150 Dfa {
151 message: String,
153 },
154
155 #[error(
157 "Dataflow pipeline failed: {message}. Fix: validate graph inputs, buffer sizing, and target adapter limits."
158 )]
159 Dataflow {
160 message: String,
162 },
163
164 #[error(
166 "Prefix construction failed: {message}. Fix: split the input before building prefix arrays or reduce per-file scan size."
167 )]
168 Prefix {
169 message: String,
171 },
172
173 #[error(
175 "CSR graph construction failed: {message}. Fix: cap graph size and ensure every edge endpoint is within node_count."
176 )]
177 Csr {
178 message: String,
180 },
181
182 #[error(
184 "Serialization failed: {message}. Fix: verify the wire payload is not truncated or corrupted."
185 )]
186 Serialization {
187 message: String,
189 },
190
191 #[error(
193 "Rule evaluation failed: {message}. Fix: validate rule pattern ids, thresholds, and verdict buffer sizing before lowering."
194 )]
195 RuleEval {
196 message: String,
198 },
199
200 #[error(
202 "Wire-format version mismatch: expected {expected}, found {found}. Fix: re-encode with a matching vyre version or upgrade this runtime."
203 )]
204 VersionMismatch {
205 expected: u32,
207 found: u32,
209 },
210
211 #[error(
213 "Unknown dialect `{name}` (requested version `{requested}`). Fix: link the dialect crate providing `{name}` into this runtime or drop the op that uses it before encoding."
214 )]
215 UnknownDialect {
216 name: String,
218 requested: String,
220 },
221
222 #[error(
224 "Unknown op `{op}` in dialect `{dialect}`. Fix: upgrade the runtime to a version that includes this op, or drop the op before encoding."
225 )]
226 UnknownOp {
227 dialect: String,
229 op: String,
231 },
232}
233
234impl Error {
235 #[must_use]
237 pub fn lowering(message: impl Into<String>) -> Self {
238 Self::Lowering {
239 message: message.into(),
240 }
241 }
242
243 #[must_use]
245 pub fn interp(message: impl Into<String>) -> Self {
246 Self::Interp {
247 message: message.into(),
248 }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn lowering_helper_contains_fix_hint() {
258 let err = Error::lowering("buffer too large");
259 let msg = err.to_string();
260 assert!(msg.contains("buffer too large"));
261 assert!(msg.contains("Fix:"));
262 }
263
264 #[test]
265 fn interp_helper_contains_fix_hint() {
266 let err = Error::interp("division by zero");
267 let msg = err.to_string();
268 assert!(msg.contains("division by zero"));
269 assert!(msg.contains("Fix:"));
270 }
271
272 #[test]
273 fn inline_cycle_display() {
274 let err = Error::InlineCycle {
275 op_id: "math::add".into(),
276 };
277 assert!(err.to_string().contains("math::add"));
278 assert!(err.to_string().contains("cycle"));
279 }
280
281 #[test]
282 fn version_mismatch_display() {
283 let err = Error::VersionMismatch {
284 expected: 6,
285 found: 5,
286 };
287 let msg = err.to_string();
288 assert!(msg.contains("6"));
289 assert!(msg.contains("5"));
290 }
291
292 #[test]
293 fn unknown_dialect_display() {
294 let err = Error::UnknownDialect {
295 name: "my-dialect".into(),
296 requested: "1.0".into(),
297 };
298 assert!(err.to_string().contains("my-dialect"));
299 }
300
301 #[test]
302 fn error_is_clone_and_eq() {
303 let a = Error::lowering("test");
304 let b = a.clone();
305 assert_eq!(a, b);
306 }
307
308 #[test]
309 fn inline_arg_count_mismatch_display() {
310 let err = Error::InlineArgCountMismatch {
311 op_id: "test::op".into(),
312 expected: 3,
313 got: 1,
314 };
315 let msg = err.to_string();
316 assert!(msg.contains("expected 3"));
317 assert!(msg.contains("got 1"));
318 }
319}