Skip to main content

besl/vm/
error.rs

1//! Error types and diagnostics for VM compilation and execution.
2
3use super::ResourceSlot;
4
5#[derive(Debug, PartialEq, Eq)]
6pub enum VmError {
7	MissingMainFunction,
8	UnsupportedRawCode,
9	UnsupportedMainSignature {
10		message: String,
11	},
12	UnsupportedType {
13		type_name: String,
14	},
15	UnsupportedStatement {
16		message: String,
17	},
18	UnsupportedAssignmentTarget {
19		message: String,
20	},
21	UnsupportedExpression {
22		message: String,
23	},
24	UnsupportedBufferLayout {
25		message: String,
26	},
27	UnsupportedDescriptor {
28		slot: ResourceSlot,
29		message: String,
30	},
31	DescriptorAccessDenied {
32		slot: ResourceSlot,
33		access: &'static str,
34	},
35	DescriptorTypeMismatch {
36		slot: ResourceSlot,
37		expected: &'static str,
38		found: &'static str,
39	},
40	UnknownBufferMember {
41		member: String,
42	},
43	UnboundDescriptor {
44		slot: ResourceSlot,
45	},
46	MissingPushConstant,
47	MissingMeshOutputs,
48	MissingTaskOutputs,
49	MissingWorkgroupState,
50	UninitializedWorkgroupValue {
51		name: String,
52	},
53	MissingTaskPayload {
54		name: String,
55	},
56	TaskPayloadIndexOutOfBounds {
57		name: String,
58		index: usize,
59		count: usize,
60	},
61	TaskPayloadOutputIndexOutOfBounds {
62		name: String,
63		index: usize,
64		count: usize,
65	},
66	MeshOutputIndexOutOfBounds {
67		kind: &'static str,
68		index: usize,
69		count: usize,
70	},
71	MeshOutputCountLimitExceeded {
72		kind: &'static str,
73		requested: u32,
74		limit: u32,
75	},
76	TaskMeshOutputCountLimitExceeded {
77		requested: u32,
78		limit: u32,
79	},
80	DivergentWorkgroupBarrier {
81		lane: usize,
82		expected_instruction: usize,
83		found_instruction: Option<usize>,
84	},
85	MissingSpecialization {
86		name: String,
87	},
88	InstructionLimitExceeded {
89		limit: usize,
90	},
91	CallDepthLimitExceeded {
92		limit: usize,
93	},
94	CallArgumentMismatch {
95		expected: usize,
96		found: usize,
97	},
98	BufferAccessOutOfBounds {
99		offset: usize,
100		size: usize,
101		buffer_size: usize,
102	},
103	BufferArrayIndexOutOfBounds {
104		index: usize,
105		count: usize,
106	},
107	TextureAccessOutOfBounds {
108		x: u32,
109		y: u32,
110		z: u32,
111		width: u32,
112		height: u32,
113		depth: u32,
114	},
115	InvalidTextureDimensions {
116		width: u32,
117		height: u32,
118		depth: u32,
119	},
120	TextureTexelCountOverflow {
121		width: u32,
122		height: u32,
123		depth: u32,
124	},
125	TextureFormatMismatch {
126		expected: &'static str,
127		found: &'static str,
128	},
129	InvalidLiteral {
130		value: String,
131		value_type: String,
132	},
133	ArithmeticError {
134		message: String,
135	},
136	TypeMismatch {
137		expected: String,
138		found: String,
139	},
140	UninitializedRegister {
141		register: usize,
142	},
143	UninitializedLocal {
144		local: usize,
145	},
146}
147
148impl std::fmt::Display for VmError {
149	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150		match self {
151			VmError::MissingMainFunction => {
152				write!(
153					f,
154					"Missing main function. The most likely cause is that the lexed BESL program does not define `main`."
155				)
156			}
157			VmError::UnsupportedRawCode => write!(
158				f,
159				"Raw code blocks are not supported. The most likely cause is that a reachable BESL function contains non-empty platform shader code with no portable VM semantics."
160			),
161			VmError::UnsupportedMainSignature { message } => write!(
162				f,
163				"Unsupported main signature: {}. The most likely cause is that the VM only accepts `main: fn () -> void` right now.",
164				message
165			),
166			VmError::UnsupportedType { type_name } => write!(
167				f,
168				"Unsupported type `{}`. The most likely cause is that the BESL type has no portable VM value or resource representation.",
169				type_name
170			),
171			VmError::UnsupportedStatement { message } => write!(
172				f,
173				"Unsupported statement. {}. The most likely cause is that this statement form has not been lowered into VM control-flow or side-effect instructions.",
174				message
175			),
176			VmError::UnsupportedAssignmentTarget { message } => write!(
177				f,
178				"Unsupported assignment target. {}. The most likely cause is that the target is not a writable local, buffer member, output, or image operation.",
179				message
180			),
181			VmError::UnsupportedExpression { message } => write!(
182				f,
183				"Unsupported expression. {}. The most likely cause is that this BESL syntax or operand-type combination has no VM lowering yet.",
184				message
185			),
186			VmError::UnsupportedBufferLayout { message } => write!(
187				f,
188				"Unsupported buffer layout. {}. The most likely cause is that a member cannot be represented in the VM's packed CPU buffer layout.",
189				message
190			),
191			VmError::UnsupportedDescriptor { slot, message } => write!(
192				f,
193				"Unsupported resource at slot {}. {}. The most likely cause is a resource-kind mismatch, incompatible reused slot, or unsupported resource access.",
194				slot.slot(),
195				message
196			),
197			VmError::DescriptorAccessDenied { slot, access } => write!(
198				f,
199				"Resource access denied at slot {}. The most likely cause is that the BESL resource was not declared with `{}` access.",
200				slot.slot(),
201				access
202			),
203			VmError::DescriptorTypeMismatch { slot, expected, found } => write!(
204				f,
205				"Resource type mismatch at slot {}: expected `{}` but found `{}`. The most likely cause is that the host bound a different resource kind than the compiled BESL program requires.",
206				slot.slot(),
207				expected,
208				found
209			),
210			VmError::UnknownBufferMember { member } => write!(
211				f,
212				"Unknown buffer member `{}`. The most likely cause is that the BESL accessor does not match the bound buffer layout.",
213				member
214			),
215			VmError::UnboundDescriptor { slot } => write!(
216				f,
217				"Unbound resource at slot {}. The most likely cause is that no resource was bound into the slot before execution.",
218				slot.slot()
219			),
220			VmError::MissingPushConstant => write!(
221				f,
222				"Missing push constant binding. The most likely cause is that the BESL program reads `push_constant` but the host did not bind any push constant data before execution."
223			),
224			VmError::MissingMeshOutputs => write!(
225				f,
226				"Missing mesh output capture. The most likely cause is that the BESL mesh shader ran without binding `MeshOutputs`."
227			),
228			VmError::MissingTaskOutputs => write!(
229				f,
230				"Missing task output capture. The most likely cause is that the BESL task shader ran without binding `TaskOutputs`."
231			),
232			VmError::MissingWorkgroupState => write!(
233				f,
234				"Missing workgroup state. The most likely cause is that a BESL task shader accessed workgroup storage without binding `WorkgroupState`."
235			),
236			VmError::UninitializedWorkgroupValue { name } => write!(
237				f,
238				"Uninitialized workgroup value `{name}`. The most likely cause is that the BESL shader loaded workgroup storage before one invocation initialized it."
239			),
240			VmError::MissingTaskPayload { name } => write!(
241				f,
242				"Missing task payload `{name}`. The most likely cause is that the BESL mesh shader read a task-payload array that the host did not bind before execution."
243			),
244			VmError::TaskPayloadIndexOutOfBounds { name, index, count } => write!(
245				f,
246				"Task payload `{name}` index {index} exceeds {count} bound elements. The most likely cause is that the host supplied fewer task-payload values than the mesh shader reads."
247			),
248			VmError::TaskPayloadOutputIndexOutOfBounds { name, index, count } => write!(
249				f,
250				"Task payload output `{name}` index {index} exceeds {count} declared elements. The most likely cause is that the task shader wrote beyond its payload declaration."
251			),
252			VmError::MeshOutputIndexOutOfBounds { kind, index, count } => write!(
253				f,
254				"Mesh {kind} output index {index} exceeds {count} declared outputs. The most likely cause is that the shader wrote beyond the counts supplied to `set_mesh_output_counts`."
255			),
256			VmError::MeshOutputCountLimitExceeded {
257				kind,
258				requested,
259				limit,
260			} => write!(
261				f,
262				"Mesh {kind} output count {requested} exceeds the configured limit of {limit}. The most likely cause is that the shader requested more mesh output storage than the host allows."
263			),
264			VmError::TaskMeshOutputCountLimitExceeded { requested, limit } => write!(
265				f,
266				"Task mesh output count {requested} exceeds the configured limit of {limit}. The most likely cause is that the shader requested more mesh workgroups than the host allows."
267			),
268			VmError::DivergentWorkgroupBarrier {
269				lane,
270				expected_instruction,
271				found_instruction,
272			} => match found_instruction {
273				Some(found_instruction) => write!(
274					f,
275					"Divergent workgroup barrier in lane {lane}: expected instruction {expected_instruction} but found {found_instruction}. The most likely cause is that task invocations reached different barriers in the same synchronization phase."
276				),
277				None => write!(
278					f,
279					"Divergent workgroup barrier in lane {lane}: expected instruction {expected_instruction} but the lane completed. The most likely cause is that task control flow skipped a barrier reached by peer invocations."
280				),
281			},
282			VmError::MissingSpecialization { name } => write!(
283				f,
284				"Missing specialization `{}`. The most likely cause is that the host did not provide a value for a specialization used by the BESL program.",
285				name
286			),
287			VmError::InstructionLimitExceeded { limit } => write!(
288				f,
289				"VM instruction limit {} exceeded. The most likely cause is that the BESL program contains an unbounded loop or needs a larger explicit execution budget.",
290				limit
291			),
292			VmError::CallDepthLimitExceeded { limit } => write!(
293				f,
294				"VM call-depth limit {} exceeded. The most likely cause is that the BESL program recurses without reaching a base case.",
295				limit
296			),
297			VmError::CallArgumentMismatch { expected, found } => write!(
298				f,
299				"Function call argument mismatch: expected {} arguments but found {}. The most likely cause is that the BESL function call does not match the declared parameter list.",
300				expected, found
301			),
302			VmError::BufferAccessOutOfBounds {
303				offset,
304				size,
305				buffer_size,
306			} => write!(
307				f,
308				"Buffer access out of bounds at byte {} for {} bytes in a {} byte buffer. The most likely cause is that the bound buffer does not match the compiled BESL buffer layout.",
309				offset, size, buffer_size
310			),
311			VmError::BufferArrayIndexOutOfBounds { index, count } => write!(
312				f,
313				"Buffer array index {} is out of bounds for {} elements. The most likely cause is that the BESL program indexed a buffer array member outside its declared length.",
314				index, count
315			),
316			VmError::TextureAccessOutOfBounds {
317				x,
318				y,
319				z,
320				width,
321				height,
322				depth,
323			} => write!(
324				f,
325				"Texture access out of bounds at ({}, {}, {}) in a {}x{}x{} texture. The most likely cause is that the BESL program fetched a texel outside the bound texture dimensions.",
326				x, y, z, width, height, depth
327			),
328			VmError::InvalidTextureDimensions { width, height, depth } => write!(
329				f,
330				"Invalid texture dimensions {}x{}x{}. The most likely cause is that the host created a texture with a zero dimension.",
331				width, height, depth
332			),
333			VmError::TextureTexelCountOverflow { width, height, depth } => write!(
334				f,
335				"Texture dimensions {}x{}x{} are too large. The most likely cause is that their texel count exceeds addressable CPU memory.",
336				width, height, depth
337			),
338			VmError::TextureFormatMismatch { expected, found } => write!(
339				f,
340				"Texture format mismatch: expected `{}` but found `{}`. The most likely cause is that the same CPU texture was used for incompatible float and integer shader operations.",
341				expected, found
342			),
343			VmError::InvalidLiteral { value, value_type } => write!(
344				f,
345				"Invalid literal `{}` for `{}`. The most likely cause is that the literal cannot be parsed as the target BESL scalar type.",
346				value, value_type
347			),
348			VmError::ArithmeticError { message } => write!(
349				f,
350				"Invalid arithmetic operation. {}. The most likely cause is that the BESL program evaluated an unsupported numeric operation such as division or modulo by zero.",
351				message
352			),
353			VmError::TypeMismatch { expected, found } => write!(
354				f,
355				"Type mismatch: expected `{}` but found `{}`. The most likely cause is that the BESL assignment mixes incompatible scalar types.",
356				expected, found
357			),
358			VmError::UninitializedRegister { register } => write!(
359				f,
360				"Uninitialized register {}. The most likely cause is that the VM tried to use a register before any instruction wrote a value into it.",
361				register
362			),
363			VmError::UninitializedLocal { local } => write!(
364				f,
365				"Uninitialized local {}. The most likely cause is that the BESL program read a local variable before assigning a value to it.",
366				local
367			),
368		}
369	}
370}
371
372impl std::error::Error for VmError {}