1use thiserror::Error;
4
5pub type IrResult<T, E = IrError> = std::result::Result<T, E>;
7
8#[derive(Debug, Clone, PartialEq, Eq, Error)]
10#[non_exhaustive]
11pub enum IrError {
12 #[error(
14 "IR inlining cycle at operation `{op_id}`. Fix: remove the recursive Expr::Call chain or split the recursive algorithm into an explicit bounded Loop."
15 )]
16 InlineCycle {
17 op_id: String,
19 },
20
21 #[error(
23 "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."
24 )]
25 InlineUnknownOp {
26 op_id: String,
28 },
29
30 #[error(
32 "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."
33 )]
34 InlineNonInlinable {
35 op_id: String,
37 },
38
39 #[error(
41 "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."
42 )]
43 InlineArgCountMismatch {
44 op_id: String,
46 expected: usize,
48 got: usize,
50 },
51
52 #[error(
54 "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."
55 )]
56 InlineNoOutput {
57 op_id: String,
59 },
60
61 #[error(
63 "IR inlining found {got} declared output buffers for operation `{op_id}`. Fix: mark exactly one result buffer with BufferDecl::output(...)."
64 )]
65 InlineOutputCountMismatch {
66 op_id: String,
68 got: usize,
70 },
71
72 #[error("IR validation rejected the Program: {issues:?}")]
74 Validation {
75 issues: Vec<crate::validate::ValidationError>,
77 },
78
79 #[error(
81 "Wire-format validation failed: {message}. Fix: recompile the frontend program set and ensure the compiler only emits valid instructions."
82 )]
83 WireFormatValidation {
84 message: String,
86 },
87
88 #[error(
90 "vyre target-text lowering: {message}. Fix: inspect the Program shape, backend capability report, and emitted shader diagnostics before retrying."
91 )]
92 Lowering {
93 message: String,
95 },
96
97 #[error(
99 "Wire-format version mismatch: expected {expected}, found {found}. Fix: re-encode with a matching vyre version or upgrade this runtime."
100 )]
101 VersionMismatch {
102 expected: u32,
104 found: u32,
106 },
107
108 #[error(
110 "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."
111 )]
112 UnknownDialect {
113 name: String,
115 requested: String,
117 },
118
119 #[error(
121 "Unknown op `{op}` in dialect `{dialect}`. Fix: upgrade the runtime to a version that includes this op, or drop the op before encoding."
122 )]
123 UnknownOp {
124 dialect: String,
126 op: String,
128 },
129}
130
131impl IrError {
132 #[must_use]
134 pub fn lowering(message: impl Into<String>) -> Self {
135 Self::Lowering {
136 message: message.into(),
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn lowering_helper_contains_fix_hint() {
147 let err = IrError::lowering("buffer too large");
148 let msg = err.to_string();
149 assert!(msg.contains("buffer too large"));
150 assert!(msg.contains("Fix:"));
151 }
152
153 #[test]
154 fn inline_cycle_display() {
155 let err = IrError::InlineCycle {
156 op_id: "math::add".into(),
157 };
158 assert!(err.to_string().contains("math::add"));
159 assert!(err.to_string().contains("cycle"));
160 }
161
162 #[test]
163 fn version_mismatch_display() {
164 let err = IrError::VersionMismatch {
165 expected: 6,
166 found: 5,
167 };
168 let msg = err.to_string();
169 assert!(msg.contains("6"));
170 assert!(msg.contains("5"));
171 }
172
173 #[test]
174 fn unknown_dialect_display() {
175 let err = IrError::UnknownDialect {
176 name: "my-dialect".into(),
177 requested: "1.0".into(),
178 };
179 assert!(err.to_string().contains("my-dialect"));
180 }
181
182 #[test]
183 fn error_is_clone_and_eq() {
184 let a = IrError::lowering("test");
185 let b = a.clone();
186 assert_eq!(a, b);
187 }
188
189 #[test]
190 fn inline_arg_count_mismatch_display() {
191 let err = IrError::InlineArgCountMismatch {
192 op_id: "test::op".into(),
193 expected: 3,
194 got: 1,
195 };
196 let msg = err.to_string();
197 assert!(msg.contains("expected 3"));
198 assert!(msg.contains("got 1"));
199 }
200}