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