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