Skip to main content

naga/valid/
expression.rs

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