Skip to content

Commit a9ce6c6

Browse files
committed
Add rich error handling for additional validation errors
Add rich error handlers with helpful hints for: - MissingInstructionCapability / MissingOperandCapability - MissingDescriptorSetDecoration / MissingBindingDecoration - InvalidBlockLayout - InvalidBuiltInType These handlers provide user-friendly rustc diagnostics with source spans (when available) and actionable hints for fixing common issues.
1 parent 28ce612 commit a9ce6c6

1 file changed

Lines changed: 160 additions & 1 deletion

File tree

crates/rustc_codegen_spirv/src/validation_err.rs

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use std::path::Path;
77

88
use rspirv::dr::Module;
9-
use rspirv::spirv::{Decoration, Op};
9+
use rspirv::spirv::{BuiltIn, Capability, Decoration, Op};
1010
use rustc_session::Session;
1111
use spirv_tools::ValidationError;
1212

@@ -62,6 +62,42 @@ impl<'a> ValidationErrorContext<'a> {
6262
} => {
6363
self.emit_not_a_logical_pointer(*instruction, u32::from(*pointer), *source_opcode);
6464
}
65+
ValidationError::MissingInstructionCapability {
66+
opcode,
67+
required_capability,
68+
} => {
69+
self.emit_missing_capability(*opcode, *required_capability, None);
70+
}
71+
ValidationError::MissingOperandCapability {
72+
opcode,
73+
operand_index,
74+
required_capability,
75+
} => {
76+
self.emit_missing_capability(*opcode, *required_capability, Some(*operand_index));
77+
}
78+
ValidationError::MissingDescriptorSetDecoration { variable } => {
79+
self.emit_missing_decoration(
80+
u32::from(*variable),
81+
"DescriptorSet",
82+
"#[spirv(descriptor_set = N)]",
83+
);
84+
}
85+
ValidationError::MissingBindingDecoration { variable } => {
86+
self.emit_missing_decoration(
87+
u32::from(*variable),
88+
"Binding",
89+
"#[spirv(binding = N)]",
90+
);
91+
}
92+
ValidationError::InvalidBlockLayout {
93+
struct_type,
94+
reason,
95+
} => {
96+
self.emit_invalid_block_layout(u32::from(*struct_type), reason);
97+
}
98+
ValidationError::InvalidBuiltInType { builtin, expected } => {
99+
self.emit_invalid_builtin_type(*builtin, expected);
100+
}
65101
_ => {
66102
// Fall back to generic error message
67103
self.emit_generic_error(error);
@@ -144,6 +180,96 @@ impl<'a> ValidationErrorContext<'a> {
144180
}
145181
}
146182

183+
fn emit_invalid_block_layout(&mut self, struct_type_id: u32, reason: &str) {
184+
let span = self.id_to_span(struct_type_id);
185+
let type_name = self.get_name(struct_type_id);
186+
let type_name_fallback = format!("%{}", struct_type_id);
187+
let type_name_display = type_name.as_deref().unwrap_or(&type_name_fallback);
188+
189+
let message = format!(
190+
"struct `{}` has invalid block layout: {}",
191+
type_name_display, reason
192+
);
193+
194+
let mut err = if let Some(span) = span {
195+
self.sess.dcx().struct_span_err(span, message)
196+
} else {
197+
self.sess.dcx().struct_err(message)
198+
};
199+
200+
err.help("ensure struct members are properly aligned according to std140/std430 layout rules");
201+
err.note(format!("module `{}`", self.filename.display()));
202+
err.emit();
203+
}
204+
205+
fn emit_invalid_builtin_type(&self, builtin: BuiltIn, expected: &str) {
206+
let mut err = self.sess.dcx().struct_err(format!(
207+
"BuiltIn {:?} has incorrect type, expected {}",
208+
builtin, expected
209+
));
210+
211+
err.help(format!(
212+
"variables with `#[spirv(builtin = \"{:?}\")]` must have the correct type",
213+
builtin
214+
));
215+
err.note(format!("module `{}`", self.filename.display()));
216+
err.emit();
217+
}
218+
219+
fn emit_missing_decoration(&mut self, var_id: u32, decoration_name: &str, hint: &str) {
220+
let span = self.id_to_span(var_id);
221+
let var_name = self.get_name(var_id);
222+
let var_name_fallback = format!("%{}", var_id);
223+
let var_name_display = var_name.as_deref().unwrap_or(&var_name_fallback);
224+
225+
let message = format!(
226+
"resource variable `{}` is missing {} decoration",
227+
var_name_display, decoration_name
228+
);
229+
230+
let mut err = if let Some(span) = span {
231+
self.sess.dcx().struct_span_err(span, message)
232+
} else {
233+
self.sess.dcx().struct_err(message)
234+
};
235+
236+
err.help(format!("add `{}` to the variable", hint));
237+
err.note(format!("module `{}`", self.filename.display()));
238+
err.emit();
239+
}
240+
241+
fn emit_missing_capability(
242+
&mut self,
243+
opcode: Op,
244+
capability: Capability,
245+
operand_index: Option<usize>,
246+
) {
247+
// Try to find an instruction with this opcode to get a span
248+
let span = self.find_instruction_span_by_opcode(opcode);
249+
250+
let message = if let Some(idx) = operand_index {
251+
format!(
252+
"operand {} of Op{:?} requires capability {:?}",
253+
idx, opcode, capability
254+
)
255+
} else {
256+
format!("Op{:?} requires capability {:?}", opcode, capability)
257+
};
258+
259+
let mut err = if let Some(span) = span {
260+
self.sess.dcx().struct_span_err(span, message)
261+
} else {
262+
self.sess.dcx().struct_err(message)
263+
};
264+
265+
err.help(format!(
266+
"add `#[spirv(capability({:?}))]` to your entry point function",
267+
capability
268+
));
269+
err.note(format!("module `{}`", self.filename.display()));
270+
err.emit();
271+
}
272+
147273
fn emit_not_a_logical_pointer(&mut self, instruction: Op, pointer_id: u32, source_opcode: Op) {
148274
// Try to find a useful span - first the pointer, then the instruction using it,
149275
// then trace back through the def chain
@@ -190,6 +316,39 @@ impl<'a> ValidationErrorContext<'a> {
190316
err.emit();
191317
}
192318

319+
/// Finds the first instruction with the given opcode and returns its span.
320+
fn find_instruction_span_by_opcode(&mut self, opcode: Op) -> Option<rustc_span::Span> {
321+
let m = self.module?;
322+
323+
// Search in functions first (most common case)
324+
for func in &m.functions {
325+
for block in &func.blocks {
326+
for inst in &block.instructions {
327+
if inst.class.opcode == opcode {
328+
if let Some(id) = inst.result_id {
329+
if let Some(span) = self.id_to_span(id) {
330+
return Some(span);
331+
}
332+
}
333+
}
334+
}
335+
}
336+
}
337+
338+
// Search in types/globals
339+
for inst in &m.types_global_values {
340+
if inst.class.opcode == opcode {
341+
if let Some(id) = inst.result_id {
342+
if let Some(span) = self.id_to_span(id) {
343+
return Some(span);
344+
}
345+
}
346+
}
347+
}
348+
349+
None
350+
}
351+
193352
/// Finds an instruction that uses the given pointer ID with the specified opcode.
194353
fn find_instruction_using_pointer(&self, pointer_id: u32, opcode: Op) -> Option<u32> {
195354
let m = self.module?;

0 commit comments

Comments
 (0)