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