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