1use alloc::{format, string::String};
2
3use super::validate_atomic_compare_exchange_struct;
4use super::{
5 analyzer::{UniformityDisruptor, UniformityRequirements},
6 ExpressionError, FunctionInfo, ModuleInfo,
7};
8use crate::arena::{Arena, UniqueArena};
9use crate::arena::{Handle, HandleSet};
10use crate::proc::TypeResolution;
11use crate::span::WithSpan;
12use crate::span::{AddSpan as _, MapErrWithSpan as _};
13
14#[derive(Clone, Debug, thiserror::Error)]
15#[cfg_attr(test, derive(PartialEq))]
16pub enum CallError {
17 #[error("Argument {index} expression is invalid")]
18 Argument {
19 index: usize,
20 source: ExpressionError,
21 },
22 #[error("Result expression {0:?} has already been introduced earlier")]
23 ResultAlreadyInScope(Handle<crate::Expression>),
24 #[error("Result expression {0:?} is populated by multiple `Call` statements")]
25 ResultAlreadyPopulated(Handle<crate::Expression>),
26 #[error("Requires {required} arguments, but {seen} are provided")]
27 ArgumentCount { required: usize, seen: usize },
28 #[error("Argument {index} value {seen_expression:?} doesn't match the type {required:?}")]
29 ArgumentType {
30 index: usize,
31 required: Handle<crate::Type>,
32 seen_expression: Handle<crate::Expression>,
33 },
34 #[error("The emitted expression doesn't match the call")]
35 ExpressionMismatch(Option<Handle<crate::Expression>>),
36}
37
38#[derive(Clone, Debug, thiserror::Error)]
39#[cfg_attr(test, derive(PartialEq))]
40pub enum AtomicError {
41 #[error("Pointer {0:?} to atomic is invalid.")]
42 InvalidPointer(Handle<crate::Expression>),
43 #[error("Address space {0:?} is not supported.")]
44 InvalidAddressSpace(crate::AddressSpace),
45 #[error("Operand {0:?} has invalid type.")]
46 InvalidOperand(Handle<crate::Expression>),
47 #[error("Operator {0:?} is not supported.")]
48 InvalidOperator(crate::AtomicFunction),
49 #[error("Result expression {0:?} is not an `AtomicResult` expression")]
50 InvalidResultExpression(Handle<crate::Expression>),
51 #[error("Result expression {0:?} is marked as an `exchange`")]
52 ResultExpressionExchange(Handle<crate::Expression>),
53 #[error("Result expression {0:?} is not marked as an `exchange`")]
54 ResultExpressionNotExchange(Handle<crate::Expression>),
55 #[error("Result type for {0:?} doesn't match the statement")]
56 ResultTypeMismatch(Handle<crate::Expression>),
57 #[error("Exchange operations must return a value")]
58 MissingReturnValue,
59 #[error("Capability {0:?} is required")]
60 MissingCapability(super::Capabilities),
61 #[error("Result expression {0:?} is populated by multiple `Atomic` statements")]
62 ResultAlreadyPopulated(Handle<crate::Expression>),
63}
64
65#[derive(Clone, Debug, thiserror::Error)]
66#[cfg_attr(test, derive(PartialEq))]
67pub enum SubgroupError {
68 #[error("Operand {0:?} has invalid type.")]
69 InvalidOperand(Handle<crate::Expression>),
70 #[error("Result type for {0:?} doesn't match the statement")]
71 ResultTypeMismatch(Handle<crate::Expression>),
72 #[error("Support for subgroup operation {0:?} is required")]
73 UnsupportedOperation(super::SubgroupOperationSet),
74 #[error("Unknown operation")]
75 UnknownOperation,
76 #[error("Invocation ID must be a const-expression")]
77 InvalidInvocationIdExprType(Handle<crate::Expression>),
78}
79
80#[derive(Clone, Debug, thiserror::Error)]
81#[cfg_attr(test, derive(PartialEq))]
82pub enum LocalVariableError {
83 #[error("Local variable has a type {0:?} that can't be stored in a local variable.")]
84 InvalidType(Handle<crate::Type>),
85 #[error("Initializer doesn't match the variable type")]
86 InitializerType,
87 #[error("Initializer is not a const or override expression")]
88 NonConstOrOverrideInitializer,
89}
90
91#[derive(Clone, Debug, thiserror::Error)]
92#[cfg_attr(test, derive(PartialEq))]
93pub enum FunctionError {
94 #[error("Expression {handle:?} is invalid")]
95 Expression {
96 handle: Handle<crate::Expression>,
97 source: ExpressionError,
98 },
99 #[error("Expression {0:?} can't be introduced - it's already in scope")]
100 ExpressionAlreadyInScope(Handle<crate::Expression>),
101 #[error("Local variable {handle:?} '{name}' is invalid")]
102 LocalVariable {
103 handle: Handle<crate::LocalVariable>,
104 name: String,
105 source: LocalVariableError,
106 },
107 #[error("Argument '{name}' at index {index} has a type that can't be passed into functions.")]
108 InvalidArgumentType { index: usize, name: String },
109 #[error("The function's given return type cannot be returned from functions")]
110 NonConstructibleReturnType,
111 #[error("Argument '{name}' at index {index} is a pointer of space {space:?}, which can't be passed into functions.")]
112 InvalidArgumentPointerSpace {
113 index: usize,
114 name: String,
115 space: crate::AddressSpace,
116 },
117 #[error("The `break` is used outside of a `loop` or `switch` context")]
118 BreakOutsideOfLoopOrSwitch,
119 #[error("The `continue` is used outside of a `loop` context")]
120 ContinueOutsideOfLoop,
121 #[error("The `return` is called within a `continuing` block")]
122 InvalidReturnSpot,
123 #[error("The `return` expression {expression:?} does not match the declared return type {expected_ty:?}")]
124 InvalidReturnType {
125 expression: Option<Handle<crate::Expression>>,
126 expected_ty: Option<Handle<crate::Type>>,
127 },
128 #[error("The `if` condition {0:?} is not a boolean scalar")]
129 InvalidIfType(Handle<crate::Expression>),
130 #[error("The `switch` value {0:?} is not an integer scalar")]
131 InvalidSwitchType(Handle<crate::Expression>),
132 #[error("Multiple `switch` cases for {0:?} are present")]
133 ConflictingSwitchCase(crate::SwitchValue),
134 #[error("The `switch` contains cases with conflicting types")]
135 ConflictingCaseType,
136 #[error("The `switch` is missing a `default` case")]
137 MissingDefaultCase,
138 #[error("Multiple `default` cases are present")]
139 MultipleDefaultCases,
140 #[error("The last `switch` case contains a `fallthrough`")]
141 LastCaseFallTrough,
142 #[error("The pointer {0:?} doesn't relate to a valid destination for a store")]
143 InvalidStorePointer(Handle<crate::Expression>),
144 #[error("Image store texture parameter type mismatch")]
145 InvalidStoreTexture {
146 actual: Handle<crate::Expression>,
147 actual_ty: crate::TypeInner,
148 },
149 #[error("Image store value parameter type mismatch")]
150 InvalidStoreValue {
151 actual: Handle<crate::Expression>,
152 actual_ty: crate::TypeInner,
153 expected_ty: crate::TypeInner,
154 },
155 #[error("The type of {value:?} doesn't match the type stored in {pointer:?}")]
156 InvalidStoreTypes {
157 pointer: Handle<crate::Expression>,
158 value: Handle<crate::Expression>,
159 },
160 #[error("Image store parameters are invalid")]
161 InvalidImageStore(#[source] ExpressionError),
162 #[error("Image atomic parameters are invalid")]
163 InvalidImageAtomic(#[source] ExpressionError),
164 #[error("Image atomic function is invalid")]
165 InvalidImageAtomicFunction(crate::AtomicFunction),
166 #[error("Image atomic value is invalid")]
167 InvalidImageAtomicValue(Handle<crate::Expression>),
168 #[error("Call to {function:?} is invalid")]
169 InvalidCall {
170 function: Handle<crate::Function>,
171 #[source]
172 error: CallError,
173 },
174 #[error("Atomic operation is invalid")]
175 InvalidAtomic(#[from] AtomicError),
176 #[error("Ray Query {0:?} is not a local variable")]
177 InvalidRayQueryExpression(Handle<crate::Expression>),
178 #[error("Acceleration structure {0:?} is not a matching expression")]
179 InvalidAccelerationStructure(Handle<crate::Expression>),
180 #[error(
181 "Acceleration structure {0:?} is missing flag vertex_return while Ray Query {1:?} does"
182 )]
183 MissingAccelerationStructureVertexReturn(Handle<crate::Expression>, Handle<crate::Expression>),
184 #[error("Ray Query {0:?} is missing flag vertex_return")]
185 MissingRayQueryVertexReturn(Handle<crate::Expression>),
186 #[error("Ray descriptor {0:?} is not a matching expression")]
187 InvalidRayDescriptor(Handle<crate::Expression>),
188 #[error("Ray Query {0:?} does not have a matching type")]
189 InvalidRayQueryType(Handle<crate::Type>),
190 #[error("Hit distance {0:?} must be an f32")]
191 InvalidHitDistanceType(Handle<crate::Expression>),
192 #[error("Shader requires capability {0:?}")]
193 MissingCapability(super::Capabilities),
194 #[error(
195 "Required uniformity of control flow for {0:?} in {1:?} is not fulfilled because of {2:?}"
196 )]
197 NonUniformControlFlow(
198 UniformityRequirements,
199 Handle<crate::Expression>,
200 UniformityDisruptor,
201 ),
202 #[error("Functions that are not entry points cannot have `@location` or `@builtin` attributes on their arguments: \"{name}\" has attributes")]
203 PipelineInputRegularFunction { name: String },
204 #[error("Functions that are not entry points cannot have `@location` or `@builtin` attributes on their return value types")]
205 PipelineOutputRegularFunction,
206 #[error("Required uniformity for WorkGroupUniformLoad is not fulfilled because of {0:?}")]
207 NonUniformWorkgroupUniformLoad(UniformityDisruptor),
209 #[error("The expression {0:?} for a WorkGroupUniformLoad isn't a WorkgroupUniformLoadResult")]
211 WorkgroupUniformLoadExpressionMismatch(Handle<crate::Expression>),
212 #[error("The expression {0:?} is not valid as a WorkGroupUniformLoad argument. It should be a Pointer in Workgroup address space")]
213 WorkgroupUniformLoadInvalidPointer(Handle<crate::Expression>),
214 #[error("Subgroup operation is invalid")]
215 InvalidSubgroup(#[from] SubgroupError),
216 #[error("Emit statement should not cover \"result\" expressions like {0:?}")]
217 EmitResult(Handle<crate::Expression>),
218 #[error("Expression not visited by the appropriate statement")]
219 UnvisitedExpression(Handle<crate::Expression>),
220 #[error("Expression {0:?} in mesh shader intrinsic call should be `u32` (is the expression a signed integer?)")]
221 InvalidMeshFunctionCall(Handle<crate::Expression>),
222 #[error("Mesh output types differ from {0:?} to {1:?}")]
223 ConflictingMeshOutputTypes(Handle<crate::Expression>, Handle<crate::Expression>),
224 #[error("Task payload variables differ from {0:?} to {1:?}")]
225 ConflictingTaskPayloadVariables(Handle<crate::Expression>, Handle<crate::Expression>),
226 #[error("Mesh shader output at {0:?} is not a user-defined struct")]
227 InvalidMeshShaderOutputType(Handle<crate::Expression>),
228}
229
230bitflags::bitflags! {
231 #[repr(transparent)]
232 #[derive(Clone, Copy)]
233 struct ControlFlowAbility: u8 {
234 const RETURN = 0x1;
236 const BREAK = 0x2;
238 const CONTINUE = 0x4;
240 }
241}
242
243struct BlockInfo {
244 stages: super::ShaderStages,
245}
246
247struct BlockContext<'a> {
248 abilities: ControlFlowAbility,
249 info: &'a FunctionInfo,
250 expressions: &'a Arena<crate::Expression>,
251 types: &'a UniqueArena<crate::Type>,
252 local_vars: &'a Arena<crate::LocalVariable>,
253 global_vars: &'a Arena<crate::GlobalVariable>,
254 functions: &'a Arena<crate::Function>,
255 special_types: &'a crate::SpecialTypes,
256 prev_infos: &'a [FunctionInfo],
257 return_type: Option<Handle<crate::Type>>,
258 local_expr_kind: &'a crate::proc::ExpressionKindTracker,
259}
260
261impl<'a> BlockContext<'a> {
262 fn new(
263 fun: &'a crate::Function,
264 module: &'a crate::Module,
265 info: &'a FunctionInfo,
266 prev_infos: &'a [FunctionInfo],
267 local_expr_kind: &'a crate::proc::ExpressionKindTracker,
268 ) -> Self {
269 Self {
270 abilities: ControlFlowAbility::RETURN,
271 info,
272 expressions: &fun.expressions,
273 types: &module.types,
274 local_vars: &fun.local_variables,
275 global_vars: &module.global_variables,
276 functions: &module.functions,
277 special_types: &module.special_types,
278 prev_infos,
279 return_type: fun.result.as_ref().map(|fr| fr.ty),
280 local_expr_kind,
281 }
282 }
283
284 const fn with_abilities(&self, abilities: ControlFlowAbility) -> Self {
285 BlockContext { abilities, ..*self }
286 }
287
288 fn get_expression(&self, handle: Handle<crate::Expression>) -> &'a crate::Expression {
289 &self.expressions[handle]
290 }
291
292 fn resolve_type_impl(
293 &self,
294 handle: Handle<crate::Expression>,
295 valid_expressions: &HandleSet<crate::Expression>,
296 ) -> Result<&TypeResolution, WithSpan<ExpressionError>> {
297 if !valid_expressions.contains(handle) {
298 Err(ExpressionError::NotInScope.with_span_handle(handle, self.expressions))
299 } else {
300 Ok(&self.info[handle].ty)
301 }
302 }
303
304 fn resolve_type(
305 &self,
306 handle: Handle<crate::Expression>,
307 valid_expressions: &HandleSet<crate::Expression>,
308 ) -> Result<&TypeResolution, WithSpan<FunctionError>> {
309 self.resolve_type_impl(handle, valid_expressions)
310 .map_err_inner(|source| FunctionError::Expression { handle, source }.with_span())
311 }
312
313 fn resolve_type_inner(
314 &self,
315 handle: Handle<crate::Expression>,
316 valid_expressions: &HandleSet<crate::Expression>,
317 ) -> Result<&crate::TypeInner, WithSpan<FunctionError>> {
318 self.resolve_type(handle, valid_expressions)
319 .map(|tr| tr.inner_with(self.types))
320 }
321
322 fn resolve_pointer_type(&self, handle: Handle<crate::Expression>) -> &crate::TypeInner {
323 self.info[handle].ty.inner_with(self.types)
324 }
325
326 fn compare_types(&self, lhs: &TypeResolution, rhs: &TypeResolution) -> bool {
327 crate::proc::compare_types(lhs, rhs, self.types)
328 }
329}
330
331impl super::Validator {
332 fn validate_call(
333 &mut self,
334 function: Handle<crate::Function>,
335 arguments: &[Handle<crate::Expression>],
336 result: Option<Handle<crate::Expression>>,
337 context: &BlockContext,
338 ) -> Result<super::ShaderStages, WithSpan<CallError>> {
339 let fun = &context.functions[function];
340 if fun.arguments.len() != arguments.len() {
341 return Err(CallError::ArgumentCount {
342 required: fun.arguments.len(),
343 seen: arguments.len(),
344 }
345 .with_span());
346 }
347 for (index, (arg, &expr)) in fun.arguments.iter().zip(arguments).enumerate() {
348 let ty = context
349 .resolve_type_impl(expr, &self.valid_expression_set)
350 .map_err_inner(|source| {
351 CallError::Argument { index, source }
352 .with_span_handle(expr, context.expressions)
353 })?;
354 if !context.compare_types(&TypeResolution::Handle(arg.ty), ty) {
355 return Err(CallError::ArgumentType {
356 index,
357 required: arg.ty,
358 seen_expression: expr,
359 }
360 .with_span_handle(expr, context.expressions));
361 }
362 }
363
364 if let Some(expr) = result {
365 if self.valid_expression_set.insert(expr) {
366 self.valid_expression_list.push(expr);
367 } else {
368 return Err(CallError::ResultAlreadyInScope(expr)
369 .with_span_handle(expr, context.expressions));
370 }
371 match context.expressions[expr] {
372 crate::Expression::CallResult(callee)
373 if fun.result.is_some() && callee == function =>
374 {
375 if !self.needs_visit.remove(expr) {
376 return Err(CallError::ResultAlreadyPopulated(expr)
377 .with_span_handle(expr, context.expressions));
378 }
379 }
380 _ => {
381 return Err(CallError::ExpressionMismatch(result)
382 .with_span_handle(expr, context.expressions))
383 }
384 }
385 } else if fun.result.is_some() {
386 return Err(CallError::ExpressionMismatch(result).with_span());
387 }
388
389 let callee_info = &context.prev_infos[function.index()];
390 Ok(callee_info.available_stages)
391 }
392
393 fn emit_expression(
394 &mut self,
395 handle: Handle<crate::Expression>,
396 context: &BlockContext,
397 ) -> Result<(), WithSpan<FunctionError>> {
398 if self.valid_expression_set.insert(handle) {
399 self.valid_expression_list.push(handle);
400 Ok(())
401 } else {
402 Err(FunctionError::ExpressionAlreadyInScope(handle)
403 .with_span_handle(handle, context.expressions))
404 }
405 }
406
407 fn validate_atomic(
408 &mut self,
409 pointer: Handle<crate::Expression>,
410 fun: &crate::AtomicFunction,
411 value: Handle<crate::Expression>,
412 result: Option<Handle<crate::Expression>>,
413 span: crate::Span,
414 context: &BlockContext,
415 ) -> Result<(), WithSpan<FunctionError>> {
416 let pointer_inner = context.resolve_type_inner(pointer, &self.valid_expression_set)?;
418 let crate::TypeInner::Pointer {
419 base: pointer_base,
420 space: pointer_space,
421 } = *pointer_inner
422 else {
423 log::error!("Atomic operation on type {:?}", *pointer_inner);
424 return Err(AtomicError::InvalidPointer(pointer)
425 .with_span_handle(pointer, context.expressions)
426 .into_other());
427 };
428 let crate::TypeInner::Atomic(pointer_scalar) = context.types[pointer_base].inner else {
429 log::error!(
430 "Atomic pointer to type {:?}",
431 context.types[pointer_base].inner
432 );
433 return Err(AtomicError::InvalidPointer(pointer)
434 .with_span_handle(pointer, context.expressions)
435 .into_other());
436 };
437
438 let value_inner = context.resolve_type_inner(value, &self.valid_expression_set)?;
440 let crate::TypeInner::Scalar(value_scalar) = *value_inner else {
441 log::error!("Atomic operand type {:?}", *value_inner);
442 return Err(AtomicError::InvalidOperand(value)
443 .with_span_handle(value, context.expressions)
444 .into_other());
445 };
446 if pointer_scalar != value_scalar {
447 log::error!("Atomic operand type {:?}", *value_inner);
448 return Err(AtomicError::InvalidOperand(value)
449 .with_span_handle(value, context.expressions)
450 .into_other());
451 }
452
453 match pointer_scalar {
454 crate::Scalar::I64 | crate::Scalar::U64 => {
460 if self
463 .capabilities
464 .contains(super::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS)
465 {
466 } else {
468 if matches!(
471 *fun,
472 crate::AtomicFunction::Min | crate::AtomicFunction::Max
473 ) && matches!(pointer_space, crate::AddressSpace::Storage { .. })
474 && result.is_none()
475 {
476 if !self
477 .capabilities
478 .contains(super::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX)
479 {
480 log::error!("Int64 min-max atomic operations are not supported");
481 return Err(AtomicError::MissingCapability(
482 super::Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,
483 )
484 .with_span_handle(value, context.expressions)
485 .into_other());
486 }
487 } else {
488 log::error!("Int64 atomic operations are not supported");
490 return Err(AtomicError::MissingCapability(
491 super::Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,
492 )
493 .with_span_handle(value, context.expressions)
494 .into_other());
495 }
496 }
497 }
498 crate::Scalar::F32 => {
500 if !self
504 .capabilities
505 .contains(super::Capabilities::SHADER_FLOAT32_ATOMIC)
506 {
507 log::error!("Float32 atomic operations are not supported");
508 return Err(AtomicError::MissingCapability(
509 super::Capabilities::SHADER_FLOAT32_ATOMIC,
510 )
511 .with_span_handle(value, context.expressions)
512 .into_other());
513 }
514 if !matches!(
515 *fun,
516 crate::AtomicFunction::Add
517 | crate::AtomicFunction::Subtract
518 | crate::AtomicFunction::Exchange { compare: None }
519 ) {
520 log::error!("Float32 atomic operation {fun:?} is not supported");
521 return Err(AtomicError::InvalidOperator(*fun)
522 .with_span_handle(value, context.expressions)
523 .into_other());
524 }
525 if !matches!(pointer_space, crate::AddressSpace::Storage { .. }) {
526 log::error!(
527 "Float32 atomic operations are only supported in the Storage address space"
528 );
529 return Err(AtomicError::InvalidAddressSpace(pointer_space)
530 .with_span_handle(value, context.expressions)
531 .into_other());
532 }
533 }
534 _ => {}
535 }
536
537 match result {
539 Some(result) => {
540 let crate::Expression::AtomicResult {
542 ty: result_ty,
543 comparison,
544 } = context.expressions[result]
545 else {
546 return Err(AtomicError::InvalidResultExpression(result)
547 .with_span_handle(result, context.expressions)
548 .into_other());
549 };
550
551 if !self.needs_visit.remove(result) {
554 return Err(AtomicError::ResultAlreadyPopulated(result)
555 .with_span_handle(result, context.expressions)
556 .into_other());
557 }
558
559 if let crate::AtomicFunction::Exchange {
561 compare: Some(compare),
562 } = *fun
563 {
564 let compare_inner =
567 context.resolve_type_inner(compare, &self.valid_expression_set)?;
568 if !compare_inner.non_struct_equivalent(value_inner, context.types) {
569 log::error!(
570 "Atomic exchange comparison has a different type from the value"
571 );
572 return Err(AtomicError::InvalidOperand(compare)
573 .with_span_handle(compare, context.expressions)
574 .into_other());
575 }
576
577 let crate::TypeInner::Struct { ref members, .. } =
581 context.types[result_ty].inner
582 else {
583 return Err(AtomicError::ResultTypeMismatch(result)
584 .with_span_handle(result, context.expressions)
585 .into_other());
586 };
587 if !validate_atomic_compare_exchange_struct(
588 context.types,
589 members,
590 |ty: &crate::TypeInner| *ty == crate::TypeInner::Scalar(pointer_scalar),
591 ) {
592 return Err(AtomicError::ResultTypeMismatch(result)
593 .with_span_handle(result, context.expressions)
594 .into_other());
595 }
596
597 if !comparison {
599 return Err(AtomicError::ResultExpressionNotExchange(result)
600 .with_span_handle(result, context.expressions)
601 .into_other());
602 }
603 } else {
604 let result_inner = &context.types[result_ty].inner;
607 if !result_inner.non_struct_equivalent(value_inner, context.types) {
608 return Err(AtomicError::ResultTypeMismatch(result)
609 .with_span_handle(result, context.expressions)
610 .into_other());
611 }
612
613 if comparison {
615 return Err(AtomicError::ResultExpressionExchange(result)
616 .with_span_handle(result, context.expressions)
617 .into_other());
618 }
619 }
620 self.emit_expression(result, context)?;
621 }
622
623 None => {
624 if let crate::AtomicFunction::Exchange { compare: None } = *fun {
626 log::error!("Atomic exchange's value is unused");
627 return Err(AtomicError::MissingReturnValue
628 .with_span_static(span, "atomic exchange operation")
629 .into_other());
630 }
631 }
632 }
633
634 Ok(())
635 }
636 fn validate_subgroup_operation(
637 &mut self,
638 op: &crate::SubgroupOperation,
639 collective_op: &crate::CollectiveOperation,
640 argument: Handle<crate::Expression>,
641 result: Handle<crate::Expression>,
642 context: &BlockContext,
643 ) -> Result<(), WithSpan<FunctionError>> {
644 let argument_inner = context.resolve_type_inner(argument, &self.valid_expression_set)?;
645
646 let (is_scalar, scalar) = match *argument_inner {
647 crate::TypeInner::Scalar(scalar) => (true, scalar),
648 crate::TypeInner::Vector { scalar, .. } => (false, scalar),
649 _ => {
650 log::error!("Subgroup operand type {argument_inner:?}");
651 return Err(SubgroupError::InvalidOperand(argument)
652 .with_span_handle(argument, context.expressions)
653 .into_other());
654 }
655 };
656
657 use crate::ScalarKind as sk;
658 use crate::SubgroupOperation as sg;
659 match (scalar.kind, *op) {
660 (sk::Bool, sg::All | sg::Any) if is_scalar => {}
661 (sk::Sint | sk::Uint | sk::Float, sg::Add | sg::Mul | sg::Min | sg::Max) => {}
662 (sk::Sint | sk::Uint, sg::And | sg::Or | sg::Xor) => {}
663
664 (_, _) => {
665 log::error!("Subgroup operand type {argument_inner:?}");
666 return Err(SubgroupError::InvalidOperand(argument)
667 .with_span_handle(argument, context.expressions)
668 .into_other());
669 }
670 };
671
672 use crate::CollectiveOperation as co;
673 match (*collective_op, *op) {
674 (
675 co::Reduce,
676 sg::All
677 | sg::Any
678 | sg::Add
679 | sg::Mul
680 | sg::Min
681 | sg::Max
682 | sg::And
683 | sg::Or
684 | sg::Xor,
685 ) => {}
686 (co::InclusiveScan | co::ExclusiveScan, sg::Add | sg::Mul) => {}
687
688 (_, _) => {
689 return Err(SubgroupError::UnknownOperation.with_span().into_other());
690 }
691 };
692
693 self.emit_expression(result, context)?;
694 match context.expressions[result] {
695 crate::Expression::SubgroupOperationResult { ty }
696 if { &context.types[ty].inner == argument_inner } => {}
697 _ => {
698 return Err(SubgroupError::ResultTypeMismatch(result)
699 .with_span_handle(result, context.expressions)
700 .into_other())
701 }
702 }
703 Ok(())
704 }
705 fn validate_subgroup_gather(
706 &mut self,
707 mode: &crate::GatherMode,
708 argument: Handle<crate::Expression>,
709 result: Handle<crate::Expression>,
710 context: &BlockContext,
711 ) -> Result<(), WithSpan<FunctionError>> {
712 match *mode {
713 crate::GatherMode::BroadcastFirst => {}
714 crate::GatherMode::Broadcast(index)
715 | crate::GatherMode::Shuffle(index)
716 | crate::GatherMode::ShuffleDown(index)
717 | crate::GatherMode::ShuffleUp(index)
718 | crate::GatherMode::ShuffleXor(index)
719 | crate::GatherMode::QuadBroadcast(index) => {
720 let index_ty = context.resolve_type_inner(index, &self.valid_expression_set)?;
721 match *index_ty {
722 crate::TypeInner::Scalar(crate::Scalar::U32) => {}
723 _ => {
724 log::error!(
725 "Subgroup gather index type {index_ty:?}, expected unsigned int"
726 );
727 return Err(SubgroupError::InvalidOperand(argument)
728 .with_span_handle(index, context.expressions)
729 .into_other());
730 }
731 }
732 }
733 crate::GatherMode::QuadSwap(_) => {}
734 }
735 match *mode {
736 crate::GatherMode::Broadcast(index) | crate::GatherMode::QuadBroadcast(index) => {
737 if !context.local_expr_kind.is_const(index) {
738 return Err(SubgroupError::InvalidInvocationIdExprType(index)
739 .with_span_handle(index, context.expressions)
740 .into_other());
741 }
742 }
743 _ => {}
744 }
745 let argument_inner = context.resolve_type_inner(argument, &self.valid_expression_set)?;
746 if !matches!(*argument_inner,
747 crate::TypeInner::Scalar ( scalar, .. ) | crate::TypeInner::Vector { scalar, .. }
748 if matches!(scalar.kind, crate::ScalarKind::Uint | crate::ScalarKind::Sint | crate::ScalarKind::Float)
749 ) {
750 log::error!("Subgroup gather operand type {argument_inner:?}");
751 return Err(SubgroupError::InvalidOperand(argument)
752 .with_span_handle(argument, context.expressions)
753 .into_other());
754 }
755
756 self.emit_expression(result, context)?;
757 match context.expressions[result] {
758 crate::Expression::SubgroupOperationResult { ty }
759 if { &context.types[ty].inner == argument_inner } => {}
760 _ => {
761 return Err(SubgroupError::ResultTypeMismatch(result)
762 .with_span_handle(result, context.expressions)
763 .into_other())
764 }
765 }
766 Ok(())
767 }
768
769 fn validate_block_impl(
770 &mut self,
771 statements: &crate::Block,
772 context: &BlockContext,
773 ) -> Result<BlockInfo, WithSpan<FunctionError>> {
774 use crate::{AddressSpace, Statement as S, TypeInner as Ti};
775 let mut stages = super::ShaderStages::all();
776 for (statement, &span) in statements.span_iter() {
777 match *statement {
778 S::Emit(ref range) => {
779 for handle in range.clone() {
780 use crate::Expression as Ex;
781 match context.expressions[handle] {
782 Ex::Literal(_)
783 | Ex::Constant(_)
784 | Ex::Override(_)
785 | Ex::ZeroValue(_)
786 | Ex::Compose { .. }
787 | Ex::Access { .. }
788 | Ex::AccessIndex { .. }
789 | Ex::Splat { .. }
790 | Ex::Swizzle { .. }
791 | Ex::FunctionArgument(_)
792 | Ex::GlobalVariable(_)
793 | Ex::LocalVariable(_)
794 | Ex::Load { .. }
795 | Ex::ImageSample { .. }
796 | Ex::ImageLoad { .. }
797 | Ex::ImageQuery { .. }
798 | Ex::Unary { .. }
799 | Ex::Binary { .. }
800 | Ex::Select { .. }
801 | Ex::Derivative { .. }
802 | Ex::Relational { .. }
803 | Ex::Math { .. }
804 | Ex::As { .. }
805 | Ex::ArrayLength(_)
806 | Ex::RayQueryGetIntersection { .. }
807 | Ex::RayQueryVertexPositions { .. } => {
808 self.emit_expression(handle, context)?
809 }
810 Ex::CallResult(_)
811 | Ex::AtomicResult { .. }
812 | Ex::WorkGroupUniformLoadResult { .. }
813 | Ex::RayQueryProceedResult
814 | Ex::SubgroupBallotResult
815 | Ex::SubgroupOperationResult { .. } => {
816 return Err(FunctionError::EmitResult(handle)
817 .with_span_handle(handle, context.expressions));
818 }
819 }
820 }
821 }
822 S::Block(ref block) => {
823 let info = self.validate_block(block, context)?;
824 stages &= info.stages;
825 }
826 S::If {
827 condition,
828 ref accept,
829 ref reject,
830 } => {
831 match *context.resolve_type_inner(condition, &self.valid_expression_set)? {
832 Ti::Scalar(crate::Scalar {
833 kind: crate::ScalarKind::Bool,
834 width: _,
835 }) => {}
836 _ => {
837 return Err(FunctionError::InvalidIfType(condition)
838 .with_span_handle(condition, context.expressions))
839 }
840 }
841 stages &= self.validate_block(accept, context)?.stages;
842 stages &= self.validate_block(reject, context)?.stages;
843 }
844 S::Switch {
845 selector,
846 ref cases,
847 } => {
848 let uint = match context
849 .resolve_type_inner(selector, &self.valid_expression_set)?
850 .scalar_kind()
851 {
852 Some(crate::ScalarKind::Uint) => true,
853 Some(crate::ScalarKind::Sint) => false,
854 _ => {
855 return Err(FunctionError::InvalidSwitchType(selector)
856 .with_span_handle(selector, context.expressions))
857 }
858 };
859 self.switch_values.clear();
860 for case in cases {
861 match case.value {
862 crate::SwitchValue::I32(_) if !uint => {}
863 crate::SwitchValue::U32(_) if uint => {}
864 crate::SwitchValue::Default => {}
865 _ => {
866 return Err(FunctionError::ConflictingCaseType.with_span_static(
867 case.body
868 .span_iter()
869 .next()
870 .map_or(Default::default(), |(_, s)| *s),
871 "conflicting switch arm here",
872 ));
873 }
874 };
875 if !self.switch_values.insert(case.value) {
876 return Err(match case.value {
877 crate::SwitchValue::Default => FunctionError::MultipleDefaultCases
878 .with_span_static(
879 case.body
880 .span_iter()
881 .next()
882 .map_or(Default::default(), |(_, s)| *s),
883 "duplicated switch arm here",
884 ),
885 _ => FunctionError::ConflictingSwitchCase(case.value)
886 .with_span_static(
887 case.body
888 .span_iter()
889 .next()
890 .map_or(Default::default(), |(_, s)| *s),
891 "conflicting switch arm here",
892 ),
893 });
894 }
895 }
896 if !self.switch_values.contains(&crate::SwitchValue::Default) {
897 return Err(FunctionError::MissingDefaultCase
898 .with_span_static(span, "missing default case"));
899 }
900 if let Some(case) = cases.last() {
901 if case.fall_through {
902 return Err(FunctionError::LastCaseFallTrough.with_span_static(
903 case.body
904 .span_iter()
905 .next()
906 .map_or(Default::default(), |(_, s)| *s),
907 "bad switch arm here",
908 ));
909 }
910 }
911 let pass_through_abilities = context.abilities
912 & (ControlFlowAbility::RETURN | ControlFlowAbility::CONTINUE);
913 let sub_context =
914 context.with_abilities(pass_through_abilities | ControlFlowAbility::BREAK);
915 for case in cases {
916 stages &= self.validate_block(&case.body, &sub_context)?.stages;
917 }
918 }
919 S::Loop {
920 ref body,
921 ref continuing,
922 break_if,
923 } => {
924 let base_expression_count = self.valid_expression_list.len();
927 let pass_through_abilities = context.abilities & ControlFlowAbility::RETURN;
928 stages &= self
929 .validate_block_impl(
930 body,
931 &context.with_abilities(
932 pass_through_abilities
933 | ControlFlowAbility::BREAK
934 | ControlFlowAbility::CONTINUE,
935 ),
936 )?
937 .stages;
938 stages &= self
939 .validate_block_impl(
940 continuing,
941 &context.with_abilities(ControlFlowAbility::empty()),
942 )?
943 .stages;
944
945 if let Some(condition) = break_if {
946 match *context.resolve_type_inner(condition, &self.valid_expression_set)? {
947 Ti::Scalar(crate::Scalar {
948 kind: crate::ScalarKind::Bool,
949 width: _,
950 }) => {}
951 _ => {
952 return Err(FunctionError::InvalidIfType(condition)
953 .with_span_handle(condition, context.expressions))
954 }
955 }
956 }
957
958 for handle in self.valid_expression_list.drain(base_expression_count..) {
959 self.valid_expression_set.remove(handle);
960 }
961 }
962 S::Break => {
963 if !context.abilities.contains(ControlFlowAbility::BREAK) {
964 return Err(FunctionError::BreakOutsideOfLoopOrSwitch
965 .with_span_static(span, "invalid break"));
966 }
967 }
968 S::Continue => {
969 if !context.abilities.contains(ControlFlowAbility::CONTINUE) {
970 return Err(FunctionError::ContinueOutsideOfLoop
971 .with_span_static(span, "invalid continue"));
972 }
973 }
974 S::Return { value } => {
975 if !context.abilities.contains(ControlFlowAbility::RETURN) {
976 return Err(FunctionError::InvalidReturnSpot
977 .with_span_static(span, "invalid return"));
978 }
979 let value_ty = value
980 .map(|expr| context.resolve_type(expr, &self.valid_expression_set))
981 .transpose()?;
982 let okay = match (value_ty, context.return_type) {
985 (None, None) => true,
986 (Some(value_inner), Some(expected_ty)) => {
987 context.compare_types(value_inner, &TypeResolution::Handle(expected_ty))
988 }
989 (_, _) => false,
990 };
991
992 if !okay {
993 log::error!(
994 "Returning {:?} where {:?} is expected",
995 value_ty,
996 context.return_type,
997 );
998 if let Some(handle) = value {
999 return Err(FunctionError::InvalidReturnType {
1000 expression: value,
1001 expected_ty: context.return_type,
1002 }
1003 .with_span_handle(handle, context.expressions));
1004 } else {
1005 return Err(FunctionError::InvalidReturnType {
1006 expression: value,
1007 expected_ty: context.return_type,
1008 }
1009 .with_span_static(span, "invalid return"));
1010 }
1011 }
1012 }
1013 S::Kill => {
1014 stages &= super::ShaderStages::FRAGMENT;
1015 }
1016 S::ControlBarrier(barrier) | S::MemoryBarrier(barrier) => {
1017 stages &= super::ShaderStages::COMPUTE;
1018 if barrier.contains(crate::Barrier::SUB_GROUP) {
1019 if !self.capabilities.contains(
1020 super::Capabilities::SUBGROUP | super::Capabilities::SUBGROUP_BARRIER,
1021 ) {
1022 return Err(FunctionError::MissingCapability(
1023 super::Capabilities::SUBGROUP
1024 | super::Capabilities::SUBGROUP_BARRIER,
1025 )
1026 .with_span_static(span, "missing capability for this operation"));
1027 }
1028 if !self
1029 .subgroup_operations
1030 .contains(super::SubgroupOperationSet::BASIC)
1031 {
1032 return Err(FunctionError::InvalidSubgroup(
1033 SubgroupError::UnsupportedOperation(
1034 super::SubgroupOperationSet::BASIC,
1035 ),
1036 )
1037 .with_span_static(span, "support for this operation is not present"));
1038 }
1039 }
1040 }
1041 S::Store { pointer, value } => {
1042 let mut current = pointer;
1043 loop {
1044 match context.expressions[current] {
1045 crate::Expression::Access { base, .. }
1046 | crate::Expression::AccessIndex { base, .. } => current = base,
1047 crate::Expression::LocalVariable(_)
1048 | crate::Expression::GlobalVariable(_)
1049 | crate::Expression::FunctionArgument(_) => break,
1050 _ => {
1051 return Err(FunctionError::InvalidStorePointer(current)
1052 .with_span_handle(pointer, context.expressions))
1053 }
1054 }
1055 }
1056
1057 let value_tr = context.resolve_type(value, &self.valid_expression_set)?;
1058 let value_ty = value_tr.inner_with(context.types);
1059 match *value_ty {
1060 Ti::Image { .. } | Ti::Sampler { .. } => {
1061 return Err(FunctionError::InvalidStoreTexture {
1062 actual: value,
1063 actual_ty: value_ty.clone(),
1064 }
1065 .with_span_context((
1066 context.expressions.get_span(value),
1067 format!("this value is of type {value_ty:?}"),
1068 ))
1069 .with_span(span, "expects a texture argument"));
1070 }
1071 _ => {}
1072 }
1073
1074 let pointer_ty = context.resolve_pointer_type(pointer);
1075 let pointer_base_tr = pointer_ty.pointer_base_type();
1076 let pointer_base_ty = pointer_base_tr
1077 .as_ref()
1078 .map(|ty| ty.inner_with(context.types));
1079 let good = if let Some(&Ti::Atomic(ref scalar)) = pointer_base_ty {
1080 *value_ty == Ti::Scalar(*scalar)
1082 } else if let Some(tr) = pointer_base_tr {
1083 context.compare_types(value_tr, &tr)
1084 } else {
1085 false
1086 };
1087
1088 if !good {
1089 return Err(FunctionError::InvalidStoreTypes { pointer, value }
1090 .with_span()
1091 .with_handle(pointer, context.expressions)
1092 .with_handle(value, context.expressions));
1093 }
1094
1095 if let Some(space) = pointer_ty.pointer_space() {
1096 if !space.access().contains(crate::StorageAccess::STORE) {
1097 return Err(FunctionError::InvalidStorePointer(pointer)
1098 .with_span_static(
1099 context.expressions.get_span(pointer),
1100 "writing to this location is not permitted",
1101 ));
1102 }
1103 }
1104 }
1105 S::ImageStore {
1106 image,
1107 coordinate,
1108 array_index,
1109 value,
1110 } => {
1111 let global_var;
1114 let image_ty;
1115 match *context.get_expression(image) {
1116 crate::Expression::GlobalVariable(var_handle) => {
1117 global_var = &context.global_vars[var_handle];
1118 image_ty = global_var.ty;
1119 }
1120 crate::Expression::Access { base, .. }
1124 | crate::Expression::AccessIndex { base, .. } => {
1125 let crate::Expression::GlobalVariable(var_handle) =
1126 *context.get_expression(base)
1127 else {
1128 return Err(FunctionError::InvalidImageStore(
1129 ExpressionError::ExpectedGlobalVariable,
1130 )
1131 .with_span_handle(image, context.expressions));
1132 };
1133 global_var = &context.global_vars[var_handle];
1134
1135 let Ti::BindingArray { base, .. } = context.types[global_var.ty].inner
1137 else {
1138 return Err(FunctionError::InvalidImageStore(
1139 ExpressionError::ExpectedBindingArrayType(global_var.ty),
1140 )
1141 .with_span_handle(global_var.ty, context.types));
1142 };
1143
1144 image_ty = base;
1145 }
1146 _ => {
1147 return Err(FunctionError::InvalidImageStore(
1148 ExpressionError::ExpectedGlobalVariable,
1149 )
1150 .with_span_handle(image, context.expressions))
1151 }
1152 };
1153
1154 let Ti::Image {
1156 class,
1157 arrayed,
1158 dim,
1159 } = context.types[image_ty].inner
1160 else {
1161 return Err(FunctionError::InvalidImageStore(
1162 ExpressionError::ExpectedImageType(global_var.ty),
1163 )
1164 .with_span()
1165 .with_handle(global_var.ty, context.types)
1166 .with_handle(image, context.expressions));
1167 };
1168
1169 let crate::ImageClass::Storage { format, .. } = class else {
1171 return Err(FunctionError::InvalidImageStore(
1172 ExpressionError::InvalidImageClass(class),
1173 )
1174 .with_span_handle(image, context.expressions));
1175 };
1176
1177 if context
1179 .resolve_type_inner(coordinate, &self.valid_expression_set)?
1180 .image_storage_coordinates()
1181 .is_none_or(|coord_dim| coord_dim != dim)
1182 {
1183 return Err(FunctionError::InvalidImageStore(
1184 ExpressionError::InvalidImageCoordinateType(dim, coordinate),
1185 )
1186 .with_span_handle(coordinate, context.expressions));
1187 }
1188
1189 if arrayed != array_index.is_some() {
1192 return Err(FunctionError::InvalidImageStore(
1193 ExpressionError::InvalidImageArrayIndex,
1194 )
1195 .with_span_handle(coordinate, context.expressions));
1196 }
1197
1198 if let Some(expr) = array_index {
1200 if !matches!(
1201 *context.resolve_type_inner(expr, &self.valid_expression_set)?,
1202 Ti::Scalar(crate::Scalar {
1203 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
1204 width: _,
1205 })
1206 ) {
1207 return Err(FunctionError::InvalidImageStore(
1208 ExpressionError::InvalidImageArrayIndexType(expr),
1209 )
1210 .with_span_handle(expr, context.expressions));
1211 }
1212 }
1213
1214 let value_ty = crate::TypeInner::Vector {
1215 size: crate::VectorSize::Quad,
1216 scalar: format.into(),
1217 };
1218
1219 let actual_value_ty =
1222 context.resolve_type_inner(value, &self.valid_expression_set)?;
1223 if actual_value_ty != &value_ty {
1224 return Err(FunctionError::InvalidStoreValue {
1225 actual: value,
1226 actual_ty: actual_value_ty.clone(),
1227 expected_ty: value_ty.clone(),
1228 }
1229 .with_span_context((
1230 context.expressions.get_span(value),
1231 format!("this value is of type {actual_value_ty:?}"),
1232 ))
1233 .with_span(
1234 span,
1235 format!("expects a value argument of type {value_ty:?}"),
1236 ));
1237 }
1238 }
1239 S::Call {
1240 function,
1241 ref arguments,
1242 result,
1243 } => match self.validate_call(function, arguments, result, context) {
1244 Ok(callee_stages) => stages &= callee_stages,
1245 Err(error) => {
1246 return Err(error.and_then(|error| {
1247 FunctionError::InvalidCall { function, error }
1248 .with_span_static(span, "invalid function call")
1249 }))
1250 }
1251 },
1252 S::Atomic {
1253 pointer,
1254 ref fun,
1255 value,
1256 result,
1257 } => {
1258 self.validate_atomic(pointer, fun, value, result, span, context)?;
1259 }
1260 S::ImageAtomic {
1261 image,
1262 coordinate,
1263 array_index,
1264 fun,
1265 value,
1266 } => {
1267 let var = match *context.get_expression(image) {
1268 crate::Expression::GlobalVariable(var_handle) => {
1269 &context.global_vars[var_handle]
1270 }
1271 crate::Expression::Access { base, .. }
1273 | crate::Expression::AccessIndex { base, .. } => {
1274 match *context.get_expression(base) {
1275 crate::Expression::GlobalVariable(var_handle) => {
1276 &context.global_vars[var_handle]
1277 }
1278 _ => {
1279 return Err(FunctionError::InvalidImageAtomic(
1280 ExpressionError::ExpectedGlobalVariable,
1281 )
1282 .with_span_handle(image, context.expressions))
1283 }
1284 }
1285 }
1286 _ => {
1287 return Err(FunctionError::InvalidImageAtomic(
1288 ExpressionError::ExpectedGlobalVariable,
1289 )
1290 .with_span_handle(image, context.expressions))
1291 }
1292 };
1293
1294 let global_ty = match context.types[var.ty].inner {
1296 Ti::BindingArray { base, .. } => &context.types[base].inner,
1297 ref inner => inner,
1298 };
1299
1300 let value_ty = match *global_ty {
1301 Ti::Image {
1302 class,
1303 arrayed,
1304 dim,
1305 } => {
1306 match context
1307 .resolve_type_inner(coordinate, &self.valid_expression_set)?
1308 .image_storage_coordinates()
1309 {
1310 Some(coord_dim) if coord_dim == dim => {}
1311 _ => {
1312 return Err(FunctionError::InvalidImageAtomic(
1313 ExpressionError::InvalidImageCoordinateType(
1314 dim, coordinate,
1315 ),
1316 )
1317 .with_span_handle(coordinate, context.expressions));
1318 }
1319 };
1320 if arrayed != array_index.is_some() {
1321 return Err(FunctionError::InvalidImageAtomic(
1322 ExpressionError::InvalidImageArrayIndex,
1323 )
1324 .with_span_handle(coordinate, context.expressions));
1325 }
1326 if let Some(expr) = array_index {
1327 match *context
1328 .resolve_type_inner(expr, &self.valid_expression_set)?
1329 {
1330 Ti::Scalar(crate::Scalar {
1331 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
1332 width: _,
1333 }) => {}
1334 _ => {
1335 return Err(FunctionError::InvalidImageAtomic(
1336 ExpressionError::InvalidImageArrayIndexType(expr),
1337 )
1338 .with_span_handle(expr, context.expressions));
1339 }
1340 }
1341 }
1342 match class {
1343 crate::ImageClass::Storage { format, access } => {
1344 if !access.contains(crate::StorageAccess::ATOMIC) {
1345 return Err(FunctionError::InvalidImageAtomic(
1346 ExpressionError::InvalidImageStorageAccess(access),
1347 )
1348 .with_span_handle(image, context.expressions));
1349 }
1350 match format {
1351 crate::StorageFormat::R64Uint => {
1352 if !self.capabilities.intersects(
1353 super::Capabilities::TEXTURE_INT64_ATOMIC,
1354 ) {
1355 return Err(FunctionError::MissingCapability(
1356 super::Capabilities::TEXTURE_INT64_ATOMIC,
1357 )
1358 .with_span_static(
1359 span,
1360 "missing capability for this operation",
1361 ));
1362 }
1363 match fun {
1364 crate::AtomicFunction::Min
1365 | crate::AtomicFunction::Max => {}
1366 _ => {
1367 return Err(
1368 FunctionError::InvalidImageAtomicFunction(
1369 fun,
1370 )
1371 .with_span_handle(
1372 image,
1373 context.expressions,
1374 ),
1375 );
1376 }
1377 }
1378 }
1379 crate::StorageFormat::R32Sint
1380 | crate::StorageFormat::R32Uint => {
1381 if !self
1382 .capabilities
1383 .intersects(super::Capabilities::TEXTURE_ATOMIC)
1384 {
1385 return Err(FunctionError::MissingCapability(
1386 super::Capabilities::TEXTURE_ATOMIC,
1387 )
1388 .with_span_static(
1389 span,
1390 "missing capability for this operation",
1391 ));
1392 }
1393 match fun {
1394 crate::AtomicFunction::Add
1395 | crate::AtomicFunction::And
1396 | crate::AtomicFunction::ExclusiveOr
1397 | crate::AtomicFunction::InclusiveOr
1398 | crate::AtomicFunction::Min
1399 | crate::AtomicFunction::Max => {}
1400 _ => {
1401 return Err(
1402 FunctionError::InvalidImageAtomicFunction(
1403 fun,
1404 )
1405 .with_span_handle(
1406 image,
1407 context.expressions,
1408 ),
1409 );
1410 }
1411 }
1412 }
1413 _ => {
1414 return Err(FunctionError::InvalidImageAtomic(
1415 ExpressionError::InvalidImageFormat(format),
1416 )
1417 .with_span_handle(image, context.expressions));
1418 }
1419 }
1420 crate::TypeInner::Scalar(format.into())
1421 }
1422 _ => {
1423 return Err(FunctionError::InvalidImageAtomic(
1424 ExpressionError::InvalidImageClass(class),
1425 )
1426 .with_span_handle(image, context.expressions));
1427 }
1428 }
1429 }
1430 _ => {
1431 return Err(FunctionError::InvalidImageAtomic(
1432 ExpressionError::ExpectedImageType(var.ty),
1433 )
1434 .with_span()
1435 .with_handle(var.ty, context.types)
1436 .with_handle(image, context.expressions))
1437 }
1438 };
1439
1440 if *context.resolve_type_inner(value, &self.valid_expression_set)? != value_ty {
1441 return Err(FunctionError::InvalidImageAtomicValue(value)
1442 .with_span_handle(value, context.expressions));
1443 }
1444 }
1445 S::WorkGroupUniformLoad { pointer, result } => {
1446 stages &= super::ShaderStages::COMPUTE;
1447 let pointer_inner =
1448 context.resolve_type_inner(pointer, &self.valid_expression_set)?;
1449 match *pointer_inner {
1450 Ti::Pointer {
1451 space: AddressSpace::WorkGroup,
1452 ..
1453 } => {}
1454 Ti::ValuePointer {
1455 space: AddressSpace::WorkGroup,
1456 ..
1457 } => {}
1458 _ => {
1459 return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)
1460 .with_span_static(span, "WorkGroupUniformLoad"))
1461 }
1462 }
1463 self.emit_expression(result, context)?;
1464 let ty = match &context.expressions[result] {
1465 &crate::Expression::WorkGroupUniformLoadResult { ty } => ty,
1466 _ => {
1467 return Err(FunctionError::WorkgroupUniformLoadExpressionMismatch(
1468 result,
1469 )
1470 .with_span_static(span, "WorkGroupUniformLoad"));
1471 }
1472 };
1473 let expected_pointer_inner = Ti::Pointer {
1474 base: ty,
1475 space: AddressSpace::WorkGroup,
1476 };
1477 if !expected_pointer_inner.non_struct_equivalent(pointer_inner, context.types) {
1478 return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)
1479 .with_span_static(span, "WorkGroupUniformLoad"));
1480 }
1481 }
1482 S::RayQuery { query, ref fun } => {
1483 let query_var = match *context.get_expression(query) {
1484 crate::Expression::LocalVariable(var) => &context.local_vars[var],
1485 ref other => {
1486 log::error!("Unexpected ray query expression {other:?}");
1487 return Err(FunctionError::InvalidRayQueryExpression(query)
1488 .with_span_static(span, "invalid query expression"));
1489 }
1490 };
1491 let rq_vertex_return = match context.types[query_var.ty].inner {
1492 Ti::RayQuery { vertex_return } => vertex_return,
1493 ref other => {
1494 log::error!("Unexpected ray query type {other:?}");
1495 return Err(FunctionError::InvalidRayQueryType(query_var.ty)
1496 .with_span_static(span, "invalid query type"));
1497 }
1498 };
1499 match *fun {
1500 crate::RayQueryFunction::Initialize {
1501 acceleration_structure,
1502 descriptor,
1503 } => {
1504 match *context.resolve_type_inner(
1505 acceleration_structure,
1506 &self.valid_expression_set,
1507 )? {
1508 Ti::AccelerationStructure { vertex_return } => {
1509 if (!vertex_return) && rq_vertex_return {
1510 return Err(FunctionError::MissingAccelerationStructureVertexReturn(acceleration_structure, query).with_span_static(span, "invalid acceleration structure"));
1511 }
1512 }
1513 _ => {
1514 return Err(FunctionError::InvalidAccelerationStructure(
1515 acceleration_structure,
1516 )
1517 .with_span_static(span, "invalid acceleration structure"))
1518 }
1519 }
1520 let desc_ty_given = context
1521 .resolve_type_inner(descriptor, &self.valid_expression_set)?;
1522 let desc_ty_expected = context
1523 .special_types
1524 .ray_desc
1525 .map(|handle| &context.types[handle].inner);
1526 if Some(desc_ty_given) != desc_ty_expected {
1527 return Err(FunctionError::InvalidRayDescriptor(descriptor)
1528 .with_span_static(span, "invalid ray descriptor"));
1529 }
1530 }
1531 crate::RayQueryFunction::Proceed { result } => {
1532 self.emit_expression(result, context)?;
1533 }
1534 crate::RayQueryFunction::GenerateIntersection { hit_t } => {
1535 match *context.resolve_type_inner(hit_t, &self.valid_expression_set)? {
1536 Ti::Scalar(crate::Scalar {
1537 kind: crate::ScalarKind::Float,
1538 width: _,
1539 }) => {}
1540 _ => {
1541 return Err(FunctionError::InvalidHitDistanceType(hit_t)
1542 .with_span_static(span, "invalid hit_t"))
1543 }
1544 }
1545 }
1546 crate::RayQueryFunction::ConfirmIntersection => {}
1547 crate::RayQueryFunction::Terminate => {}
1548 }
1549 }
1550 S::MeshFunction(func) => {
1551 let ensure_u32 =
1552 |expr: Handle<crate::Expression>| -> Result<(), WithSpan<FunctionError>> {
1553 let u32_ty = TypeResolution::Value(Ti::Scalar(crate::Scalar::U32));
1554 let ty = context
1555 .resolve_type_impl(expr, &self.valid_expression_set)
1556 .map_err_inner(|source| {
1557 FunctionError::Expression {
1558 source,
1559 handle: expr,
1560 }
1561 .with_span_handle(expr, context.expressions)
1562 })?;
1563 if !context.compare_types(&u32_ty, ty) {
1564 return Err(FunctionError::InvalidMeshFunctionCall(expr)
1565 .with_span_handle(expr, context.expressions));
1566 }
1567 Ok(())
1568 };
1569 match func {
1570 crate::MeshFunction::SetMeshOutputs {
1571 vertex_count,
1572 primitive_count,
1573 } => {
1574 ensure_u32(vertex_count)?;
1575 ensure_u32(primitive_count)?;
1576 }
1577 crate::MeshFunction::SetVertex { index, value: _ }
1578 | crate::MeshFunction::SetPrimitive { index, value: _ } => {
1579 ensure_u32(index)?;
1580 }
1583 }
1584 }
1585 S::SubgroupBallot { result, predicate } => {
1586 stages &= self.subgroup_stages;
1587 if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1588 return Err(FunctionError::MissingCapability(
1589 super::Capabilities::SUBGROUP,
1590 )
1591 .with_span_static(span, "missing capability for this operation"));
1592 }
1593 if !self
1594 .subgroup_operations
1595 .contains(super::SubgroupOperationSet::BALLOT)
1596 {
1597 return Err(FunctionError::InvalidSubgroup(
1598 SubgroupError::UnsupportedOperation(
1599 super::SubgroupOperationSet::BALLOT,
1600 ),
1601 )
1602 .with_span_static(span, "support for this operation is not present"));
1603 }
1604 if let Some(predicate) = predicate {
1605 let predicate_inner =
1606 context.resolve_type_inner(predicate, &self.valid_expression_set)?;
1607 if !matches!(
1608 *predicate_inner,
1609 crate::TypeInner::Scalar(crate::Scalar::BOOL,)
1610 ) {
1611 log::error!(
1612 "Subgroup ballot predicate type {predicate_inner:?} expected bool"
1613 );
1614 return Err(SubgroupError::InvalidOperand(predicate)
1615 .with_span_handle(predicate, context.expressions)
1616 .into_other());
1617 }
1618 }
1619 self.emit_expression(result, context)?;
1620 }
1621 S::SubgroupCollectiveOperation {
1622 ref op,
1623 ref collective_op,
1624 argument,
1625 result,
1626 } => {
1627 stages &= self.subgroup_stages;
1628 if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1629 return Err(FunctionError::MissingCapability(
1630 super::Capabilities::SUBGROUP,
1631 )
1632 .with_span_static(span, "missing capability for this operation"));
1633 }
1634 let operation = op.required_operations();
1635 if !self.subgroup_operations.contains(operation) {
1636 return Err(FunctionError::InvalidSubgroup(
1637 SubgroupError::UnsupportedOperation(operation),
1638 )
1639 .with_span_static(span, "support for this operation is not present"));
1640 }
1641 self.validate_subgroup_operation(op, collective_op, argument, result, context)?;
1642 }
1643 S::SubgroupGather {
1644 ref mode,
1645 argument,
1646 result,
1647 } => {
1648 stages &= self.subgroup_stages;
1649 if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1650 return Err(FunctionError::MissingCapability(
1651 super::Capabilities::SUBGROUP,
1652 )
1653 .with_span_static(span, "missing capability for this operation"));
1654 }
1655 let operation = mode.required_operations();
1656 if !self.subgroup_operations.contains(operation) {
1657 return Err(FunctionError::InvalidSubgroup(
1658 SubgroupError::UnsupportedOperation(operation),
1659 )
1660 .with_span_static(span, "support for this operation is not present"));
1661 }
1662 self.validate_subgroup_gather(mode, argument, result, context)?;
1663 }
1664 }
1665 }
1666 Ok(BlockInfo { stages })
1667 }
1668
1669 fn validate_block(
1670 &mut self,
1671 statements: &crate::Block,
1672 context: &BlockContext,
1673 ) -> Result<BlockInfo, WithSpan<FunctionError>> {
1674 let base_expression_count = self.valid_expression_list.len();
1675 let info = self.validate_block_impl(statements, context)?;
1676 for handle in self.valid_expression_list.drain(base_expression_count..) {
1677 self.valid_expression_set.remove(handle);
1678 }
1679 Ok(info)
1680 }
1681
1682 fn validate_local_var(
1683 &self,
1684 var: &crate::LocalVariable,
1685 gctx: crate::proc::GlobalCtx,
1686 fun_info: &FunctionInfo,
1687 local_expr_kind: &crate::proc::ExpressionKindTracker,
1688 ) -> Result<(), LocalVariableError> {
1689 log::debug!("var {var:?}");
1690 let type_info = self
1691 .types
1692 .get(var.ty.index())
1693 .ok_or(LocalVariableError::InvalidType(var.ty))?;
1694 if !type_info.flags.contains(super::TypeFlags::CONSTRUCTIBLE) {
1695 return Err(LocalVariableError::InvalidType(var.ty));
1696 }
1697
1698 if let Some(init) = var.init {
1699 if !gctx.compare_types(&TypeResolution::Handle(var.ty), &fun_info[init].ty) {
1700 return Err(LocalVariableError::InitializerType);
1701 }
1702
1703 if !local_expr_kind.is_const_or_override(init) {
1704 return Err(LocalVariableError::NonConstOrOverrideInitializer);
1705 }
1706 }
1707
1708 Ok(())
1709 }
1710
1711 pub(super) fn validate_function(
1712 &mut self,
1713 fun: &crate::Function,
1714 module: &crate::Module,
1715 mod_info: &ModuleInfo,
1716 entry_point: bool,
1717 ) -> Result<FunctionInfo, WithSpan<FunctionError>> {
1718 let mut info = mod_info.process_function(fun, module, self.flags, self.capabilities)?;
1719
1720 let local_expr_kind = crate::proc::ExpressionKindTracker::from_arena(&fun.expressions);
1721
1722 for (var_handle, var) in fun.local_variables.iter() {
1723 self.validate_local_var(var, module.to_ctx(), &info, &local_expr_kind)
1724 .map_err(|source| {
1725 FunctionError::LocalVariable {
1726 handle: var_handle,
1727 name: var.name.clone().unwrap_or_default(),
1728 source,
1729 }
1730 .with_span_handle(var.ty, &module.types)
1731 .with_handle(var_handle, &fun.local_variables)
1732 })?;
1733 }
1734
1735 for (index, argument) in fun.arguments.iter().enumerate() {
1736 match module.types[argument.ty].inner.pointer_space() {
1737 Some(crate::AddressSpace::Private | crate::AddressSpace::Function) | None => {}
1738 Some(other) => {
1739 return Err(FunctionError::InvalidArgumentPointerSpace {
1740 index,
1741 name: argument.name.clone().unwrap_or_default(),
1742 space: other,
1743 }
1744 .with_span_handle(argument.ty, &module.types))
1745 }
1746 }
1747 if !self.types[argument.ty.index()]
1749 .flags
1750 .contains(super::TypeFlags::ARGUMENT)
1751 {
1752 return Err(FunctionError::InvalidArgumentType {
1753 index,
1754 name: argument.name.clone().unwrap_or_default(),
1755 }
1756 .with_span_handle(argument.ty, &module.types));
1757 }
1758
1759 if !entry_point && argument.binding.is_some() {
1760 return Err(FunctionError::PipelineInputRegularFunction {
1761 name: argument.name.clone().unwrap_or_default(),
1762 }
1763 .with_span_handle(argument.ty, &module.types));
1764 }
1765 }
1766
1767 if let Some(ref result) = fun.result {
1768 if !self.types[result.ty.index()]
1769 .flags
1770 .contains(super::TypeFlags::CONSTRUCTIBLE)
1771 {
1772 return Err(FunctionError::NonConstructibleReturnType
1773 .with_span_handle(result.ty, &module.types));
1774 }
1775
1776 if !entry_point && result.binding.is_some() {
1777 return Err(FunctionError::PipelineOutputRegularFunction
1778 .with_span_handle(result.ty, &module.types));
1779 }
1780 }
1781
1782 self.valid_expression_set.clear_for_arena(&fun.expressions);
1783 self.valid_expression_list.clear();
1784 self.needs_visit.clear_for_arena(&fun.expressions);
1785 for (handle, expr) in fun.expressions.iter() {
1786 if expr.needs_pre_emit() {
1787 self.valid_expression_set.insert(handle);
1788 }
1789 if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {
1790 if let crate::Expression::CallResult(_) | crate::Expression::AtomicResult { .. } =
1793 *expr
1794 {
1795 self.needs_visit.insert(handle);
1796 }
1797
1798 match self.validate_expression(
1799 handle,
1800 expr,
1801 fun,
1802 module,
1803 &info,
1804 mod_info,
1805 &local_expr_kind,
1806 ) {
1807 Ok(stages) => info.available_stages &= stages,
1808 Err(source) => {
1809 return Err(FunctionError::Expression { handle, source }
1810 .with_span_handle(handle, &fun.expressions))
1811 }
1812 }
1813 }
1814 }
1815
1816 if self.flags.contains(super::ValidationFlags::BLOCKS) {
1817 let stages = self
1818 .validate_block(
1819 &fun.body,
1820 &BlockContext::new(fun, module, &info, &mod_info.functions, &local_expr_kind),
1821 )?
1822 .stages;
1823 info.available_stages &= stages;
1824
1825 if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {
1826 if let Some(handle) = self.needs_visit.iter().next() {
1827 return Err(FunctionError::UnvisitedExpression(handle)
1828 .with_span_handle(handle, &fun.expressions));
1829 }
1830 }
1831 }
1832 Ok(info)
1833 }
1834}