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