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 ShaderStages::all()
529 }
530 E::Compose { ref components, ty } => {
531 validate_compose(
532 ty,
533 module.to_ctx(),
534 components.iter().map(|&handle| info[handle].ty.clone()),
535 )?;
536 ShaderStages::all()
537 }
538 E::FunctionArgument(index) => {
539 if index >= function.arguments.len() as u32 {
540 return Err(ExpressionError::FunctionArgumentDoesntExist(index));
541 }
542 ShaderStages::all()
543 }
544 E::GlobalVariable(_handle) => ShaderStages::all(),
545 E::LocalVariable(_handle) => ShaderStages::all(),
546 E::Load { pointer } => {
547 match resolver[pointer] {
548 Ti::Pointer { base, .. }
549 if self.types[base.index()]
550 .flags
551 .contains(TypeFlags::SIZED | TypeFlags::DATA) => {}
552 Ti::ValuePointer { .. } => {}
553 ref other => {
554 log::debug!("Loading {other:?}");
555 return Err(ExpressionError::InvalidPointerType(pointer));
556 }
557 }
558 ShaderStages::all()
559 }
560 E::ImageSample {
561 image,
562 sampler,
563 gather,
564 coordinate,
565 array_index,
566 offset,
567 level,
568 depth_ref,
569 clamp_to_edge,
570 } => {
571 let image_ty = Self::global_var_ty(module, function, image)?;
573 let sampler_ty = Self::global_var_ty(module, function, sampler)?;
574
575 let comparison = match module.types[sampler_ty].inner {
576 Ti::Sampler { comparison } => comparison,
577 _ => return Err(ExpressionError::ExpectedSamplerType(sampler_ty)),
578 };
579
580 let (class, dim) = match module.types[image_ty].inner {
581 Ti::Image {
582 class,
583 arrayed,
584 dim,
585 } => {
586 if arrayed != array_index.is_some() {
588 return Err(ExpressionError::InvalidImageArrayIndex);
589 }
590 if let Some(expr) = array_index {
591 match resolver[expr] {
592 Ti::Scalar(Sc {
593 kind: Sk::Sint | Sk::Uint,
594 ..
595 }) => {}
596 _ => return Err(ExpressionError::InvalidImageArrayIndexType(expr)),
597 }
598 }
599 (class, dim)
600 }
601 _ => return Err(ExpressionError::ExpectedImageType(image_ty)),
602 };
603
604 let image_depth = match class {
606 crate::ImageClass::Sampled {
607 kind: crate::ScalarKind::Float,
608 multi: false,
609 } => false,
610 crate::ImageClass::Sampled {
611 kind: crate::ScalarKind::Uint | crate::ScalarKind::Sint,
612 multi: false,
613 } if gather.is_some() => false,
614 crate::ImageClass::External => false,
615 crate::ImageClass::Depth { multi: false } => true,
616 _ => return Err(ExpressionError::InvalidImageClass(class)),
617 };
618 if comparison != depth_ref.is_some() || (comparison && !image_depth) {
619 return Err(ExpressionError::ComparisonSamplingMismatch {
620 image: class,
621 sampler: comparison,
622 has_ref: depth_ref.is_some(),
623 });
624 }
625
626 let num_components = match dim {
628 crate::ImageDimension::D1 => 1,
629 crate::ImageDimension::D2 => 2,
630 crate::ImageDimension::D3 | crate::ImageDimension::Cube => 3,
631 };
632 match resolver[coordinate] {
633 Ti::Scalar(Sc {
634 kind: Sk::Float, ..
635 }) if num_components == 1 => {}
636 Ti::Vector {
637 size,
638 scalar:
639 Sc {
640 kind: Sk::Float, ..
641 },
642 } if size as u32 == num_components => {}
643 _ => return Err(ExpressionError::InvalidImageCoordinateType(dim, coordinate)),
644 }
645
646 if let Some(const_expr) = offset {
648 if !expr_kind.is_const(const_expr) {
649 return Err(ExpressionError::InvalidSampleOffsetExprType);
650 }
651
652 match resolver[const_expr] {
653 Ti::Scalar(Sc { kind: Sk::Sint, .. }) if num_components == 1 => {}
654 Ti::Vector {
655 size,
656 scalar: Sc { kind: Sk::Sint, .. },
657 } if size as u32 == num_components => {}
658 _ => {
659 return Err(ExpressionError::InvalidSampleOffset(dim, const_expr));
660 }
661 }
662 }
663
664 if let Some(expr) = depth_ref {
666 match resolver[expr] {
667 Ti::Scalar(Sc {
668 kind: Sk::Float, ..
669 }) => {}
670 _ => return Err(ExpressionError::InvalidDepthReference(expr)),
671 }
672 match level {
673 crate::SampleLevel::Auto | crate::SampleLevel::Zero => {}
674 _ => return Err(ExpressionError::InvalidDepthSampleLevel),
675 }
676 }
677
678 if let Some(component) = gather {
679 match dim {
680 crate::ImageDimension::D2 | crate::ImageDimension::Cube => {}
681 crate::ImageDimension::D1 | crate::ImageDimension::D3 => {
682 return Err(ExpressionError::InvalidGatherDimension(dim))
683 }
684 };
685 let max_component = match class {
686 crate::ImageClass::Depth { .. } => crate::SwizzleComponent::X,
687 _ => crate::SwizzleComponent::W,
688 };
689 if component > max_component {
690 return Err(ExpressionError::InvalidGatherComponent(component));
691 }
692 match level {
693 crate::SampleLevel::Zero => {}
694 _ => return Err(ExpressionError::InvalidGatherLevel),
695 }
696 }
697
698 if clamp_to_edge {
701 if !matches!(
702 class,
703 crate::ImageClass::Sampled {
704 kind: crate::ScalarKind::Float,
705 multi: false
706 } | crate::ImageClass::External
707 ) {
708 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
709 alloc::format!("image class `{class:?}`"),
710 ));
711 }
712 if dim != crate::ImageDimension::D2 {
713 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
714 alloc::format!("image dimension `{dim:?}`"),
715 ));
716 }
717 if gather.is_some() {
718 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
719 "gather".into(),
720 ));
721 }
722 if array_index.is_some() {
723 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
724 "array index".into(),
725 ));
726 }
727 if offset.is_some() {
728 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
729 "offset".into(),
730 ));
731 }
732 if level != crate::SampleLevel::Zero {
733 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
734 "non-zero level".into(),
735 ));
736 }
737 if depth_ref.is_some() {
738 return Err(ExpressionError::InvalidSampleClampCoordinateToEdge(
739 "depth comparison".into(),
740 ));
741 }
742 }
743
744 if matches!(class, crate::ImageClass::External) && !clamp_to_edge {
746 return Err(ExpressionError::InvalidImageClass(class));
747 }
748
749 match level {
751 crate::SampleLevel::Auto => ShaderStages::FRAGMENT,
752 crate::SampleLevel::Zero => ShaderStages::all(),
753 crate::SampleLevel::Exact(expr) => {
754 match class {
755 crate::ImageClass::Depth { .. } => match resolver[expr] {
756 Ti::Scalar(Sc {
757 kind: Sk::Sint | Sk::Uint,
758 ..
759 }) => {}
760 _ => {
761 return Err(ExpressionError::InvalidSampleLevelExactType(expr))
762 }
763 },
764 _ => match resolver[expr] {
765 Ti::Scalar(Sc {
766 kind: Sk::Float, ..
767 }) => {}
768 _ => {
769 return Err(ExpressionError::InvalidSampleLevelExactType(expr))
770 }
771 },
772 }
773 ShaderStages::all()
774 }
775 crate::SampleLevel::Bias(expr) => {
776 match resolver[expr] {
777 Ti::Scalar(Sc {
778 kind: Sk::Float, ..
779 }) => {}
780 _ => return Err(ExpressionError::InvalidSampleLevelBiasType(expr)),
781 }
782 match class {
783 crate::ImageClass::Sampled {
784 kind: Sk::Float,
785 multi: false,
786 } => {
787 if dim == crate::ImageDimension::D1 {
788 return Err(ExpressionError::InvalidSampleLevelBiasDimension(
789 dim,
790 ));
791 }
792 }
793 _ => return Err(ExpressionError::InvalidImageClass(class)),
794 }
795 ShaderStages::FRAGMENT
796 }
797 crate::SampleLevel::Gradient { x, y } => {
798 match resolver[x] {
799 Ti::Scalar(Sc {
800 kind: Sk::Float, ..
801 }) if num_components == 1 => {}
802 Ti::Vector {
803 size,
804 scalar:
805 Sc {
806 kind: Sk::Float, ..
807 },
808 } if size as u32 == num_components => {}
809 _ => {
810 return Err(ExpressionError::InvalidSampleLevelGradientType(dim, x))
811 }
812 }
813 match resolver[y] {
814 Ti::Scalar(Sc {
815 kind: Sk::Float, ..
816 }) if num_components == 1 => {}
817 Ti::Vector {
818 size,
819 scalar:
820 Sc {
821 kind: Sk::Float, ..
822 },
823 } if size as u32 == num_components => {}
824 _ => {
825 return Err(ExpressionError::InvalidSampleLevelGradientType(dim, y))
826 }
827 }
828 ShaderStages::all()
829 }
830 }
831 }
832 E::ImageLoad {
833 image,
834 coordinate,
835 array_index,
836 sample,
837 level,
838 } => {
839 let ty = Self::global_var_ty(module, function, image)?;
840 let Ti::Image {
841 class,
842 arrayed,
843 dim,
844 } = module.types[ty].inner
845 else {
846 return Err(ExpressionError::ExpectedImageType(ty));
847 };
848
849 match resolver[coordinate].image_storage_coordinates() {
850 Some(coord_dim) if coord_dim == dim => {}
851 _ => return Err(ExpressionError::InvalidImageCoordinateType(dim, coordinate)),
852 };
853 if arrayed != array_index.is_some() {
854 return Err(ExpressionError::InvalidImageArrayIndex);
855 }
856 if let Some(expr) = array_index {
857 if !matches!(resolver[expr], Ti::Scalar(Sc::I32 | Sc::U32)) {
858 return Err(ExpressionError::InvalidImageArrayIndexType(expr));
859 }
860 }
861
862 match (sample, class.is_multisampled()) {
863 (None, false) => {}
864 (Some(sample), true) => {
865 if !matches!(resolver[sample], Ti::Scalar(Sc::I32 | Sc::U32)) {
866 return Err(ExpressionError::InvalidImageOtherIndexType(sample));
867 }
868 }
869 (Some(_), false) => {
870 return Err(ExpressionError::InvalidImageSampleSelector);
871 }
872 (None, true) => {
873 return Err(ExpressionError::MissingImageSampleSelector);
874 }
875 }
876
877 match (level, class.is_mipmapped()) {
878 (None, false) => {}
879 (Some(level), true) => match resolver[level] {
880 Ti::Scalar(Sc {
881 kind: Sk::Sint | Sk::Uint,
882 width: _,
883 }) => {}
884 _ => return Err(ExpressionError::InvalidImageArrayIndexType(level)),
885 },
886 (Some(_), false) => {
887 return Err(ExpressionError::InvalidImageLevelSelector);
888 }
889 (None, true) => {
890 return Err(ExpressionError::MissingImageLevelSelector);
891 }
892 }
893 ShaderStages::all()
894 }
895 E::ImageQuery { image, query } => {
896 let ty = Self::global_var_ty(module, function, image)?;
897 match module.types[ty].inner {
898 Ti::Image { class, arrayed, .. } => {
899 let good = match query {
900 crate::ImageQuery::NumLayers => arrayed,
901 crate::ImageQuery::Size { level: None } => true,
902 crate::ImageQuery::Size { level: Some(level) } => {
903 match resolver[level] {
904 Ti::Scalar(Sc::I32 | Sc::U32) => {}
905 _ => {
906 return Err(ExpressionError::InvalidImageOtherIndexType(
907 level,
908 ))
909 }
910 }
911 class.is_mipmapped()
912 }
913 crate::ImageQuery::NumLevels => class.is_mipmapped(),
914 crate::ImageQuery::NumSamples => class.is_multisampled(),
915 };
916 if !good {
917 return Err(ExpressionError::InvalidImageClass(class));
918 }
919 }
920 _ => return Err(ExpressionError::ExpectedImageType(ty)),
921 }
922 ShaderStages::all()
923 }
924 E::Unary { op, expr } => {
925 use crate::UnaryOperator as Uo;
926 let Some((_, scalar)) = resolver[expr].vector_size_and_scalar() else {
927 return Err(ExpressionError::InvalidUnaryOperandType(op, expr));
928 };
929 match (op, scalar.kind) {
930 (Uo::Negate, Sk::Float | Sk::Sint) => {}
931 (Uo::LogicalNot, Sk::Bool) => {}
932 (Uo::BitwiseNot, Sk::Sint | Sk::Uint) => {}
933 _ => return Err(ExpressionError::InvalidUnaryOperandType(op, expr)),
934 }
935 ShaderStages::all()
936 }
937 E::Binary { op, left, right } => {
938 use crate::BinaryOperator as Bo;
939 let left_inner = &resolver[left];
940 let right_inner = &resolver[right];
941 let good = match op {
942 Bo::Add | Bo::Subtract => match *left_inner {
943 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
944 Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
945 Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
946 },
947 Ti::Matrix { .. } | Ti::CooperativeMatrix { .. } => {
948 left_inner == right_inner
949 }
950 _ => false,
951 },
952 Bo::Divide | Bo::Modulo => match *left_inner {
953 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
954 Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
955 Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
956 },
957 _ => false,
958 },
959 Bo::Multiply => {
960 let kind_allowed = match left_inner.scalar_kind() {
961 Some(Sk::Uint | Sk::Sint | Sk::Float) => true,
962 Some(Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat) | None => false,
963 };
964 let types_match = match (left_inner, right_inner) {
965 (&Ti::Scalar(scalar1), &Ti::Scalar(scalar2))
967 | (
968 &Ti::Vector {
969 scalar: scalar1, ..
970 },
971 &Ti::Scalar(scalar2),
972 )
973 | (
974 &Ti::Scalar(scalar1),
975 &Ti::Vector {
976 scalar: scalar2, ..
977 },
978 ) => scalar1 == scalar2,
979 (
981 &Ti::Scalar(Sc {
982 kind: Sk::Float, ..
983 }),
984 &Ti::Matrix { .. },
985 )
986 | (
987 &Ti::Matrix { .. },
988 &Ti::Scalar(Sc {
989 kind: Sk::Float, ..
990 }),
991 ) => true,
992 (
994 &Ti::Vector {
995 size: size1,
996 scalar: scalar1,
997 },
998 &Ti::Vector {
999 size: size2,
1000 scalar: scalar2,
1001 },
1002 ) => scalar1 == scalar2 && size1 == size2,
1003 (
1005 &Ti::Matrix { columns, .. },
1006 &Ti::Vector {
1007 size,
1008 scalar:
1009 Sc {
1010 kind: Sk::Float, ..
1011 },
1012 },
1013 ) => columns == size,
1014 (
1016 &Ti::Vector {
1017 size,
1018 scalar:
1019 Sc {
1020 kind: Sk::Float, ..
1021 },
1022 },
1023 &Ti::Matrix { rows, .. },
1024 ) => size == rows,
1025 (&Ti::Matrix { columns, .. }, &Ti::Matrix { rows, .. }) => {
1027 columns == rows
1028 }
1029 (&Ti::Scalar(s1), &Ti::CooperativeMatrix { scalar: s2, .. })
1031 | (&Ti::CooperativeMatrix { scalar: s1, .. }, &Ti::Scalar(s2)) => {
1032 s1 == s2
1033 }
1034 _ => false,
1035 };
1036 let left_width = left_inner.scalar_width().unwrap_or(0);
1037 let right_width = right_inner.scalar_width().unwrap_or(0);
1038 kind_allowed && types_match && left_width == right_width
1039 }
1040 Bo::Equal | Bo::NotEqual => left_inner.is_sized() && left_inner == right_inner,
1041 Bo::Less | Bo::LessEqual | Bo::Greater | Bo::GreaterEqual => {
1042 match *left_inner {
1043 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
1044 Sk::Uint | Sk::Sint | Sk::Float => left_inner == right_inner,
1045 Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false,
1046 },
1047 ref other => {
1048 log::debug!("Op {op:?} left type {other:?}");
1049 false
1050 }
1051 }
1052 }
1053 Bo::LogicalAnd | Bo::LogicalOr => match *left_inner {
1054 Ti::Scalar(Sc { kind: Sk::Bool, .. })
1055 | Ti::Vector {
1056 scalar: Sc { kind: Sk::Bool, .. },
1057 ..
1058 } => left_inner == right_inner,
1059 ref other => {
1060 log::debug!("Op {op:?} left type {other:?}");
1061 false
1062 }
1063 },
1064 Bo::And | Bo::InclusiveOr => match *left_inner {
1065 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
1066 Sk::Bool | Sk::Sint | Sk::Uint => left_inner == right_inner,
1067 Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,
1068 },
1069 ref other => {
1070 log::debug!("Op {op:?} left type {other:?}");
1071 false
1072 }
1073 },
1074 Bo::ExclusiveOr => match *left_inner {
1075 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } => match scalar.kind {
1076 Sk::Sint | Sk::Uint => left_inner == right_inner,
1077 Sk::Bool | Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false,
1078 },
1079 ref other => {
1080 log::debug!("Op {op:?} left type {other:?}");
1081 false
1082 }
1083 },
1084 Bo::ShiftLeft | Bo::ShiftRight => {
1085 let (base_size, base_scalar) = match *left_inner {
1086 Ti::Scalar(scalar) => (Ok(None), scalar),
1087 Ti::Vector { size, scalar } => (Ok(Some(size)), scalar),
1088 ref other => {
1089 log::debug!("Op {op:?} base type {other:?}");
1090 (Err(()), Sc::BOOL)
1091 }
1092 };
1093 let shift_size = match *right_inner {
1094 Ti::Scalar(Sc { kind: Sk::Uint, .. }) => Ok(None),
1095 Ti::Vector {
1096 size,
1097 scalar: Sc { kind: Sk::Uint, .. },
1098 } => Ok(Some(size)),
1099 ref other => {
1100 log::debug!("Op {op:?} shift type {other:?}");
1101 Err(())
1102 }
1103 };
1104 match base_scalar.kind {
1105 Sk::Sint | Sk::Uint => base_size.is_ok() && base_size == shift_size,
1106 Sk::Float | Sk::AbstractInt | Sk::AbstractFloat | Sk::Bool => false,
1107 }
1108 }
1109 };
1110 if !good {
1111 log::debug!(
1112 "Left: {:?} of type {:?}",
1113 function.expressions[left],
1114 left_inner
1115 );
1116 log::debug!(
1117 "Right: {:?} of type {:?}",
1118 function.expressions[right],
1119 right_inner
1120 );
1121 return Err(ExpressionError::InvalidBinaryOperandTypes {
1122 op,
1123 lhs_expr: left,
1124 lhs_type: left_inner.clone(),
1125 rhs_expr: right,
1126 rhs_type: right_inner.clone(),
1127 });
1128 }
1129 if matches!(op, Bo::ShiftLeft | Bo::ShiftRight) {
1131 Self::validate_constant_shift_amounts(left_inner, right, module, function)?;
1132 }
1133 if matches!(op, Bo::Divide | Bo::Modulo) {
1135 Self::validate_constant_divisor(left_inner, right, module, function)?;
1136 }
1137 ShaderStages::all()
1138 }
1139 E::Select {
1140 condition,
1141 accept,
1142 reject,
1143 } => {
1144 let accept_inner = &resolver[accept];
1145 let reject_inner = &resolver[reject];
1146 let condition_ty = &resolver[condition];
1147 let condition_good = match *condition_ty {
1148 Ti::Scalar(Sc {
1149 kind: Sk::Bool,
1150 width: _,
1151 }) => {
1152 match *accept_inner {
1155 Ti::Scalar { .. } | Ti::Vector { .. } => true,
1156 _ => false,
1157 }
1158 }
1159 Ti::Vector {
1160 size,
1161 scalar:
1162 Sc {
1163 kind: Sk::Bool,
1164 width: _,
1165 },
1166 } => match *accept_inner {
1167 Ti::Vector {
1168 size: other_size, ..
1169 } => size == other_size,
1170 _ => false,
1171 },
1172 _ => false,
1173 };
1174 if accept_inner != reject_inner {
1175 return Err(ExpressionError::SelectValuesTypeMismatch {
1176 accept: accept_inner.clone(),
1177 reject: reject_inner.clone(),
1178 });
1179 }
1180 if !condition_good {
1181 return Err(ExpressionError::SelectConditionNotABool {
1182 actual: condition_ty.clone(),
1183 });
1184 }
1185 ShaderStages::all()
1186 }
1187 E::Derivative { expr, .. } => {
1188 let Some((_, scalar)) = resolver[expr].vector_size_and_scalar() else {
1189 return Err(ExpressionError::InvalidDerivative);
1190 };
1191 if scalar.kind != Sk::Float || scalar.width < 4 {
1192 return Err(ExpressionError::InvalidDerivative);
1195 }
1196 ShaderStages::FRAGMENT
1197 }
1198 E::Relational { fun, argument } => {
1199 use crate::RelationalFunction as Rf;
1200 let argument_inner = &resolver[argument];
1201 match fun {
1202 Rf::All | Rf::Any => match *argument_inner {
1203 Ti::Vector {
1204 scalar: Sc { kind: Sk::Bool, .. },
1205 ..
1206 } => {}
1207 ref other => {
1208 log::debug!("All/Any of type {other:?}");
1209 return Err(ExpressionError::InvalidBooleanVector(argument));
1210 }
1211 },
1212 Rf::IsNan | Rf::IsInf => match *argument_inner {
1213 Ti::Scalar(scalar) | Ti::Vector { scalar, .. }
1214 if scalar.kind == Sk::Float => {}
1215 ref other => {
1216 log::debug!("Float test of type {other:?}");
1217 return Err(ExpressionError::InvalidFloatArgument(argument));
1218 }
1219 },
1220 }
1221 ShaderStages::all()
1222 }
1223 E::Math {
1224 fun,
1225 arg,
1226 arg1,
1227 arg2,
1228 arg3,
1229 } => {
1230 if matches!(
1231 fun,
1232 crate::MathFunction::QuantizeToF16
1233 | crate::MathFunction::Pack2x16float
1234 | crate::MathFunction::Unpack2x16float
1235 ) && !self
1236 .capabilities
1237 .contains(crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32)
1238 {
1239 return Err(ExpressionError::MissingCapabilities(
1240 crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32,
1241 ));
1242 }
1243
1244 let actuals: &[_] = match (arg1, arg2, arg3) {
1245 (None, None, None) => &[arg],
1246 (Some(arg1), None, None) => &[arg, arg1],
1247 (Some(arg1), Some(arg2), None) => &[arg, arg1, arg2],
1248 (Some(arg1), Some(arg2), Some(arg3)) => &[arg, arg1, arg2, arg3],
1249 _ => return Err(ExpressionError::WrongArgumentCount(fun)),
1250 };
1251
1252 let resolve = |arg| &resolver[arg];
1253 let actual_types: &[_] = match *actuals {
1254 [arg0] => &[resolve(arg0)],
1255 [arg0, arg1] => &[resolve(arg0), resolve(arg1)],
1256 [arg0, arg1, arg2] => &[resolve(arg0), resolve(arg1), resolve(arg2)],
1257 [arg0, arg1, arg2, arg3] => {
1258 &[resolve(arg0), resolve(arg1), resolve(arg2), resolve(arg3)]
1259 }
1260 _ => unreachable!(),
1261 };
1262
1263 let mut overloads = fun.overloads();
1265 log::debug!(
1266 "initial overloads for {:?}: {:#?}",
1267 fun,
1268 overloads.for_debug(&module.types)
1269 );
1270
1271 for (i, (&expr, &ty)) in actuals.iter().zip(actual_types).enumerate() {
1279 overloads = overloads.arg(i, ty, &module.types);
1282 log::debug!(
1283 "overloads after arg {i}: {:#?}",
1284 overloads.for_debug(&module.types)
1285 );
1286
1287 if overloads.is_empty() {
1288 log::debug!("all overloads eliminated");
1289 return Err(ExpressionError::InvalidArgumentType(fun, i as u32, expr));
1290 }
1291 }
1292
1293 if actuals.len() < overloads.min_arguments() {
1294 return Err(ExpressionError::WrongArgumentCount(fun));
1295 }
1296
1297 ShaderStages::all()
1298 }
1299 E::As {
1300 expr,
1301 kind,
1302 convert,
1303 } => {
1304 let mut base_scalar = match resolver[expr] {
1305 crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => {
1306 scalar
1307 }
1308 crate::TypeInner::Matrix { scalar, .. } => scalar,
1309 _ => return Err(ExpressionError::InvalidCastArgument),
1310 };
1311 base_scalar.kind = kind;
1312 if let Some(width) = convert {
1313 base_scalar.width = width;
1314 }
1315 if self.check_width(base_scalar).is_err() {
1316 return Err(ExpressionError::InvalidCastArgument);
1317 }
1318 ShaderStages::all()
1319 }
1320 E::CallResult(function) => mod_info.functions[function.index()].available_stages,
1321 E::AtomicResult { .. } => {
1322 ShaderStages::all()
1327 }
1328 E::WorkGroupUniformLoadResult { ty } => {
1329 if self.types[ty.index()]
1330 .flags
1331 .contains(TypeFlags::SIZED | TypeFlags::CONSTRUCTIBLE)
1334 {
1335 ShaderStages::COMPUTE_LIKE
1336 } else {
1337 return Err(ExpressionError::InvalidWorkGroupUniformLoadResultType(ty));
1338 }
1339 }
1340 E::ArrayLength(expr) => match resolver[expr] {
1341 Ti::Pointer { base, .. } => {
1342 let base_ty = &resolver.types[base];
1343 if let Ti::Array {
1344 size: crate::ArraySize::Dynamic,
1345 ..
1346 } = base_ty.inner
1347 {
1348 ShaderStages::all()
1349 } else {
1350 return Err(ExpressionError::InvalidArrayType(expr));
1351 }
1352 }
1353 ref other => {
1354 log::debug!("Array length of {other:?}");
1355 return Err(ExpressionError::InvalidArrayType(expr));
1356 }
1357 },
1358 E::RayQueryProceedResult => ShaderStages::all(),
1359 E::RayQueryGetIntersection {
1360 query,
1361 committed: _,
1362 } => match resolver[query] {
1363 Ti::Pointer {
1364 base,
1365 space: crate::AddressSpace::Function,
1366 } => match resolver.types[base].inner {
1367 Ti::RayQuery { .. } => ShaderStages::all(),
1368 ref other => {
1369 log::debug!("Intersection result of a pointer to {other:?}");
1370 return Err(ExpressionError::InvalidRayQueryType(query));
1371 }
1372 },
1373 ref other => {
1374 log::debug!("Intersection result of {other:?}");
1375 return Err(ExpressionError::InvalidRayQueryType(query));
1376 }
1377 },
1378 E::RayQueryVertexPositions {
1379 query,
1380 committed: _,
1381 } => match resolver[query] {
1382 Ti::Pointer {
1383 base,
1384 space: crate::AddressSpace::Function,
1385 } => match resolver.types[base].inner {
1386 Ti::RayQuery {
1387 vertex_return: true,
1388 } => ShaderStages::all(),
1389 ref other => {
1390 log::debug!("Intersection result of a pointer to {other:?}");
1391 return Err(ExpressionError::InvalidRayQueryType(query));
1392 }
1393 },
1394 ref other => {
1395 log::debug!("Intersection result of {other:?}");
1396 return Err(ExpressionError::InvalidRayQueryType(query));
1397 }
1398 },
1399 E::SubgroupBallotResult | E::SubgroupOperationResult { .. } => self.subgroup_stages,
1400 E::CooperativeLoad { ref data, .. } => {
1401 if resolver[data.pointer]
1402 .pointer_base_type()
1403 .and_then(|tr| tr.inner_with(&module.types).scalar())
1404 .is_none()
1405 {
1406 return Err(ExpressionError::InvalidPointerType(data.pointer));
1407 }
1408 ShaderStages::COMPUTE
1409 }
1410 E::CooperativeMultiplyAdd { a, b, c } => {
1411 let roles = [
1412 crate::CooperativeRole::A,
1413 crate::CooperativeRole::B,
1414 crate::CooperativeRole::C,
1415 ];
1416 for (operand, expected_role) in [a, b, c].into_iter().zip(roles) {
1417 match resolver[operand] {
1418 Ti::CooperativeMatrix { role, .. } if role == expected_role => {}
1419 ref other => {
1420 log::debug!("{expected_role:?} operand type: {other:?}");
1421 return Err(ExpressionError::InvalidCooperativeOperand(a));
1422 }
1423 }
1424 }
1425 ShaderStages::COMPUTE
1426 }
1427 };
1428 Ok(stages)
1429 }
1430
1431 fn global_var_ty(
1432 module: &crate::Module,
1433 function: &crate::Function,
1434 expr: Handle<crate::Expression>,
1435 ) -> Result<Handle<crate::Type>, ExpressionError> {
1436 use crate::Expression as Ex;
1437
1438 match function.expressions[expr] {
1439 Ex::GlobalVariable(var_handle) => Ok(module.global_variables[var_handle].ty),
1440 Ex::FunctionArgument(i) => Ok(function.arguments[i as usize].ty),
1441 Ex::Access { base, .. } | Ex::AccessIndex { base, .. } => {
1442 match function.expressions[base] {
1443 Ex::GlobalVariable(var_handle) => {
1444 let array_ty = module.global_variables[var_handle].ty;
1445
1446 match module.types[array_ty].inner {
1447 crate::TypeInner::BindingArray { base, .. } => Ok(base),
1448 _ => Err(ExpressionError::ExpectedBindingArrayType(array_ty)),
1449 }
1450 }
1451 _ => Err(ExpressionError::ExpectedGlobalVariable),
1452 }
1453 }
1454 _ => Err(ExpressionError::ExpectedGlobalVariable),
1455 }
1456 }
1457
1458 pub fn validate_literal(&self, literal: crate::Literal) -> Result<(), LiteralError> {
1459 let _ = self.check_width(literal.scalar())?;
1460 check_literal_value(literal)?;
1461
1462 Ok(())
1463 }
1464}
1465
1466pub const fn check_literal_value(literal: crate::Literal) -> Result<(), LiteralError> {
1467 let is_nan = match literal {
1468 crate::Literal::F64(v) => v.is_nan(),
1469 crate::Literal::F32(v) => v.is_nan(),
1470 _ => false,
1471 };
1472 if is_nan {
1473 return Err(LiteralError::NaN);
1474 }
1475
1476 let is_infinite = match literal {
1477 crate::Literal::F64(v) => v.is_infinite(),
1478 crate::Literal::F32(v) => v.is_infinite(),
1479 _ => false,
1480 };
1481 if is_infinite {
1482 return Err(LiteralError::Infinity);
1483 }
1484
1485 Ok(())
1486}
1487
1488#[cfg(test)]
1489fn validate_with_expression(
1491 expr: crate::Expression,
1492 caps: super::Capabilities,
1493) -> Result<ModuleInfo, Box<crate::span::WithSpan<super::ValidationError>>> {
1494 use crate::span::Span;
1495
1496 let mut function = crate::Function::default();
1497 function.expressions.append(expr, Span::default());
1498 function.body.push(
1499 crate::Statement::Emit(function.expressions.range_from(0)),
1500 Span::default(),
1501 );
1502
1503 let mut module = crate::Module::default();
1504 module.functions.append(function, Span::default());
1505
1506 let mut validator = super::Validator::new(super::ValidationFlags::EXPRESSIONS, caps);
1507
1508 validator.validate(&module)
1509}
1510
1511#[cfg(test)]
1512fn validate_with_const_expression(
1514 expr: crate::Expression,
1515 caps: super::Capabilities,
1516) -> Result<ModuleInfo, Box<crate::span::WithSpan<super::ValidationError>>> {
1517 use crate::span::Span;
1518
1519 let mut module = crate::Module::default();
1520 module.global_expressions.append(expr, Span::default());
1521
1522 let mut validator = super::Validator::new(super::ValidationFlags::CONSTANTS, caps);
1523
1524 validator.validate(&module)
1525}
1526
1527#[test]
1529fn f64_runtime_literals() {
1530 let result = validate_with_expression(
1531 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1532 super::Capabilities::default(),
1533 );
1534 let error = result.unwrap_err().into_inner();
1535 assert!(matches!(
1536 error,
1537 crate::valid::ValidationError::Function {
1538 source: super::FunctionError::Expression {
1539 source: ExpressionError::Literal(LiteralError::Width(
1540 super::r#type::WidthError::MissingCapability {
1541 name: "f64",
1542 flag: "FLOAT64",
1543 }
1544 ),),
1545 ..
1546 },
1547 ..
1548 }
1549 ));
1550
1551 let result = validate_with_expression(
1552 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1553 super::Capabilities::default() | super::Capabilities::FLOAT64,
1554 );
1555 assert!(result.is_ok());
1556}
1557
1558#[test]
1560fn f64_const_literals() {
1561 let result = validate_with_const_expression(
1562 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1563 super::Capabilities::default(),
1564 );
1565 let error = result.unwrap_err().into_inner();
1566 assert!(matches!(
1567 error,
1568 crate::valid::ValidationError::ConstExpression {
1569 source: ConstExpressionError::Literal(LiteralError::Width(
1570 super::r#type::WidthError::MissingCapability {
1571 name: "f64",
1572 flag: "FLOAT64",
1573 }
1574 )),
1575 ..
1576 }
1577 ));
1578
1579 let result = validate_with_const_expression(
1580 crate::Expression::Literal(crate::Literal::F64(0.57721_56649)),
1581 super::Capabilities::default() | super::Capabilities::FLOAT64,
1582 );
1583 assert!(result.is_ok());
1584}