1use super::{compose::validate_compose, FunctionInfo, ModuleInfo, ShaderStages, TypeFlags};
2use crate::arena::UniqueArena;
3use crate::{
4 arena::Handle,
5 proc::OverloadSet as _,
6 proc::{IndexableLengthError, ResolveError},
7};
8
9#[cfg(test)]
10use alloc::boxed::Box;
11
12#[derive(Clone, Debug, thiserror::Error)]
13#[cfg_attr(test, derive(PartialEq))]
14pub enum ExpressionError {
15 #[error("Used by a statement before it was introduced into the scope by any of the dominating blocks")]
16 NotInScope,
17 #[error("Base type {0:?} is not compatible with this expression")]
18 InvalidBaseType(Handle<crate::Expression>),
19 #[error("Accessing with index {0:?} can't be done")]
20 InvalidIndexType(Handle<crate::Expression>),
21 #[error("Accessing {0:?} via a negative index is invalid")]
22 NegativeIndex(Handle<crate::Expression>),
23 #[error("Accessing index {1} is out of {0:?} bounds")]
24 IndexOutOfBounds(Handle<crate::Expression>, u32),
25 #[error("Function argument {0:?} doesn't exist")]
26 FunctionArgumentDoesntExist(u32),
27 #[error("Loading of {0:?} can't be done")]
28 InvalidPointerType(Handle<crate::Expression>),
29 #[error("Array length of {0:?} can't be done")]
30 InvalidArrayType(Handle<crate::Expression>),
31 #[error("Get intersection of {0:?} can't be done")]
32 InvalidRayQueryType(Handle<crate::Expression>),
33 #[error("Splatting {0:?} can't be done")]
34 InvalidSplatType(Handle<crate::Expression>),
35 #[error("Swizzling {0:?} can't be done")]
36 InvalidVectorType(Handle<crate::Expression>),
37 #[error("Swizzle component {0:?} is outside of vector size {1:?}")]
38 InvalidSwizzleComponent(crate::SwizzleComponent, crate::VectorSize),
39 #[error(transparent)]
40 Compose(#[from] super::ComposeError),
41 #[error("Cannot construct zero value of {0:?} because it is not a constructible type")]
42 InvalidZeroValue(Handle<crate::Type>),
43 #[error(transparent)]
44 IndexableLength(#[from] IndexableLengthError),
45 #[error("Operation {0:?} can't work with {1:?}")]
46 InvalidUnaryOperandType(crate::UnaryOperator, Handle<crate::Expression>),
47 #[error(
48 "Operation {:?} can't work with {:?} (of type {:?}) and {:?} (of type {:?})",
49 op,
50 lhs_expr,
51 lhs_type,
52 rhs_expr,
53 rhs_type
54 )]
55 InvalidBinaryOperandTypes {
56 op: crate::BinaryOperator,
57 lhs_expr: Handle<crate::Expression>,
58 lhs_type: crate::TypeInner,
59 rhs_expr: Handle<crate::Expression>,
60 rhs_type: crate::TypeInner,
61 },
62 #[error("Expected selection argument types to match, but reject value of type {reject:?} does not match accept value of value {accept:?}")]
63 SelectValuesTypeMismatch {
64 accept: crate::TypeInner,
65 reject: crate::TypeInner,
66 },
67 #[error("Expected selection condition to be a boolean value, got {actual:?}")]
68 SelectConditionNotABool { actual: crate::TypeInner },
69 #[error("Relational argument {0:?} is not a boolean vector")]
70 InvalidBooleanVector(Handle<crate::Expression>),
71 #[error("Relational argument {0:?} is not a float")]
72 InvalidFloatArgument(Handle<crate::Expression>),
73 #[error("Type resolution failed")]
74 Type(#[from] ResolveError),
75 #[error("Not a global variable")]
76 ExpectedGlobalVariable,
77 #[error("Not a global variable or a function argument")]
78 ExpectedGlobalOrArgument,
79 #[error("Needs to be an binding array instead of {0:?}")]
80 ExpectedBindingArrayType(Handle<crate::Type>),
81 #[error("Needs to be an image instead of {0:?}")]
82 ExpectedImageType(Handle<crate::Type>),
83 #[error("Needs to be an image instead of {0:?}")]
84 ExpectedSamplerType(Handle<crate::Type>),
85 #[error("Unable to operate on image class {0:?}")]
86 InvalidImageClass(crate::ImageClass),
87 #[error("Image atomics are not supported for storage format {0:?}")]
88 InvalidImageFormat(crate::StorageFormat),
89 #[error("Image atomics require atomic storage access, {0:?} is insufficient")]
90 InvalidImageStorageAccess(crate::StorageAccess),
91 #[error("Derivatives can only be taken from scalar and vector floats")]
92 InvalidDerivative,
93 #[error("Image array index parameter is misplaced")]
94 InvalidImageArrayIndex,
95 #[error("Cannot textureLoad from a specific multisample sample on a non-multisampled image.")]
96 InvalidImageSampleSelector,
97 #[error("Cannot textureLoad from a multisampled image without specifying a sample.")]
98 MissingImageSampleSelector,
99 #[error("Cannot textureLoad with a specific mip level on a non-mipmapped image.")]
100 InvalidImageLevelSelector,
101 #[error("Cannot textureLoad from a mipmapped image without specifying a level.")]
102 MissingImageLevelSelector,
103 #[error("Image array index type of {0:?} is not an integer scalar")]
104 InvalidImageArrayIndexType(Handle<crate::Expression>),
105 #[error("Image sample or level-of-detail index's type of {0:?} is not an integer scalar")]
106 InvalidImageOtherIndexType(Handle<crate::Expression>),
107 #[error("Image coordinate type of {1:?} does not match dimension {0:?}")]
108 InvalidImageCoordinateType(crate::ImageDimension, Handle<crate::Expression>),
109 #[error("Comparison sampling mismatch: image has class {image:?}, but the sampler is comparison={sampler}, and the reference was provided={has_ref}")]
110 ComparisonSamplingMismatch {
111 image: crate::ImageClass,
112 sampler: bool,
113 has_ref: bool,
114 },
115 #[error("Sample offset must be a const-expression")]
116 InvalidSampleOffsetExprType,
117 #[error("Sample offset constant {1:?} doesn't match the image dimension {0:?}")]
118 InvalidSampleOffset(crate::ImageDimension, Handle<crate::Expression>),
119 #[error("Depth reference {0:?} is not a scalar float")]
120 InvalidDepthReference(Handle<crate::Expression>),
121 #[error("Depth sample level can only be Auto or Zero")]
122 InvalidDepthSampleLevel,
123 #[error("Gather level can only be Zero")]
124 InvalidGatherLevel,
125 #[error("Gather component {0:?} doesn't exist in the image")]
126 InvalidGatherComponent(crate::SwizzleComponent),
127 #[error("Gather can't be done for image dimension {0:?}")]
128 InvalidGatherDimension(crate::ImageDimension),
129 #[error("Sample level (exact) type {0:?} has an invalid type")]
130 InvalidSampleLevelExactType(Handle<crate::Expression>),
131 #[error("Sample level (bias) type {0:?} is not a scalar float")]
132 InvalidSampleLevelBiasType(Handle<crate::Expression>),
133 #[error("Bias can't be done for image dimension {0:?}")]
134 InvalidSampleLevelBiasDimension(crate::ImageDimension),
135 #[error("Sample level (gradient) of {1:?} doesn't match the image dimension {0:?}")]
136 InvalidSampleLevelGradientType(crate::ImageDimension, Handle<crate::Expression>),
137 #[error("Clamping sample coordinate to edge is not supported with {0}")]
138 InvalidSampleClampCoordinateToEdge(alloc::string::String),
139 #[error("Unable to cast")]
140 InvalidCastArgument,
141 #[error("Invalid argument count for {0:?}")]
142 WrongArgumentCount(crate::MathFunction),
143 #[error("Argument [{1}] to {0:?} as expression {2:?} has an invalid type.")]
144 InvalidArgumentType(crate::MathFunction, u32, Handle<crate::Expression>),
145 #[error(
146 "workgroupUniformLoad result type can't be {0:?}. It can only be a constructible type."
147 )]
148 InvalidWorkGroupUniformLoadResultType(Handle<crate::Type>),
149 #[error("Shader requires capability {0:?}")]
150 MissingCapabilities(super::Capabilities),
151 #[error(transparent)]
152 Literal(#[from] LiteralError),
153 #[error("{0:?} is not supported for Width {2} {1:?} arguments yet, see https://github.com/gfx-rs/wgpu/issues/5276")]
154 UnsupportedWidth(crate::MathFunction, crate::ScalarKind, crate::Bytes),
155 #[error("Invalid operand for cooperative op")]
156 InvalidCooperativeOperand(Handle<crate::Expression>),
157 #[error("Shift amount exceeds the bit width of {lhs_type:?}")]
158 ShiftAmountTooLarge {
159 lhs_type: crate::TypeInner,
160 rhs_expr: Handle<crate::Expression>,
161 },
162 #[error("Division by zero")]
163 DivideByZero,
164}
165
166#[derive(Clone, Debug, thiserror::Error)]
167#[cfg_attr(test, derive(PartialEq))]
168pub enum ConstExpressionError {
169 #[error("The expression is not a constant or override expression")]
170 NonConstOrOverride,
171 #[error("The expression is not a fully evaluated constant expression")]
172 NonFullyEvaluatedConst,
173 #[error(transparent)]
174 Compose(#[from] super::ComposeError),
175 #[error("Splatting {0:?} can't be done")]
176 InvalidSplatType(Handle<crate::Expression>),
177 #[error("Type resolution failed")]
178 Type(#[from] ResolveError),
179 #[error(transparent)]
180 Literal(#[from] LiteralError),
181 #[error(transparent)]
182 Width(#[from] super::r#type::WidthError),
183}
184
185#[derive(Clone, Debug, thiserror::Error)]
186#[cfg_attr(test, derive(PartialEq))]
187pub enum LiteralError {
188 #[error("Float literal is NaN")]
189 NaN,
190 #[error("Float literal is infinite")]
191 Infinity,
192 #[error(transparent)]
193 Width(#[from] super::r#type::WidthError),
194}
195
196struct ExpressionTypeResolver<'a> {
197 root: Handle<crate::Expression>,
198 types: &'a UniqueArena<crate::Type>,
199 info: &'a FunctionInfo,
200}
201
202impl core::ops::Index<Handle<crate::Expression>> for ExpressionTypeResolver<'_> {
203 type Output = crate::TypeInner;
204
205 #[allow(clippy::panic)]
206 fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
207 if handle < self.root {
208 self.info[handle].ty.inner_with(self.types)
209 } else {
210 panic!(
212 "Depends on {:?}, which has not been processed yet",
213 self.root
214 )
215 }
216 }
217}
218
219impl super::Validator {
220 pub(super) fn validate_const_expression(
221 &self,
222 handle: Handle<crate::Expression>,
223 gctx: crate::proc::GlobalCtx,
224 mod_info: &ModuleInfo,
225 global_expr_kind: &crate::proc::ExpressionKindTracker,
226 ) -> Result<(), ConstExpressionError> {
227 use crate::Expression as E;
228
229 if !global_expr_kind.is_const_or_override(handle) {
230 return Err(ConstExpressionError::NonConstOrOverride);
231 }
232
233 match gctx.global_expressions[handle] {
234 E::Literal(literal) => {
235 self.validate_literal(literal)?;
236 }
237 E::Constant(_) | E::ZeroValue(_) => {}
238 E::Compose { ref components, ty } => {
239 validate_compose(
240 ty,
241 gctx,
242 components.iter().map(|&handle| mod_info[handle].clone()),
243 )?;
244 }
245 E::Splat { value, .. } => match *mod_info[value].inner_with(gctx.types) {
246 crate::TypeInner::Scalar { .. } => {}
247 _ => return Err(ConstExpressionError::InvalidSplatType(value)),
248 },
249 _ if global_expr_kind.is_const(handle) || self.overrides_resolved => {
250 return Err(ConstExpressionError::NonFullyEvaluatedConst)
251 }
252 _ => {}
254 }
255
256 Ok(())
257 }
258
259 fn validate_constant_shift_amounts(
268 left_ty: &crate::TypeInner,
269 right: Handle<crate::Expression>,
270 module: &crate::Module,
271 function: &crate::Function,
272 ) -> Result<(), ExpressionError> {
273 fn is_overflowing_shift(
274 left_ty: &crate::TypeInner,
275 right: Handle<crate::Expression>,
276 module: &crate::Module,
277 function: &crate::Function,
278 ) -> bool {
279 let Some((vec_size, scalar)) = left_ty.vector_size_and_scalar() else {
280 return false;
281 };
282 if !matches!(
283 scalar.kind,
284 crate::ScalarKind::Sint | crate::ScalarKind::Uint
285 ) {
286 return false;
287 }
288 let lhs_bits = u32::from(8 * scalar.width);
289 if vec_size.is_none() {
290 let shift_amount = module
291 .to_ctx()
292 .get_const_val_from::<u32, _>(right, &function.expressions);
293 shift_amount.ok().is_some_and(|s| s >= lhs_bits)
294 } else {
295 match function.expressions[right] {
296 crate::Expression::ZeroValue(_) => false, crate::Expression::Splat { value, .. } => module
298 .to_ctx()
299 .get_const_val_from::<u32, _>(value, &function.expressions)
300 .ok()
301 .is_some_and(|s| s >= lhs_bits),
302 crate::Expression::Compose {
303 ty: _,
304 ref components,
305 } => components.iter().any(|comp| {
306 module
307 .to_ctx()
308 .get_const_val_from::<u32, _>(*comp, &function.expressions)
309 .ok()
310 .is_some_and(|s| s >= lhs_bits)
311 }),
312 _ => false,
313 }
314 }
315 }
316
317 if is_overflowing_shift(left_ty, right, module, function) {
318 Err(ExpressionError::ShiftAmountTooLarge {
319 lhs_type: left_ty.clone(),
320 rhs_expr: right,
321 })
322 } else {
323 Ok(())
324 }
325 }
326
327 fn validate_constant_divisor(
337 left_ty: &crate::TypeInner,
338 right: Handle<crate::Expression>,
339 module: &crate::Module,
340 function: &crate::Function,
341 ) -> Result<(), ExpressionError> {
342 fn contains_zero(
343 handle: Handle<crate::Expression>,
344 expressions: &crate::Arena<crate::Expression>,
345 module: &crate::Module,
346 ) -> bool {
347 match expressions[handle] {
348 crate::Expression::Literal(_) | crate::Expression::ZeroValue(_) => module
349 .to_ctx()
350 .get_const_val_from::<u32, _>(handle, expressions)
351 .ok()
352 .is_some_and(|v| v == 0),
353 crate::Expression::Splat { value, .. } => contains_zero(value, expressions, module),
354 crate::Expression::Compose { ref components, .. } => components
355 .iter()
356 .any(|&comp| contains_zero(comp, expressions, module)),
357 crate::Expression::Constant(c) => {
358 contains_zero(module.constants[c].init, &module.global_expressions, module)
359 }
360 _ => false,
361 }
362 }
363
364 let Some((_, scalar)) = left_ty.vector_size_and_scalar() else {
365 return Ok(());
366 };
367 if !matches!(
368 scalar.kind,
369 crate::ScalarKind::Sint | crate::ScalarKind::Uint
370 ) {
371 return Ok(());
372 }
373
374 if contains_zero(right, &function.expressions, module) {
375 Err(ExpressionError::DivideByZero)
376 } else {
377 Ok(())
378 }
379 }
380
381 #[allow(clippy::too_many_arguments)]
382 pub(super) fn validate_expression(
383 &self,
384 root: Handle<crate::Expression>,
385 expression: &crate::Expression,
386 function: &crate::Function,
387 module: &crate::Module,
388 info: &FunctionInfo,
389 mod_info: &ModuleInfo,
390 expr_kind: &crate::proc::ExpressionKindTracker,
391 ) -> Result<ShaderStages, ExpressionError> {
392 use crate::{Expression as E, Scalar as Sc, ScalarKind as Sk, TypeInner as Ti};
393
394 let resolver = ExpressionTypeResolver {
395 root,
396 types: &module.types,
397 info,
398 };
399
400 let stages = match *expression {
401 E::Access { base, index } => {
402 let base_type = &resolver[base];
403 match *base_type {
404 Ti::Matrix { .. }
405 | Ti::Vector { .. }
406 | Ti::Array { .. }
407 | Ti::Pointer { .. }
408 | Ti::ValuePointer { size: Some(_), .. }
409 | Ti::BindingArray { .. } => {}
410 ref other => {
411 log::debug!("Indexing of {other:?}");
412 return Err(ExpressionError::InvalidBaseType(base));
413 }
414 };
415 match resolver[index] {
416 Ti::Scalar(Sc {
418 kind: Sk::Sint | Sk::Uint,
419 ..
420 }) => {}
421 ref other => {
422 log::debug!("Indexing by {other:?}");
423 return Err(ExpressionError::InvalidIndexType(index));
424 }
425 }
426
427 match module
429 .to_ctx()
430 .get_const_val_from(index, &function.expressions)
431 {
432 Ok(value) => {
433 let length = if self.overrides_resolved {
434 base_type.indexable_length_resolved(module)
435 } else {
436 base_type.indexable_length_pending(module)
437 }?;
438 if let crate::proc::IndexableLength::Known(known_length) = length {
441 if value >= known_length {
442 return Err(ExpressionError::IndexOutOfBounds(base, value));
443 }
444 }
445 }
446 Err(crate::proc::ConstValueError::Negative) => {
447 return Err(ExpressionError::NegativeIndex(base))
448 }
449 Err(crate::proc::ConstValueError::NonConst) => {}
450 Err(crate::proc::ConstValueError::InvalidType) => {
451 return Err(ExpressionError::InvalidIndexType(index))
452 }
453 }
454
455 ShaderStages::all()
456 }
457 E::AccessIndex { base, index } => {
458 fn resolve_index_limit(
459 module: &crate::Module,
460 top: Handle<crate::Expression>,
461 ty: &crate::TypeInner,
462 top_level: bool,
463 ) -> Result<u32, ExpressionError> {
464 let limit = match *ty {
465 Ti::Vector { size, .. }
466 | Ti::ValuePointer {
467 size: Some(size), ..
468 } => size as u32,
469 Ti::Matrix { columns, .. } => columns as u32,
470 Ti::Array {
471 size: crate::ArraySize::Constant(len),
472 ..
473 } => len.get(),
474 Ti::Array { .. } | Ti::BindingArray { .. } => u32::MAX, Ti::Pointer { base, .. } if top_level => {
476 resolve_index_limit(module, top, &module.types[base].inner, false)?
477 }
478 Ti::Struct { ref members, .. } => members.len() as u32,
479 ref other => {
480 log::debug!("Indexing of {other:?}");
481 return Err(ExpressionError::InvalidBaseType(top));
482 }
483 };
484 Ok(limit)
485 }
486
487 let limit = resolve_index_limit(module, base, &resolver[base], true)?;
488 if index >= limit {
489 return Err(ExpressionError::IndexOutOfBounds(base, index));
490 }
491 ShaderStages::all()
492 }
493 E::Splat { size: _, value } => match resolver[value] {
494 Ti::Scalar { .. } => ShaderStages::all(),
495 ref other => {
496 log::debug!("Splat scalar type {other:?}");
497 return Err(ExpressionError::InvalidSplatType(value));
498 }
499 },
500 E::Swizzle {
501 size,
502 vector,
503 pattern,
504 } => {
505 let vec_size = match resolver[vector] {
506 Ti::Vector { size: vec_size, .. } => vec_size,
507 ref other => {
508 log::debug!("Swizzle vector type {other:?}");
509 return Err(ExpressionError::InvalidVectorType(vector));
510 }
511 };
512 for &sc in pattern[..size as usize].iter() {
513 if sc as u8 >= vec_size as u8 {
514 return Err(ExpressionError::InvalidSwizzleComponent(sc, vec_size));
515 }
516 }
517 ShaderStages::all()
518 }
519 E::Literal(literal) => {
520 self.validate_literal(literal)?;
521 ShaderStages::all()
522 }
523 E::Constant(_) | E::Override(_) => ShaderStages::all(),
524 E::ZeroValue(ty) => {
525 if !mod_info[ty].contains(TypeFlags::CONSTRUCTIBLE) {
526 return Err(ExpressionError::InvalidZeroValue(ty));
527 }
528 if matches!(module.types[ty].inner, crate::TypeInner::RayQuery { .. }) {
529 return Err(ExpressionError::InvalidZeroValue(ty));
530 }
531 ShaderStages::all()
532 }
533 E::Compose { ref components, ty } => {
534 validate_compose(
535 ty,
536 module.to_ctx(),
537 components.iter().map(|&handle| info[handle].ty.clone()),
538 )?;
539 ShaderStages::all()
540 }
541 E::FunctionArgument(index) => {
542 if index >= function.arguments.len() as u32 {
543 return Err(ExpressionError::FunctionArgumentDoesntExist(index));
544 }
545 ShaderStages::all()
546 }
547 E::GlobalVariable(_handle) => ShaderStages::all(),
548 E::LocalVariable(_handle) => ShaderStages::all(),
549 E::Load { pointer } => {
550 match resolver[pointer] {
551 Ti::Pointer { base, .. }
552 if self.types[base.index()]
553 .flags
554 .contains(TypeFlags::SIZED | TypeFlags::DATA) => {}
555 Ti::ValuePointer { .. } => {}
556 ref other => {
557 log::debug!("Loading {other:?}");
558 return Err(ExpressionError::InvalidPointerType(pointer));
559 }
560 }
561 ShaderStages::all()
562 }
563 E::ImageSample {
564 image,
565 sampler,
566 gather,
567 coordinate,
568 array_index,
569 offset,
570 level,
571 depth_ref,
572 clamp_to_edge,
573 } => {
574 let image_ty = Self::global_var_ty(module, function, image)?;
576 let sampler_ty = Self::global_var_ty(module, function, sampler)?;
577
578 let comparison = match module.types[sampler_ty].inner {
579 Ti::Sampler { comparison } => comparison,
580 _ => return Err(ExpressionError::ExpectedSamplerType(sampler_ty)),
581 };
582
583 let (class, dim) = match module.types[image_ty].inner {
584 Ti::Image {
585 class,
586 arrayed,
587 dim,
588 } => {
589 if arrayed != array_index.is_some() {
591 return Err(ExpressionError::InvalidImageArrayIndex);
592 }
593 if let Some(expr) = array_index {
594 match resolver[expr] {
595 Ti::Scalar(Sc {
596 kind: Sk::Sint | Sk::Uint,
597 ..
598 }) => {}
599 _ => return Err(ExpressionError::InvalidImageArrayIndexType(expr)),
600 }
601 }
602 (class, dim)
603 }
604 _ => return Err(ExpressionError::ExpectedImageType(image_ty)),
605 };
606
607 let image_depth = match class {
609 crate::ImageClass::Sampled {
610 kind: crate::ScalarKind::Float,
611 multi: false,
612 } => false,
613 crate::ImageClass::Sampled {
614 kind: crate::ScalarKind::Uint | crate::ScalarKind::Sint,
615 multi: false,
616 } if gather.is_some() => false,
617 crate::ImageClass::External => false,
618 crate::ImageClass::Depth { multi: false } => true,
619 _ => return Err(ExpressionError::InvalidImageClass(class)),
620 };
621 if comparison != depth_ref.is_some() || (comparison && !image_depth) {
622 return Err(ExpressionError::ComparisonSamplingMismatch {
623 image: class,
624 sampler: comparison,
625 has_ref: depth_ref.is_some(),
626 });
627 }
628
629 let num_components = match dim {
631 crate::ImageDimension::D1 => 1,
632 crate::ImageDimension::D2 => 2,
633 crate::ImageDimension::D3 | crate::ImageDimension::Cube => 3,
634 };
635 match resolver[coordinate] {
636 Ti::Scalar(Sc {
637 kind: Sk::Float, ..
638 }) if num_components == 1 => {}
639 Ti::Vector {
640 size,
641 scalar:
642 Sc {
643 kind: Sk::Float, ..
644 },
645 } if size as u32 == num_components => {}
646 _ => return Err(ExpressionError::InvalidImageCoordinateType(dim, coordinate)),
647 }
648
649 if let Some(const_expr) = offset {
651 if !expr_kind.is_const(const_expr) {
652 return Err(ExpressionError::InvalidSampleOffsetExprType);
653 }
654
655 match resolver[const_expr] {
656 Ti::Scalar(Sc { kind: Sk::Sint, .. }) if num_components == 1 => {}
657 Ti::Vector {
658 size,
659 scalar: Sc { kind: Sk::Sint, .. },
660 } if size as u32 == num_components => {}
661 _ => {
662 return Err(ExpressionError::InvalidSampleOffset(dim, const_expr));
663 }
664 }
665 }
666
667 if let Some(expr) = depth_ref {
669 match resolver[expr] {
670 Ti::Scalar(Sc {
671 kind: Sk::Float, ..
672 }) => {}
673 _ => return Err(ExpressionError::InvalidDepthReference(expr)),
674 }
675 match level {
676 crate::SampleLevel::Auto | crate::SampleLevel::Zero => {}
677 _ => return Err(ExpressionError::InvalidDepthSampleLevel),
678 }
679 }
680
681 if let Some(component) = gather {
682 match dim {
683 crate::ImageDimension::D2 | crate::ImageDimension::Cube => {}
684 crate::ImageDimension::D1 | crate::ImageDimension::D3 => {
685 return Err(ExpressionError::InvalidGatherDimension(dim))
686 }
687 };
688 let max_component = match class {
689 crate::ImageClass::Depth { .. } => crate::SwizzleComponent::X,
690 _ => crate::SwizzleComponent::W,
691 };
692 if component > max_component {
693 return Err(ExpressionError::InvalidGatherComponent(component));
694 }
695 match level {
696 crate::SampleLevel::Zero => {}
697 _ => return Err(ExpressionError::InvalidGatherLevel),
698 }
699 }
700
701 if clamp_to_edge {
704 if !matches!(
705 class,
706 crate::ImageClass::Sampled {
707 kind: crate::ScalarKind::Float,
708 multi: false
709 } | crate::ImageClass::External
710 ) {
711 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
712 alloc::format!("image class `{class:?}`"),
713 ));
714 }
715 if dim != crate::ImageDimension::D2 {
716 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
717 alloc::format!("image dimension `{dim:?}`"),
718 ));
719 }
720 if gather.is_some() {
721 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
722 "gather".into(),
723 ));
724 }
725 if array_index.is_some() {
726 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
727 "array index".into(),
728 ));
729 }
730 if offset.is_some() {
731 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
732 "offset".into(),
733 ));
734 }
735 if level != crate::SampleLevel::Zero {
736 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
737 "non-zero level".into(),
738 ));
739 }
740 if depth_ref.is_some() {
741 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
742 "depth comparison".into(),
743 ));
744 }
745 }
746
747 if matches!(class, crate::ImageClass::External) && !clamp_to_edge {
749 return Err(ExpressionError::InvalidImageClass(class));
750 }
751
752 match level {
754 crate::SampleLevel::Auto => ShaderStages::FRAGMENT,
755 crate::SampleLevel::Zero => ShaderStages::all(),
756 crate::SampleLevel::Exact(expr) => {
757 match class {
758 crate::ImageClass::Depth { .. } => match resolver[expr] {
759 Ti::Scalar(Sc {
760 kind: Sk::Sint | Sk::Uint,
761 ..
762 }) => {}
763 _ => {
764 return Err(ExpressionError::InvalidSampleLevelExactType(expr))
765 }
766 },
767 _ => match resolver[expr] {
768 Ti::Scalar(Sc {
769 kind: Sk::Float, ..
770 }) => {}
771 _ => {
772 return Err(ExpressionError::InvalidSampleLevelExactType(expr))
773 }
774 },
775 }
776 ShaderStages::all()
777 }
778 crate::SampleLevel::Bias(expr) => {
779 match resolver[expr] {
780 Ti::Scalar(Sc {
781 kind: Sk::Float, ..
782 }) => {}
783 _ => return Err(ExpressionError::InvalidSampleLevelBiasType(expr)),
784 }
785 match class {
786 crate::ImageClass::Sampled {
787 kind: Sk::Float,
788 multi: false,
789 } => {
790 if dim == crate::ImageDimension::D1 {
791 return Err(ExpressionError::InvalidSampleLevelBiasDimension(
792 dim,
793 ));
794 }
795 }
796 _ => return Err(ExpressionError::InvalidImageClass(class)),
797 }
798 ShaderStages::FRAGMENT
799 }
800 crate::SampleLevel::Gradient { x, y } => {
801 match resolver[x] {
802 Ti::Scalar(Sc {
803 kind: Sk::Float, ..
804 }) if num_components == 1 => {}
805 Ti::Vector {
806 size,
807 scalar:
808 Sc {
809 kind: Sk::Float, ..
810 },
811 } if size as u32 == num_components => {}
812 _ => {
813 return Err(ExpressionError::InvalidSampleLevelGradientType(dim, x))
814 }
815 }
816 match resolver[y] {
817 Ti::Scalar(Sc {
818 kind: Sk::Float, ..
819 }) if num_components == 1 => {}
820 Ti::Vector {
821 size,
822 scalar:
823 Sc {
824 kind: Sk::Float, ..
825 },
826 } if size as u32 == num_components => {}
827 _ => {
828 return Err(ExpressionError::InvalidSampleLevelGradientType(dim, y))
829 }
830 }
831 ShaderStages::all()
832 }
833 }
834 }
835 E::ImageLoad {
836 image,
837 coordinate,
838 array_index,
839 sample,
840 level,
841 } => {
842 let ty = Self::global_var_ty(module, function, image)?;
843 let Ti::Image {
844 class,
845 arrayed,
846 dim,
847 } = module.types[ty].inner
848 else {
849 return Err(ExpressionError::ExpectedImageType(ty));
850 };
851
852 match resolver[coordinate].image_storage_coordinates() {
853 Some(coord_dim) if coord_dim == dim => {}
854 _ => return Err(ExpressionError::InvalidImageCoordinateType(dim, coordinate)),
855 };
856 if arrayed != array_index.is_some() {
857 return Err(ExpressionError::InvalidImageArrayIndex);
858 }
859 if let Some(expr) = array_index {
860 if !matches!(resolver[expr], Ti::Scalar(Sc::I32 | Sc::U32)) {
861 return Err(ExpressionError::InvalidImageArrayIndexType(expr));
862 }
863 }
864
865 match (sample, class.is_multisampled()) {
866 (None, false) => {}
867 (Some(sample), true) => {
868 if !matches!(resolver[sample], Ti::Scalar(Sc::I32 | Sc::U32)) {
869 return Err(ExpressionError::InvalidImageOtherIndexType(sample));
870 }
871 }
872 (Some(_), false) => {
873 return Err(ExpressionError::InvalidImageSampleSelector);
874 }
875 (None, true) => {
876 return Err(ExpressionError::MissingImageSampleSelector);
877 }
878 }
879
880 match (level, class.is_mipmapped()) {
881 (None, false) => {}
882 (Some(level), true) => match resolver[level] {
883 Ti::Scalar(Sc {
884 kind: Sk::Sint | Sk::Uint,
885 width: _,
886 }) => {}
887 _ => return Err(ExpressionError::InvalidImageArrayIndexType(level)),
888 },
889 (Some(_), false) => {
890 return Err(ExpressionError::InvalidImageLevelSelector);
891 }
892 (None, true) => {
893 return Err(ExpressionError::MissingImageLevelSelector);
894 }
895 }
896 ShaderStages::all()
897 }
898 E::ImageQuery { image, query } => {
899 let ty = Self::global_var_ty(module, function, image)?;
900 match module.types[ty].inner {
901 Ti::Image { class, arrayed, .. } => {
902 let good = match query {
903 crate::ImageQuery::NumLayers => arrayed,
904 crate::ImageQuery::Size { level: None } => true,
905 crate::ImageQuery::Size { level: Some(level) } => {
906 match resolver[level] {
907 Ti::Scalar(Sc::I32 | Sc::U32) => {}
908 _ => {
909 return Err(ExpressionError::InvalidImageOtherIndexType(
910 level,
911 ))
912 }
913 }
914 class.is_mipmapped()
915 }
916 crate::ImageQuery::NumLevels => class.is_mipmapped(),
917 crate::ImageQuery::NumSamples => class.is_multisampled(),
918 };
919 if !good {
920 return Err(ExpressionError::InvalidImageClass(class));
921 }
922 }
923 _ => return Err(ExpressionError::ExpectedImageType(ty)),
924 }
925 ShaderStages::all()
926 }
927 E::Unary { op, expr } => {
928 use crate::UnaryOperator as Uo;
929 let Some((_, scalar)) = resolver[expr].vector_size_and_scalar() else {
930 return Err(ExpressionError::InvalidUnaryOperandType(op, expr));
931 };
932 match (op, scalar.kind) {
933 (Uo::Negate, Sk::Float | Sk::Sint) => {}
934 (Uo::LogicalNot, Sk::Bool) => {}
935 (Uo::BitwiseNot, Sk::Sint | Sk::Uint) => {}
936 _ => return Err(ExpressionError::InvalidUnaryOperandType(op, expr)),
937 }
938 ShaderStages::all()
939 }
940 E::Binary { op, left, right } => {
941 use crate::BinaryOperator as Bo;
942 let left_inner = &resolver[left];
943 let right_inner = &resolver[right];
944 let good = match op {
945 Bo::Add | Bo::Subtract => match *left_inner {
946 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
947 Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
948 Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
949 },
950 Ti::Matrix { .. } | Ti::CooperativeMatrix { .. } => {
951 left_inner == right_inner
952 }
953 _ => false,
954 },
955 Bo::Divide | Bo::Modulo => match *left_inner {
956 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
957 Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
958 Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
959 },
960 _ => false,
961 },
962 Bo::Multiply => {
963 let kind_allowed = match left_inner.scalar_kind() {
964 Some(Sk::Uint | Sk::Sint | Sk::Float) => true,
965 Some(Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat) | None => false,
966 };
967 let types_match = match (left_inner, right_inner) {
968 (&Ti::Scalar(scalar1), &Ti::Scalar(scalar2))
970 | (
971 &Ti::Vector {
972 scalar: scalar1, ..
973 },
974 &Ti::Scalar(scalar2),
975 )
976 | (
977 &Ti::Scalar(scalar1),
978 &Ti::Vector {
979 scalar: scalar2, ..
980 },
981 ) => scalar1 == scalar2,
982 (
984 &Ti::Scalar(Sc {
985 kind: Sk::Float, ..
986 }),
987 &Ti::Matrix { .. },
988 )
989 | (
990 &Ti::Matrix { .. },
991 &Ti::Scalar(Sc {
992 kind: Sk::Float, ..
993 }),
994 ) => true,
995 (
997 &Ti::Vector {
998 size: size1,
999 scalar: scalar1,
1000 },
1001 &Ti::Vector {
1002 size: size2,
1003 scalar: scalar2,
1004 },
1005 ) => scalar1 == scalar2 && size1 == size2,
1006 (
1008 &Ti::Matrix { columns, .. },
1009 &Ti::Vector {
1010 size,
1011 scalar:
1012 Sc {
1013 kind: Sk::Float, ..
1014 },
1015 },
1016 ) => columns == size,
1017 (
1019 &Ti::Vector {
1020 size,
1021 scalar:
1022 Sc {
1023 kind: Sk::Float, ..
1024 },
1025 },
1026 &Ti::Matrix { rows, .. },
1027 ) => size == rows,
1028 (&Ti::Matrix { columns, .. }, &Ti::Matrix { rows, .. }) => {
1030 columns == rows
1031 }
1032 (&Ti::Scalar(s1), &Ti::CooperativeMatrix { scalar: s2, .. })
1034 | (&Ti::CooperativeMatrix { scalar: s1, .. }, &Ti::Scalar(s2)) => {
1035 s1 == s2
1036 }
1037 _ => false,
1038 };
1039 let left_width = left_inner.scalar_width().unwrap_or(0);
1040 let right_width = right_inner.scalar_width().unwrap_or(0);
1041 kind_allowed && types_match && left_width == right_width
1042 }
1043 Bo::Equal | Bo::NotEqual => left_inner.is_sized() && left_inner == right_inner,
1044 Bo::Less | Bo::LessEqual | Bo::Greater | Bo::GreaterEqual => {
1045 match *left_inner {
1046 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
1047 Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
1048 Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
1049 },
1050 ref other => {
1051 log::debug!("Op {op:?} left type {other:?}");
1052 false
1053 }
1054 }
1055 }
1056 Bo::LogicalAnd | Bo::LogicalOr => match *left_inner {
1057 Ti::Scalar(Sc { kind: Sk::Bool, .. })
1058 | Ti::Vector {
1059 scalar: Sc { kind: Sk::Bool, .. },
1060 ..
1061 } => left_inner == right_inner,
1062 ref other => {
1063 log::debug!("Op {op:?} left type {other:?}");
1064 false
1065 }
1066 },
1067 Bo::And | Bo::InclusiveOr => match *left_inner {
1068 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
1069 Sk::Bool | Sk::Sint | Sk::Uint => left_inner == right_inner,
1070 Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,
1071 },
1072 ref other => {
1073 log::debug!("Op {op:?} left type {other:?}");
1074 false
1075 }
1076 },
1077 Bo::ExclusiveOr => match *left_inner {
1078 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
1079 Sk::Sint | Sk::Uint => left_inner == right_inner,
1080 Sk::Bool | Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,
1081 },
1082 ref other => {
1083 log::debug!("Op {op:?} left type {other:?}");
1084 false
1085 }
1086 },
1087 Bo::ShiftLeft | Bo::ShiftRight => {
1088 let (base_size, base_scalar) = match *left_inner {
1089 Ti::Scalar(scalar) => (Ok(None), scalar),
1090 Ti::Vector { size, scalar } => (Ok(Some(size)), scalar),
1091 ref other => {
1092 log::debug!("Op {op:?} base type {other:?}");
1093 (Err(()), Sc::BOOL)
1094 }
1095 };
1096 let shift_size = match *right_inner {
1097 Ti::Scalar(Sc { kind: Sk::Uint, .. }) => Ok(None),
1098 Ti::Vector {
1099 size,
1100 scalar: Sc { kind: Sk::Uint, .. },
1101 } => Ok(Some(size)),
1102 ref other => {
1103 log::debug!("Op {op:?} shift type {other:?}");
1104 Err(())
1105 }
1106 };
1107 match base_scalar.kind {
1108 Sk::Sint | Sk::Uint => base_size.is_ok() && base_size == shift_size,
1109 Sk::Float | Sk::AbstractInt | Sk::AbstractFloat | Sk::Bool => false,
1110 }
1111 }
1112 };
1113 if !good {
1114 log::debug!(
1115 "Left: {:?} of type {:?}",
1116 function.expressions[left],
1117 left_inner
1118 );
1119 log::debug!(
1120 "Right: {:?} of type {:?}",
1121 function.expressions[right],
1122 right_inner
1123 );
1124 return Err(ExpressionError::InvalidBinaryOperandTypes {
1125 op,
1126 lhs_expr: left,
1127 lhs_type: left_inner.clone(),
1128 rhs_expr: right,
1129 rhs_type: right_inner.clone(),
1130 });
1131 }
1132 if matches!(op, Bo::ShiftLeft | Bo::ShiftRight) {
1134 Self::validate_constant_shift_amounts(left_inner, right, module, function)?;
1135 }
1136 if matches!(op, Bo::Divide | Bo::Modulo) {
1138 Self::validate_constant_divisor(left_inner, right, module, function)?;
1139 }
1140 ShaderStages::all()
1141 }
1142 E::Select {
1143 condition,
1144 accept,
1145 reject,
1146 } => {
1147 let accept_inner = &resolver[accept];
1148 let reject_inner = &resolver[reject];
1149 let condition_ty = &resolver[condition];
1150 let condition_good = match *condition_ty {
1151 Ti::Scalar(Sc {
1152 kind: Sk::Bool,
1153 width: _,
1154 }) => {
1155 match *accept_inner {
1158 Ti::Scalar { .. } | Ti::Vector { .. } => true,
1159 _ => false,
1160 }
1161 }
1162 Ti::Vector {
1163 size,
1164 scalar:
1165 Sc {
1166 kind: Sk::Bool,
1167 width: _,
1168 },
1169 } => match *accept_inner {
1170 Ti::Vector {
1171 size: other_size, ..
1172 } => size == other_size,
1173 _ => false,
1174 },
1175 _ => false,
1176 };
1177 if accept_inner != reject_inner {
1178 return Err(ExpressionError::SelectValuesTypeMismatch {
1179 accept: accept_inner.clone(),
1180 reject: reject_inner.clone(),
1181 });
1182 }
1183 if !condition_good {
1184 return Err(ExpressionError::SelectConditionNotABool {
1185 actual: condition_ty.clone(),
1186 });
1187 }
1188 ShaderStages::all()
1189 }
1190 E::Derivative { expr, .. } => {
1191 let Some((_, scalar)) = resolver[expr].vector_size_and_scalar() else {
1192 return Err(ExpressionError::InvalidDerivative);
1193 };
1194 if scalar.kind != Sk::Float || scalar.width < 4 {
1195 return Err(ExpressionError::InvalidDerivative);
1198 }
1199 ShaderStages::FRAGMENT
1200 }
1201 E::Relational { fun, argument } => {
1202 use crate::RelationalFunction as Rf;
1203 let argument_inner = &resolver[argument];
1204 match fun {
1205 Rf::All | Rf::Any => match *argument_inner {
1206 Ti::Vector {
1207 scalar: Sc { kind: Sk::Bool, .. },
1208 ..
1209 } => {}
1210 ref other => {
1211 log::debug!("All/Any of type {other:?}");
1212 return Err(ExpressionError::InvalidBooleanVector(argument));
1213 }
1214 },
1215 Rf::IsNan | Rf::IsInf => match *argument_inner {
1216 Ti::Scalar(scalar) | Ti::Vector { scalar, .. }
1217 if scalar.kind == Sk::Float => {}
1218 ref other => {
1219 log::debug!("Float test of type {other:?}");
1220 return Err(ExpressionError::InvalidFloatArgument(argument));
1221 }
1222 },
1223 }
1224 ShaderStages::all()
1225 }
1226 E::Math {
1227 fun,
1228 arg,
1229 arg1,
1230 arg2,
1231 arg3,
1232 } => {
1233 if matches!(
1234 fun,
1235 crate::MathFunction::QuantizeToF16
1236 | crate::MathFunction::Pack2x16float
1237 | crate::MathFunction::Unpack2x16float
1238 ) && !self
1239 .capabilities
1240 .contains(crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32)
1241 {
1242 return Err(ExpressionError::MissingCapabilities(
1243 crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32,
1244 ));
1245 }
1246
1247 let actuals: &[_] = match (arg1, arg2, arg3) {
1248 (None, None, None) => &[arg],
1249 (Some(arg1), None, None) => &[arg, arg1],
1250 (Some(arg1), Some(arg2), None) => &[arg, arg1, arg2],
1251 (Some(arg1), Some(arg2), Some(arg3)) => &[arg, arg1, arg2, arg3],
1252 _ => return Err(ExpressionError::WrongArgumentCount(fun)),
1253 };
1254
1255 let resolve = |arg| &resolver[arg];
1256 let actual_types: &[_] = match *actuals {
1257 [arg0] => &[resolve(arg0)],
1258 [arg0, arg1] => &[resolve(arg0), resolve(arg1)],
1259 [arg0, arg1, arg2] => &[resolve(arg0), resolve(arg1), resolve(arg2)],
1260 [arg0, arg1, arg2, arg3] => {
1261 &[resolve(arg0), resolve(arg1), resolve(arg2), resolve(arg3)]
1262 }
1263 _ => unreachable!(),
1264 };
1265
1266 let mut overloads = fun.overloads();
1268 log::debug!(
1269 "initial overloads for {:?}: {:#?}",
1270 fun,
1271 overloads.for_debug(&module.types)
1272 );
1273
1274 for (i, (&expr, &ty)) in actuals.iter().zip(actual_types).enumerate() {
1282 overloads = overloads.arg(i, ty, &module.types);
1285 log::debug!(
1286 "overloads after arg {i}: {:#?}",
1287 overloads.for_debug(&module.types)
1288 );
1289
1290 if overloads.is_empty() {
1291 log::debug!("all overloads eliminated");
1292 return Err(ExpressionError::InvalidArgumentType(fun, i as u32, expr));
1293 }
1294 }
1295
1296 if actuals.len() < overloads.min_arguments() {
1297 return Err(ExpressionError::WrongArgumentCount(fun));
1298 }
1299
1300 ShaderStages::all()
1301 }
1302 E::As {
1303 expr,
1304 kind,
1305 convert,
1306 } => {
1307 let mut base_scalar = match resolver[expr] {
1308 crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => {
1309 scalar
1310 }
1311 crate::TypeInner::Matrix { scalar, .. } => scalar,
1312 _ => return Err(ExpressionError::InvalidCastArgument),
1313 };
1314 base_scalar.kind = kind;
1315 if let Some(width) = convert {
1316 base_scalar.width = width;
1317 }
1318 if self.check_width(base_scalar).is_err() {
1319 return Err(ExpressionError::InvalidCastArgument);
1320 }
1321 ShaderStages::all()
1322 }
1323 E::CallResult(function) => mod_info.functions[function.index()].available_stages,
1324 E::AtomicResult { .. } => {
1325 ShaderStages::all()
1330 }
1331 E::WorkGroupUniformLoadResult { ty } => {
1332 if self.types[ty.index()]
1333 .flags
1334 .contains(TypeFlags::SIZED | TypeFlags::CONSTRUCTIBLE)
1337 {
1338 ShaderStages::COMPUTE_LIKE
1339 } else {
1340 return Err(ExpressionError::InvalidWorkGroupUniformLoadResultType(ty));
1341 }
1342 }
1343 E::ArrayLength(expr) => match resolver[expr] {
1344 Ti::Pointer { base, .. } => {
1345 let base_ty = &resolver.types[base];
1346 if let Ti::Array {
1347 size: crate::ArraySize::Dynamic,
1348 ..
1349 } = base_ty.inner
1350 {
1351 ShaderStages::all()
1352 } else {
1353 return Err(ExpressionError::InvalidArrayType(expr));
1354 }
1355 }
1356 ref other => {
1357 log::debug!("Array length of {other:?}");
1358 return Err(ExpressionError::InvalidArrayType(expr));
1359 }
1360 },
1361 E::RayQueryProceedResult => ShaderStages::all(),
1362 E::RayQueryGetIntersection {
1363 query,
1364 committed: _,
1365 } => match resolver[query] {
1366 Ti::Pointer {
1367 base,
1368 space: crate::AddressSpace::Function,
1369 } => match resolver.types[base].inner {
1370 Ti::RayQuery { .. } => ShaderStages::all(),
1371 ref other => {
1372 log::debug!("Intersection result of a pointer to {other:?}");
1373 return Err(ExpressionError::InvalidRayQueryType(query));
1374 }
1375 },
1376 ref other => {
1377 log::debug!("Intersection result of {other:?}");
1378 return Err(ExpressionError::InvalidRayQueryType(query));
1379 }
1380 },
1381 E::RayQueryVertexPositions {
1382 query,
1383 committed: _,
1384 } => match resolver[query] {
1385 Ti::Pointer {
1386 base,
1387 space: crate::AddressSpace::Function,
1388 } => match resolver.types[base].inner {
1389 Ti::RayQuery {
1390 vertex_return: true,
1391 } => ShaderStages::all(),
1392 ref other => {
1393 log::debug!("Intersection result of a pointer to {other:?}");
1394 return Err(ExpressionError::InvalidRayQueryType(query));
1395 }
1396 },
1397 ref other => {
1398 log::debug!("Intersection result of {other:?}");
1399 return Err(ExpressionError::InvalidRayQueryType(query));
1400 }
1401 },
1402 E::SubgroupBallotResult | E::SubgroupOperationResult { .. } => self.subgroup_stages,
1403 E::CooperativeLoad { ref data, .. } => {
1404 if resolver[data.pointer]
1405 .pointer_base_type()
1406 .and_then(|tr| tr.inner_with(&module.types).scalar())
1407 .is_none()
1408 {
1409 return Err(ExpressionError::InvalidPointerType(data.pointer));
1410 }
1411 ShaderStages::COMPUTE
1412 }
1413 E::CooperativeMultiplyAdd { a, b, c } => {
1414 let roles = [
1415 crate::CooperativeRole::A,
1416 crate::CooperativeRole::B,
1417 crate::CooperativeRole::C,
1418 ];
1419 for (operand, expected_role) in [a, b, c].into_iter().zip(roles) {
1420 match resolver[operand] {
1421 Ti::CooperativeMatrix { role, .. } if role == expected_role => {}
1422 ref other => {
1423 log::debug!("{expected_role:?} operand type: {other:?}");
1424 return Err(ExpressionError::InvalidCooperativeOperand(a));
1425 }
1426 }
1427 }
1428 ShaderStages::COMPUTE
1429 }
1430 };
1431 Ok(stages)
1432 }
1433
1434 fn global_var_ty(
1435 module: &crate::Module,
1436 function: &crate::Function,
1437 expr: Handle<crate::Expression>,
1438 ) -> Result<Handle<crate::Type>, ExpressionError> {
1439 use crate::Expression as Ex;
1440
1441 match function.expressions[expr] {
1442 Ex::GlobalVariable(var_handle) => Ok(module.global_variables[var_handle].ty),
1443 Ex::FunctionArgument(i) => Ok(function.arguments[i as usize].ty),
1444 Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1445 match function.expressions[base] {
1446 Ex::GlobalVariable(var_handle) => {
1447 let array_ty = module.global_variables[var_handle].ty;
1448
1449 match module.types[array_ty].inner {
1450 crate::TypeInner::BindingArray { base, .. } => Ok(base),
1451 _ => Err(ExpressionError::ExpectedBindingArrayType(array_ty)),
1452 }
1453 }
1454 _ => Err(ExpressionError::ExpectedGlobalVariable),
1455 }
1456 }
1457 _ => Err(ExpressionError::ExpectedGlobalVariable),
1458 }
1459 }
1460
1461 pub fn validate_literal(&self, literal: crate::Literal) -> Result<(), LiteralError> {
1462 let _ = self.check_width(literal.scalar())?;
1463 check_literal_value(literal)?;
1464
1465 Ok(())
1466 }
1467}
1468
1469pub const fn check_literal_value(literal: crate::Literal) -> Result<(), LiteralError> {
1470 let is_nan = match literal {
1471 crate::Literal::F64(v) => v.is_nan(),
1472 crate::Literal::F32(v) => v.is_nan(),
1473 _ => false,
1474 };
1475 if is_nan {
1476 return Err(LiteralError::NaN);
1477 }
1478
1479 let is_infinite = match literal {
1480 crate::Literal::F64(v) => v.is_infinite(),
1481 crate::Literal::F32(v) => v.is_infinite(),
1482 _ => false,
1483 };
1484 if is_infinite {
1485 return Err(LiteralError::Infinity);
1486 }
1487
1488 Ok(())
1489}
1490
1491#[cfg(test)]
1492fn validate_with_expression(
1494 expr: crate::Expression,
1495 caps: super::Capabilities,
1496) -> Result<ModuleInfo, Box<crate::span::WithSpan<super::ValidationError>>> {
1497 use crate::span::Span;
1498
1499 let mut function = crate::Function::default();
1500 function.expressions.append(expr, Span::default());
1501 function.body.push(
1502 crate::Statement::Emit(function.expressions.range_from(0)),
1503 Span::default(),
1504 );
1505
1506 let mut module = crate::Module::default();
1507 module.functions.append(function, Span::default());
1508
1509 let mut validator = super::Validator::new(super::ValidationFlags::EXPRESSIONS, caps);
1510
1511 validator.validate(&module)
1512}
1513
1514#[cfg(test)]
1515fn validate_with_const_expression(
1517 expr: crate::Expression,
1518 caps: super::Capabilities,
1519) -> Result<ModuleInfo, Box<crate::span::WithSpan<super::ValidationError>>> {
1520 use crate::span::Span;
1521
1522 let mut module = crate::Module::default();
1523 module.global_expressions.append(expr, Span::default());
1524
1525 let mut validator = super::Validator::new(super::ValidationFlags::CONSTANTS, caps);
1526
1527 validator.validate(&module)
1528}
1529
1530#[test]
1532fn f64_runtime_literals() {
1533 let result = validate_with_expression(
1534 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1535 super::Capabilities::default(),
1536 );
1537 let error = result.unwrap_err().into_inner();
1538 assert!(matches!(
1539 error,
1540 crate::valid::ValidationError::Function {
1541 source: super::FunctionError::Expression {
1542 source: ExpressionError::Literal(LiteralError::Width(
1543 super::r#type::WidthError::MissingCapability {
1544 name: "f64",
1545 flag: "FLOAT64",
1546 }
1547 ),),
1548 ..
1549 },
1550 ..
1551 }
1552 ));
1553
1554 let result = validate_with_expression(
1555 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1556 super::Capabilities::default() | super::Capabilities::FLOAT64,
1557 );
1558 assert!(result.is_ok());
1559}
1560
1561#[test]
1563fn f64_const_literals() {
1564 let result = validate_with_const_expression(
1565 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1566 super::Capabilities::default(),
1567 );
1568 let error = result.unwrap_err().into_inner();
1569 assert!(matches!(
1570 error,
1571 crate::valid::ValidationError::ConstExpression {
1572 source: ConstExpressionError::Literal(LiteralError::Width(
1573 super::r#type::WidthError::MissingCapability {
1574 name: "f64",
1575 flag: "FLOAT64",
1576 }
1577 )),
1578 ..
1579 }
1580 ));
1581
1582 let result = validate_with_const_expression(
1583 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1584 super::Capabilities::default() | super::Capabilities::FLOAT64,
1585 );
1586 assert!(result.is_ok());
1587}