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