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