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 fn validate_block_impl(
783 &mut self,
784 statements: &crate::Block,
785 context: &BlockContext,
786 ) -> Result<BlockInfo, WithSpan<FunctionError>> {
787 use crate::{AddressSpace, Statement as S, TypeInner as Ti};
788 let mut stages = super::ShaderStages::all();
789 for (statement, &span) in statements.span_iter() {
790 match *statement {
791 S::Emit(ref range) => {
792 for handle in range.clone() {
793 use crate::Expression as Ex;
794 match context.expressions[handle] {
795 Ex::Literal(_)
796 | Ex::Constant(_)
797 | Ex::Override(_)
798 | Ex::ZeroValue(_)
799 | Ex::Compose { .. }
800 | Ex::Access { .. }
801 | Ex::AccessIndex { .. }
802 | Ex::Splat { .. }
803 | Ex::Swizzle { .. }
804 | Ex::FunctionArgument(_)
805 | Ex::GlobalVariable(_)
806 | Ex::LocalVariable(_)
807 | Ex::Load { .. }
808 | Ex::ImageSample { .. }
809 | Ex::ImageLoad { .. }
810 | Ex::ImageQuery { .. }
811 | Ex::Unary { .. }
812 | Ex::Binary { .. }
813 | Ex::Select { .. }
814 | Ex::Derivative { .. }
815 | Ex::Relational { .. }
816 | Ex::Math { .. }
817 | Ex::As { .. }
818 | Ex::ArrayLength(_)
819 | Ex::RayQueryGetIntersection { .. }
820 | Ex::RayQueryVertexPositions { .. }
821 | Ex::CooperativeLoad { .. }
822 | Ex::CooperativeMultiplyAdd { .. } => {
823 self.emit_expression(handle, context)?
824 }
825 Ex::CallResult(_)
826 | Ex::AtomicResult { .. }
827 | Ex::WorkGroupUniformLoadResult { .. }
828 | Ex::RayQueryProceedResult
829 | Ex::SubgroupBallotResult
830 | Ex::SubgroupOperationResult { .. } => {
831 return Err(FunctionError::EmitResult(handle)
832 .with_span_handle(handle, context.expressions));
833 }
834 }
835 }
836 }
837 S::Block(ref block) => {
838 let info = self.validate_block(block, context)?;
839 stages &= info.stages;
840 }
841 S::If {
842 condition,
843 ref accept,
844 ref reject,
845 } => {
846 match *context.resolve_type_inner(condition, &self.valid_expression_set)? {
847 Ti::Scalar(crate::Scalar {
848 kind: crate::ScalarKind::Bool,
849 width: _,
850 }) => {}
851 _ => {
852 return Err(FunctionError::InvalidIfType(condition)
853 .with_span_handle(condition, context.expressions))
854 }
855 }
856 stages &= self.validate_block(accept, context)?.stages;
857 stages &= self.validate_block(reject, context)?.stages;
858 }
859 S::Switch {
860 selector,
861 ref cases,
862 } => {
863 let uint = match context
864 .resolve_type_inner(selector, &self.valid_expression_set)?
865 .scalar_kind()
866 {
867 Some(crate::ScalarKind::Uint) => true,
868 Some(crate::ScalarKind::Sint) => false,
869 _ => {
870 return Err(FunctionError::InvalidSwitchType(selector)
871 .with_span_handle(selector, context.expressions))
872 }
873 };
874 self.switch_values.clear();
875 for case in cases {
876 match case.value {
877 crate::SwitchValue::I32(_) if !uint => {}
878 crate::SwitchValue::U32(_) if uint => {}
879 crate::SwitchValue::Default => {}
880 _ => {
881 return Err(FunctionError::ConflictingCaseType.with_span_static(
882 case.body
883 .span_iter()
884 .next()
885 .map_or(Default::default(), |(_, s)| *s),
886 "conflicting switch arm here",
887 ));
888 }
889 };
890 if !self.switch_values.insert(case.value) {
891 return Err(match case.value {
892 crate::SwitchValue::Default => FunctionError::MultipleDefaultCases
893 .with_span_static(
894 case.body
895 .span_iter()
896 .next()
897 .map_or(Default::default(), |(_, s)| *s),
898 "duplicated switch arm here",
899 ),
900 _ => FunctionError::ConflictingSwitchCase(case.value)
901 .with_span_static(
902 case.body
903 .span_iter()
904 .next()
905 .map_or(Default::default(), |(_, s)| *s),
906 "conflicting switch arm here",
907 ),
908 });
909 }
910 }
911 if !self.switch_values.contains(&crate::SwitchValue::Default) {
912 return Err(FunctionError::MissingDefaultCase
913 .with_span_static(span, "missing default case"));
914 }
915 if let Some(case) = cases.last() {
916 if case.fall_through {
917 return Err(FunctionError::LastCaseFallTrough.with_span_static(
918 case.body
919 .span_iter()
920 .next()
921 .map_or(Default::default(), |(_, s)| *s),
922 "bad switch arm here",
923 ));
924 }
925 }
926 let pass_through_abilities = context.abilities
927 & (ControlFlowAbility::RETURN | ControlFlowAbility::CONTINUE);
928 let sub_context =
929 context.with_abilities(pass_through_abilities | ControlFlowAbility::BREAK);
930 for case in cases {
931 stages &= self.validate_block(&case.body, &sub_context)?.stages;
932 }
933 }
934 S::Loop {
935 ref body,
936 ref continuing,
937 break_if,
938 } => {
939 let base_expression_count = self.valid_expression_list.len();
942 let pass_through_abilities = context.abilities & ControlFlowAbility::RETURN;
943 stages &= self
944 .validate_block_impl(
945 body,
946 &context.with_abilities(
947 pass_through_abilities
948 | ControlFlowAbility::BREAK
949 | ControlFlowAbility::CONTINUE,
950 ),
951 )?
952 .stages;
953 stages &= self
954 .validate_block_impl(
955 continuing,
956 &context.with_abilities(ControlFlowAbility::empty()),
957 )?
958 .stages;
959
960 if let Some(condition) = break_if {
961 match *context.resolve_type_inner(condition, &self.valid_expression_set)? {
962 Ti::Scalar(crate::Scalar {
963 kind: crate::ScalarKind::Bool,
964 width: _,
965 }) => {}
966 _ => {
967 return Err(FunctionError::InvalidIfType(condition)
968 .with_span_handle(condition, context.expressions))
969 }
970 }
971 }
972
973 for handle in self.valid_expression_list.drain(base_expression_count..) {
974 self.valid_expression_set.remove(handle);
975 }
976 }
977 S::Break => {
978 if !context.abilities.contains(ControlFlowAbility::BREAK) {
979 return Err(FunctionError::BreakOutsideOfLoopOrSwitch
980 .with_span_static(span, "invalid break"));
981 }
982 }
983 S::Continue => {
984 if !context.abilities.contains(ControlFlowAbility::CONTINUE) {
985 return Err(FunctionError::ContinueOutsideOfLoop
986 .with_span_static(span, "invalid continue"));
987 }
988 }
989 S::Return { value } => {
990 if !context.abilities.contains(ControlFlowAbility::RETURN) {
991 return Err(FunctionError::InvalidReturnSpot
992 .with_span_static(span, "invalid return"));
993 }
994 let value_ty = value
995 .map(|expr| context.resolve_type(expr, &self.valid_expression_set))
996 .transpose()?;
997 let okay = match (value_ty, context.return_type) {
1000 (None, None) => true,
1001 (Some(value_inner), Some(expected_ty)) => {
1002 context.compare_types(value_inner, &TypeResolution::Handle(expected_ty))
1003 }
1004 (_, _) => false,
1005 };
1006
1007 if !okay {
1008 log::error!(
1009 "Returning {:?} where {:?} is expected",
1010 value_ty,
1011 context.return_type,
1012 );
1013 if let Some(handle) = value {
1014 return Err(FunctionError::InvalidReturnType {
1015 expression: value,
1016 expected_ty: context.return_type,
1017 }
1018 .with_span_handle(handle, context.expressions));
1019 } else {
1020 return Err(FunctionError::InvalidReturnType {
1021 expression: value,
1022 expected_ty: context.return_type,
1023 }
1024 .with_span_static(span, "invalid return"));
1025 }
1026 }
1027 }
1028 S::Kill => {
1029 stages &= super::ShaderStages::FRAGMENT;
1030 }
1031 S::ControlBarrier(barrier) | S::MemoryBarrier(barrier) => {
1032 stages &= super::ShaderStages::COMPUTE_LIKE;
1033 if barrier.contains(crate::Barrier::SUB_GROUP) {
1034 if !self.capabilities.contains(
1035 super::Capabilities::SUBGROUP | super::Capabilities::SUBGROUP_BARRIER,
1036 ) {
1037 return Err(FunctionError::MissingCapability(
1038 super::Capabilities::SUBGROUP
1039 | super::Capabilities::SUBGROUP_BARRIER,
1040 )
1041 .with_span_static(span, "missing capability for this operation"));
1042 }
1043 if !self
1044 .subgroup_operations
1045 .contains(super::SubgroupOperationSet::BASIC)
1046 {
1047 return Err(FunctionError::InvalidSubgroup(
1048 SubgroupError::UnsupportedOperation(
1049 super::SubgroupOperationSet::BASIC,
1050 ),
1051 )
1052 .with_span_static(span, "support for this operation is not present"));
1053 }
1054 }
1055 }
1056 S::Store { pointer, value } => {
1057 let mut current = pointer;
1058 loop {
1059 match context.expressions[current] {
1060 crate::Expression::Access { base, .. }
1061 | crate::Expression::AccessIndex { base, .. } => current = base,
1062 crate::Expression::LocalVariable(_)
1063 | crate::Expression::GlobalVariable(_)
1064 | crate::Expression::FunctionArgument(_) => break,
1065 _ => {
1066 return Err(FunctionError::InvalidStorePointer(current)
1067 .with_span_handle(pointer, context.expressions))
1068 }
1069 }
1070 }
1071
1072 let value_tr = context.resolve_type(value, &self.valid_expression_set)?;
1073 let value_ty = value_tr.inner_with(context.types);
1074 match *value_ty {
1075 Ti::Image { .. } | Ti::Sampler { .. } => {
1076 return Err(FunctionError::InvalidStoreTexture {
1077 actual: value,
1078 actual_ty: value_ty.clone(),
1079 }
1080 .with_span_context((
1081 context.expressions.get_span(value),
1082 format!("this value is of type {value_ty:?}"),
1083 ))
1084 .with_span(span, "expects a texture argument"));
1085 }
1086 _ => {}
1087 }
1088
1089 let pointer_ty = context.resolve_pointer_type(pointer);
1090 let pointer_base_tr = pointer_ty.pointer_base_type();
1091 let pointer_base_ty = pointer_base_tr
1092 .as_ref()
1093 .map(|ty| ty.inner_with(context.types));
1094 let good = if let Some(&Ti::Atomic(ref scalar)) = pointer_base_ty {
1095 *value_ty == Ti::Scalar(*scalar)
1097 } else if let Some(tr) = pointer_base_tr {
1098 context.compare_types(value_tr, &tr)
1099 } else {
1100 false
1101 };
1102
1103 if !good {
1104 return Err(FunctionError::InvalidStoreTypes { pointer, value }
1105 .with_span()
1106 .with_handle(pointer, context.expressions)
1107 .with_handle(value, context.expressions));
1108 }
1109
1110 if let Some(space) = pointer_ty.pointer_space() {
1111 if !space.access().contains(crate::StorageAccess::STORE) {
1112 return Err(FunctionError::InvalidStorePointer(pointer)
1113 .with_span_static(
1114 context.expressions.get_span(pointer),
1115 "writing to this location is not permitted",
1116 ));
1117 }
1118 }
1119 }
1120 S::ImageStore {
1121 image,
1122 coordinate,
1123 array_index,
1124 value,
1125 } => {
1126 let global_var;
1129 let image_ty;
1130 match *context.get_expression(image) {
1131 crate::Expression::GlobalVariable(var_handle) => {
1132 global_var = &context.global_vars[var_handle];
1133 image_ty = global_var.ty;
1134 }
1135 crate::Expression::Access { base, .. }
1139 | crate::Expression::AccessIndex { base, .. } => {
1140 let crate::Expression::GlobalVariable(var_handle) =
1141 *context.get_expression(base)
1142 else {
1143 return Err(FunctionError::InvalidImageStore(
1144 ExpressionError::ExpectedGlobalVariable,
1145 )
1146 .with_span_handle(image, context.expressions));
1147 };
1148 global_var = &context.global_vars[var_handle];
1149
1150 let Ti::BindingArray { base, .. } = context.types[global_var.ty].inner
1152 else {
1153 return Err(FunctionError::InvalidImageStore(
1154 ExpressionError::ExpectedBindingArrayType(global_var.ty),
1155 )
1156 .with_span_handle(global_var.ty, context.types));
1157 };
1158
1159 image_ty = base;
1160 }
1161 _ => {
1162 return Err(FunctionError::InvalidImageStore(
1163 ExpressionError::ExpectedGlobalVariable,
1164 )
1165 .with_span_handle(image, context.expressions))
1166 }
1167 };
1168
1169 let Ti::Image {
1171 class,
1172 arrayed,
1173 dim,
1174 } = context.types[image_ty].inner
1175 else {
1176 return Err(FunctionError::InvalidImageStore(
1177 ExpressionError::ExpectedImageType(global_var.ty),
1178 )
1179 .with_span()
1180 .with_handle(global_var.ty, context.types)
1181 .with_handle(image, context.expressions));
1182 };
1183
1184 let crate::ImageClass::Storage { format, .. } = class else {
1186 return Err(FunctionError::InvalidImageStore(
1187 ExpressionError::InvalidImageClass(class),
1188 )
1189 .with_span_handle(image, context.expressions));
1190 };
1191
1192 if context
1194 .resolve_type_inner(coordinate, &self.valid_expression_set)?
1195 .image_storage_coordinates()
1196 .is_none_or(|coord_dim| coord_dim != dim)
1197 {
1198 return Err(FunctionError::InvalidImageStore(
1199 ExpressionError::InvalidImageCoordinateType(dim, coordinate),
1200 )
1201 .with_span_handle(coordinate, context.expressions));
1202 }
1203
1204 if arrayed != array_index.is_some() {
1207 return Err(FunctionError::InvalidImageStore(
1208 ExpressionError::InvalidImageArrayIndex,
1209 )
1210 .with_span_handle(coordinate, context.expressions));
1211 }
1212
1213 if let Some(expr) = array_index {
1215 if !matches!(
1216 *context.resolve_type_inner(expr, &self.valid_expression_set)?,
1217 Ti::Scalar(crate::Scalar {
1218 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
1219 width: _,
1220 })
1221 ) {
1222 return Err(FunctionError::InvalidImageStore(
1223 ExpressionError::InvalidImageArrayIndexType(expr),
1224 )
1225 .with_span_handle(expr, context.expressions));
1226 }
1227 }
1228
1229 let value_ty = crate::TypeInner::Vector {
1230 size: crate::VectorSize::Quad,
1231 scalar: format.into(),
1232 };
1233
1234 let actual_value_ty =
1237 context.resolve_type_inner(value, &self.valid_expression_set)?;
1238 if actual_value_ty != &value_ty {
1239 return Err(FunctionError::InvalidStoreValue {
1240 actual: value,
1241 actual_ty: actual_value_ty.clone(),
1242 expected_ty: value_ty.clone(),
1243 }
1244 .with_span_context((
1245 context.expressions.get_span(value),
1246 format!("this value is of type {actual_value_ty:?}"),
1247 ))
1248 .with_span(
1249 span,
1250 format!("expects a value argument of type {value_ty:?}"),
1251 ));
1252 }
1253 }
1254 S::Call {
1255 function,
1256 ref arguments,
1257 result,
1258 } => match self.validate_call(function, arguments, result, context) {
1259 Ok(callee_stages) => stages &= callee_stages,
1260 Err(error) => {
1261 return Err(error.and_then(|error| {
1262 FunctionError::InvalidCall { function, error }
1263 .with_span_static(span, "invalid function call")
1264 }))
1265 }
1266 },
1267 S::Atomic {
1268 pointer,
1269 ref fun,
1270 value,
1271 result,
1272 } => {
1273 self.validate_atomic(pointer, fun, value, result, span, context)?;
1274 }
1275 S::ImageAtomic {
1276 image,
1277 coordinate,
1278 array_index,
1279 fun,
1280 value,
1281 } => {
1282 let var = match *context.get_expression(image) {
1283 crate::Expression::GlobalVariable(var_handle) => {
1284 &context.global_vars[var_handle]
1285 }
1286 crate::Expression::Access { base, .. }
1288 | crate::Expression::AccessIndex { base, .. } => {
1289 match *context.get_expression(base) {
1290 crate::Expression::GlobalVariable(var_handle) => {
1291 &context.global_vars[var_handle]
1292 }
1293 _ => {
1294 return Err(FunctionError::InvalidImageAtomic(
1295 ExpressionError::ExpectedGlobalVariable,
1296 )
1297 .with_span_handle(image, context.expressions))
1298 }
1299 }
1300 }
1301 _ => {
1302 return Err(FunctionError::InvalidImageAtomic(
1303 ExpressionError::ExpectedGlobalVariable,
1304 )
1305 .with_span_handle(image, context.expressions))
1306 }
1307 };
1308
1309 let global_ty = match context.types[var.ty].inner {
1311 Ti::BindingArray { base, .. } => &context.types[base].inner,
1312 ref inner => inner,
1313 };
1314
1315 let value_ty = match *global_ty {
1316 Ti::Image {
1317 class,
1318 arrayed,
1319 dim,
1320 } => {
1321 match context
1322 .resolve_type_inner(coordinate, &self.valid_expression_set)?
1323 .image_storage_coordinates()
1324 {
1325 Some(coord_dim) if coord_dim == dim => {}
1326 _ => {
1327 return Err(FunctionError::InvalidImageAtomic(
1328 ExpressionError::InvalidImageCoordinateType(
1329 dim, coordinate,
1330 ),
1331 )
1332 .with_span_handle(coordinate, context.expressions));
1333 }
1334 };
1335 if arrayed != array_index.is_some() {
1336 return Err(FunctionError::InvalidImageAtomic(
1337 ExpressionError::InvalidImageArrayIndex,
1338 )
1339 .with_span_handle(coordinate, context.expressions));
1340 }
1341 if let Some(expr) = array_index {
1342 match *context
1343 .resolve_type_inner(expr, &self.valid_expression_set)?
1344 {
1345 Ti::Scalar(crate::Scalar {
1346 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
1347 width: _,
1348 }) => {}
1349 _ => {
1350 return Err(FunctionError::InvalidImageAtomic(
1351 ExpressionError::InvalidImageArrayIndexType(expr),
1352 )
1353 .with_span_handle(expr, context.expressions));
1354 }
1355 }
1356 }
1357 match class {
1358 crate::ImageClass::Storage { format, access } => {
1359 if !access.contains(crate::StorageAccess::ATOMIC) {
1360 return Err(FunctionError::InvalidImageAtomic(
1361 ExpressionError::InvalidImageStorageAccess(access),
1362 )
1363 .with_span_handle(image, context.expressions));
1364 }
1365 match format {
1366 crate::StorageFormat::R64Uint => {
1367 if !self.capabilities.intersects(
1368 super::Capabilities::TEXTURE_INT64_ATOMIC,
1369 ) {
1370 return Err(FunctionError::MissingCapability(
1371 super::Capabilities::TEXTURE_INT64_ATOMIC,
1372 )
1373 .with_span_static(
1374 span,
1375 "missing capability for this operation",
1376 ));
1377 }
1378 match fun {
1379 crate::AtomicFunction::Min
1380 | crate::AtomicFunction::Max => {}
1381 _ => {
1382 return Err(
1383 FunctionError::InvalidImageAtomicFunction(
1384 fun,
1385 )
1386 .with_span_handle(
1387 image,
1388 context.expressions,
1389 ),
1390 );
1391 }
1392 }
1393 }
1394 crate::StorageFormat::R32Sint
1395 | crate::StorageFormat::R32Uint => {
1396 if !self
1397 .capabilities
1398 .intersects(super::Capabilities::TEXTURE_ATOMIC)
1399 {
1400 return Err(FunctionError::MissingCapability(
1401 super::Capabilities::TEXTURE_ATOMIC,
1402 )
1403 .with_span_static(
1404 span,
1405 "missing capability for this operation",
1406 ));
1407 }
1408 match fun {
1409 crate::AtomicFunction::Add
1410 | crate::AtomicFunction::And
1411 | crate::AtomicFunction::ExclusiveOr
1412 | crate::AtomicFunction::InclusiveOr
1413 | crate::AtomicFunction::Min
1414 | crate::AtomicFunction::Max => {}
1415 _ => {
1416 return Err(
1417 FunctionError::InvalidImageAtomicFunction(
1418 fun,
1419 )
1420 .with_span_handle(
1421 image,
1422 context.expressions,
1423 ),
1424 );
1425 }
1426 }
1427 }
1428 _ => {
1429 return Err(FunctionError::InvalidImageAtomic(
1430 ExpressionError::InvalidImageFormat(format),
1431 )
1432 .with_span_handle(image, context.expressions));
1433 }
1434 }
1435 crate::TypeInner::Scalar(format.into())
1436 }
1437 _ => {
1438 return Err(FunctionError::InvalidImageAtomic(
1439 ExpressionError::InvalidImageClass(class),
1440 )
1441 .with_span_handle(image, context.expressions));
1442 }
1443 }
1444 }
1445 _ => {
1446 return Err(FunctionError::InvalidImageAtomic(
1447 ExpressionError::ExpectedImageType(var.ty),
1448 )
1449 .with_span()
1450 .with_handle(var.ty, context.types)
1451 .with_handle(image, context.expressions))
1452 }
1453 };
1454
1455 if *context.resolve_type_inner(value, &self.valid_expression_set)? != value_ty {
1456 return Err(FunctionError::InvalidImageAtomicValue(value)
1457 .with_span_handle(value, context.expressions));
1458 }
1459 }
1460 S::WorkGroupUniformLoad { pointer, result } => {
1461 stages &= super::ShaderStages::COMPUTE_LIKE;
1462 let pointer_inner =
1463 context.resolve_type_inner(pointer, &self.valid_expression_set)?;
1464 match *pointer_inner {
1465 Ti::Pointer {
1466 space: AddressSpace::WorkGroup,
1467 ..
1468 } => {}
1469 Ti::ValuePointer {
1470 space: AddressSpace::WorkGroup,
1471 ..
1472 } => {}
1473 _ => {
1474 return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)
1475 .with_span_static(span, "WorkGroupUniformLoad"))
1476 }
1477 }
1478 self.emit_expression(result, context)?;
1479 let ty = match &context.expressions[result] {
1480 &crate::Expression::WorkGroupUniformLoadResult { ty } => ty,
1481 _ => {
1482 return Err(FunctionError::WorkgroupUniformLoadExpressionMismatch(
1483 result,
1484 )
1485 .with_span_static(span, "WorkGroupUniformLoad"));
1486 }
1487 };
1488 let expected_pointer_inner = Ti::Pointer {
1489 base: ty,
1490 space: AddressSpace::WorkGroup,
1491 };
1492 let atomic_specialization_ok = match *pointer_inner {
1495 Ti::Pointer {
1496 base: pointer_base,
1497 space: AddressSpace::WorkGroup,
1498 } => match (&context.types[pointer_base].inner, &context.types[ty].inner) {
1499 (&Ti::Atomic(pointer_scalar), &Ti::Scalar(result_scalar)) => {
1500 pointer_scalar == result_scalar
1501 }
1502 _ => false,
1503 },
1504 _ => false,
1505 };
1506 if !expected_pointer_inner.non_struct_equivalent(pointer_inner, context.types)
1507 && !atomic_specialization_ok
1508 {
1509 return Err(FunctionError::WorkgroupUniformLoadInvalidPointer(pointer)
1510 .with_span_static(span, "WorkGroupUniformLoad"));
1511 }
1512 }
1513 S::RayQuery { query, ref fun } => {
1514 let query_var = match *context.get_expression(query) {
1515 crate::Expression::LocalVariable(var) => &context.local_vars[var],
1516 ref other => {
1517 log::error!("Unexpected ray query expression {other:?}");
1518 return Err(FunctionError::InvalidRayQueryExpression(query)
1519 .with_span_static(span, "invalid query expression"));
1520 }
1521 };
1522 let rq_vertex_return = match context.types[query_var.ty].inner {
1523 Ti::RayQuery { vertex_return } => vertex_return,
1524 ref other => {
1525 log::error!("Unexpected ray query type {other:?}");
1526 return Err(FunctionError::InvalidRayQueryType(query_var.ty)
1527 .with_span_static(span, "invalid query type"));
1528 }
1529 };
1530 match *fun {
1531 crate::RayQueryFunction::Initialize {
1532 acceleration_structure,
1533 descriptor,
1534 } => {
1535 match *context.resolve_type_inner(
1536 acceleration_structure,
1537 &self.valid_expression_set,
1538 )? {
1539 Ti::AccelerationStructure { vertex_return } => {
1540 if (!vertex_return) && rq_vertex_return {
1541 return Err(FunctionError::MissingAccelerationStructureVertexReturn(acceleration_structure, query).with_span_static(span, "invalid acceleration structure"));
1542 }
1543 }
1544 _ => {
1545 return Err(FunctionError::InvalidAccelerationStructure(
1546 acceleration_structure,
1547 )
1548 .with_span_static(span, "invalid acceleration structure"))
1549 }
1550 }
1551 let desc_ty_given = context
1552 .resolve_type_inner(descriptor, &self.valid_expression_set)?;
1553 let desc_ty_expected = context
1554 .special_types
1555 .ray_desc
1556 .map(|handle| &context.types[handle].inner);
1557 if Some(desc_ty_given) != desc_ty_expected {
1558 return Err(FunctionError::InvalidRayDescriptor(descriptor)
1559 .with_span_static(span, "invalid ray descriptor"));
1560 }
1561 }
1562 crate::RayQueryFunction::Proceed { result } => {
1563 self.emit_expression(result, context)?;
1564 }
1565 crate::RayQueryFunction::GenerateIntersection { hit_t } => {
1566 match *context.resolve_type_inner(hit_t, &self.valid_expression_set)? {
1567 Ti::Scalar(crate::Scalar {
1568 kind: crate::ScalarKind::Float,
1569 width: _,
1570 }) => {}
1571 _ => {
1572 return Err(FunctionError::InvalidHitDistanceType(hit_t)
1573 .with_span_static(span, "invalid hit_t"))
1574 }
1575 }
1576 }
1577 crate::RayQueryFunction::ConfirmIntersection => {}
1578 crate::RayQueryFunction::Terminate => {}
1579 }
1580 }
1581 S::SubgroupBallot { result, predicate } => {
1582 stages &= self.subgroup_stages;
1583 if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1584 return Err(FunctionError::MissingCapability(
1585 super::Capabilities::SUBGROUP,
1586 )
1587 .with_span_static(span, "missing capability for this operation"));
1588 }
1589 if !self
1590 .subgroup_operations
1591 .contains(super::SubgroupOperationSet::BALLOT)
1592 {
1593 return Err(FunctionError::InvalidSubgroup(
1594 SubgroupError::UnsupportedOperation(
1595 super::SubgroupOperationSet::BALLOT,
1596 ),
1597 )
1598 .with_span_static(span, "support for this operation is not present"));
1599 }
1600 if let Some(predicate) = predicate {
1601 let predicate_inner =
1602 context.resolve_type_inner(predicate, &self.valid_expression_set)?;
1603 if !matches!(
1604 *predicate_inner,
1605 crate::TypeInner::Scalar(crate::Scalar::BOOL,)
1606 ) {
1607 log::error!(
1608 "Subgroup ballot predicate type {predicate_inner:?} expected bool"
1609 );
1610 return Err(SubgroupError::InvalidOperand(predicate)
1611 .with_span_handle(predicate, context.expressions)
1612 .into_other());
1613 }
1614 }
1615 self.emit_expression(result, context)?;
1616 }
1617 S::SubgroupCollectiveOperation {
1618 ref op,
1619 ref collective_op,
1620 argument,
1621 result,
1622 } => {
1623 stages &= self.subgroup_stages;
1624 if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1625 return Err(FunctionError::MissingCapability(
1626 super::Capabilities::SUBGROUP,
1627 )
1628 .with_span_static(span, "missing capability for this operation"));
1629 }
1630 let operation = op.required_operations();
1631 if !self.subgroup_operations.contains(operation) {
1632 return Err(FunctionError::InvalidSubgroup(
1633 SubgroupError::UnsupportedOperation(operation),
1634 )
1635 .with_span_static(span, "support for this operation is not present"));
1636 }
1637 self.validate_subgroup_operation(op, collective_op, argument, result, context)?;
1638 }
1639 S::SubgroupGather {
1640 ref mode,
1641 argument,
1642 result,
1643 } => {
1644 stages &= self.subgroup_stages;
1645 if !self.capabilities.contains(super::Capabilities::SUBGROUP) {
1646 return Err(FunctionError::MissingCapability(
1647 super::Capabilities::SUBGROUP,
1648 )
1649 .with_span_static(span, "missing capability for this operation"));
1650 }
1651 let operation = mode.required_operations();
1652 if !self.subgroup_operations.contains(operation) {
1653 return Err(FunctionError::InvalidSubgroup(
1654 SubgroupError::UnsupportedOperation(operation),
1655 )
1656 .with_span_static(span, "support for this operation is not present"));
1657 }
1658 self.validate_subgroup_gather(mode, argument, result, context)?;
1659 }
1660 S::CooperativeStore { target, ref data } => {
1661 stages &= super::ShaderStages::COMPUTE;
1662
1663 let target_scalar =
1664 match *context.resolve_type_inner(target, &self.valid_expression_set)? {
1665 Ti::CooperativeMatrix { scalar, .. } => scalar,
1666 ref other => {
1667 log::error!("Target operand type: {other:?}");
1668 return Err(FunctionError::InvalidCooperativeStoreTarget(target)
1669 .with_span_handle(target, context.expressions));
1670 }
1671 };
1672
1673 let ptr_ty = context.resolve_pointer_type(data.pointer);
1674 let ptr_scalar = ptr_ty
1675 .pointer_base_type()
1676 .and_then(|tr| tr.inner_with(context.types).scalar());
1677 if ptr_scalar != Some(target_scalar) {
1678 return Err(FunctionError::InvalidCooperativeDataPointer(data.pointer)
1679 .with_span_handle(data.pointer, context.expressions));
1680 }
1681
1682 let ptr_space = ptr_ty.pointer_space().unwrap_or(AddressSpace::Handle);
1683 if !ptr_space.access().contains(crate::StorageAccess::STORE) {
1684 return Err(FunctionError::InvalidStorePointer(data.pointer)
1685 .with_span_static(
1686 context.expressions.get_span(data.pointer),
1687 "writing to this location is not permitted",
1688 ));
1689 }
1690 }
1691 S::RayPipelineFunction(ref fun) => match *fun {
1692 crate::RayPipelineFunction::TraceRay {
1693 acceleration_structure,
1694 descriptor,
1695 payload,
1696 } => {
1697 match *context.resolve_type_inner(
1698 acceleration_structure,
1699 &self.valid_expression_set,
1700 )? {
1701 crate::TypeInner::AccelerationStructure { vertex_return } => {
1702 if !vertex_return {
1703 self.trace_rays_vertex_return =
1704 super::TraceRayVertexReturnState::NoVertexReturn(span);
1705 } else if let super::TraceRayVertexReturnState::NoTraceRays =
1706 self.trace_rays_vertex_return
1707 {
1708 self.trace_rays_vertex_return =
1709 super::TraceRayVertexReturnState::VertexReturn;
1710 }
1711 }
1712 _ => {
1713 return Err(FunctionError::InvalidAccelerationStructure(
1714 acceleration_structure,
1715 )
1716 .with_span_handle(acceleration_structure, context.expressions))
1717 }
1718 }
1719
1720 let current_payload_ty = match *context
1721 .resolve_type_inner(payload, &self.valid_expression_set)?
1722 {
1723 crate::TypeInner::Pointer { base, space } => {
1724 match space {
1725 AddressSpace::RayPayload | AddressSpace::IncomingRayPayload => {
1726 }
1727 space => {
1728 return Err(FunctionError::InvalidPayloadAddressSpace(
1729 space,
1730 )
1731 .with_span_handle(payload, context.expressions))
1732 }
1733 }
1734 base
1735 }
1736 _ => {
1737 return Err(FunctionError::InvalidPayloadType
1738 .with_span_handle(payload, context.expressions))
1739 }
1740 };
1741
1742 let crate::Expression::GlobalVariable(_) = context.expressions[payload]
1744 else {
1745 return Err(FunctionError::PayloadPointerNotGlobal
1746 .with_span_handle(payload, context.expressions));
1747 };
1748
1749 let ty = *self
1750 .trace_rays_payload_type
1751 .get_or_insert(current_payload_ty);
1752
1753 if ty != current_payload_ty {
1754 return Err(FunctionError::MismatchedPayloadType(
1755 current_payload_ty,
1756 ty,
1757 )
1758 .with_span_handle(ty, context.types));
1759 }
1760
1761 let desc_ty_given =
1762 context.resolve_type_inner(descriptor, &self.valid_expression_set)?;
1763 let desc_ty_expected = context
1764 .special_types
1765 .ray_desc
1766 .map(|handle| &context.types[handle].inner);
1767 if Some(desc_ty_given) != desc_ty_expected {
1768 return Err(FunctionError::InvalidRayDescriptor(descriptor)
1769 .with_span_static(span, "invalid ray descriptor"));
1770 }
1771 }
1772 },
1773 }
1774 }
1775 Ok(BlockInfo { stages })
1776 }
1777
1778 fn validate_block(
1779 &mut self,
1780 statements: &crate::Block,
1781 context: &BlockContext,
1782 ) -> Result<BlockInfo, WithSpan<FunctionError>> {
1783 let base_expression_count = self.valid_expression_list.len();
1784 let info = self.validate_block_impl(statements, context)?;
1785 for handle in self.valid_expression_list.drain(base_expression_count..) {
1786 self.valid_expression_set.remove(handle);
1787 }
1788 Ok(info)
1789 }
1790
1791 fn validate_local_var(
1792 &self,
1793 var: &crate::LocalVariable,
1794 gctx: crate::proc::GlobalCtx,
1795 fun_info: &FunctionInfo,
1796 local_expr_kind: &crate::proc::ExpressionKindTracker,
1797 ) -> Result<(), LocalVariableError> {
1798 log::debug!("var {var:?}");
1799 let type_info = self
1800 .types
1801 .get(var.ty.index())
1802 .ok_or(LocalVariableError::InvalidType(var.ty))?;
1803 if !type_info.flags.contains(super::TypeFlags::CONSTRUCTIBLE) {
1804 return Err(LocalVariableError::InvalidType(var.ty));
1805 }
1806
1807 if let Some(init) = var.init {
1808 if !gctx.compare_types(&TypeResolution::Handle(var.ty), &fun_info[init].ty) {
1809 return Err(LocalVariableError::InitializerType);
1810 }
1811
1812 if !local_expr_kind.is_const_or_override(init) {
1813 return Err(LocalVariableError::NonConstOrOverrideInitializer);
1814 }
1815 }
1816
1817 Ok(())
1818 }
1819
1820 pub(super) fn validate_function(
1821 &mut self,
1822 fun: &crate::Function,
1823 module: &crate::Module,
1824 mod_info: &ModuleInfo,
1825 entry_point: bool,
1826 ) -> Result<FunctionInfo, WithSpan<FunctionError>> {
1827 let mut info = mod_info.process_function(fun, module, self.flags, self.capabilities)?;
1828
1829 let local_expr_kind = crate::proc::ExpressionKindTracker::from_arena(&fun.expressions);
1830
1831 for (var_handle, var) in fun.local_variables.iter() {
1832 self.validate_local_var(var, module.to_ctx(), &info, &local_expr_kind)
1833 .map_err(|source| {
1834 FunctionError::LocalVariable {
1835 handle: var_handle,
1836 name: var.name.clone().unwrap_or_default(),
1837 source,
1838 }
1839 .with_span_handle(var.ty, &module.types)
1840 .with_handle(var_handle, &fun.local_variables)
1841 })?;
1842 }
1843
1844 for (index, argument) in fun.arguments.iter().enumerate() {
1845 match module.types[argument.ty].inner.pointer_space() {
1846 Some(crate::AddressSpace::Private | crate::AddressSpace::Function) | None => {}
1847 Some(other) => {
1848 return Err(FunctionError::InvalidArgumentPointerSpace {
1849 index,
1850 name: argument.name.clone().unwrap_or_default(),
1851 space: other,
1852 }
1853 .with_span_handle(argument.ty, &module.types))
1854 }
1855 }
1856 if !self.types[argument.ty.index()]
1858 .flags
1859 .contains(super::TypeFlags::ARGUMENT)
1860 {
1861 return Err(FunctionError::InvalidArgumentType {
1862 index,
1863 name: argument.name.clone().unwrap_or_default(),
1864 }
1865 .with_span_handle(argument.ty, &module.types));
1866 }
1867
1868 if !entry_point && argument.binding.is_some() {
1869 return Err(FunctionError::PipelineInputRegularFunction {
1870 name: argument.name.clone().unwrap_or_default(),
1871 }
1872 .with_span_handle(argument.ty, &module.types));
1873 }
1874 }
1875
1876 if let Some(ref result) = fun.result {
1877 if !self.types[result.ty.index()]
1878 .flags
1879 .contains(super::TypeFlags::CONSTRUCTIBLE)
1880 {
1881 return Err(FunctionError::NonConstructibleReturnType
1882 .with_span_handle(result.ty, &module.types));
1883 }
1884
1885 if !entry_point && result.binding.is_some() {
1886 return Err(FunctionError::PipelineOutputRegularFunction
1887 .with_span_handle(result.ty, &module.types));
1888 }
1889 }
1890
1891 self.valid_expression_set.clear_for_arena(&fun.expressions);
1892 self.valid_expression_list.clear();
1893 self.needs_visit.clear_for_arena(&fun.expressions);
1894 for (handle, expr) in fun.expressions.iter() {
1895 if expr.needs_pre_emit() {
1896 self.valid_expression_set.insert(handle);
1897 }
1898 if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {
1899 if let crate::Expression::CallResult(_) | crate::Expression::AtomicResult { .. } =
1902 *expr
1903 {
1904 self.needs_visit.insert(handle);
1905 }
1906
1907 match self.validate_expression(
1908 handle,
1909 expr,
1910 fun,
1911 module,
1912 &info,
1913 mod_info,
1914 &local_expr_kind,
1915 ) {
1916 Ok(stages) => info.available_stages &= stages,
1917 Err(source) => {
1918 return Err(FunctionError::Expression { handle, source }
1919 .with_span_handle(handle, &fun.expressions))
1920 }
1921 }
1922 }
1923 }
1924
1925 if self.flags.contains(super::ValidationFlags::BLOCKS) {
1926 let stages = self
1927 .validate_block(
1928 &fun.body,
1929 &BlockContext::new(fun, module, &info, &mod_info.functions, &local_expr_kind),
1930 )?
1931 .stages;
1932 info.available_stages &= stages;
1933
1934 if self.flags.contains(super::ValidationFlags::EXPRESSIONS) {
1935 if let Some(handle) = self.needs_visit.iter().next() {
1936 return Err(FunctionError::UnvisitedExpression(handle)
1937 .with_span_handle(handle, &fun.expressions));
1938 }
1939 }
1940 }
1941 Ok(info)
1942 }
1943}