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