Skip to main content

naga/valid/
mod.rs

1/*!
2Shader validator.
3*/
4
5mod analyzer;
6mod compose;
7mod expression;
8mod function;
9mod handles;
10pub(crate) mod immediates;
11mod interface;
12mod r#type;
13
14use alloc::{boxed::Box, string::String, vec, vec::Vec};
15use core::ops;
16
17use bit_set::BitSet;
18
19use crate::{
20    arena::{Handle, HandleSet},
21    proc::{ExpressionKindTracker, LayoutError, Layouter, TypeResolution},
22    FastHashSet,
23};
24
25//TODO: analyze the model at the same time as we validate it,
26// merge the corresponding matches over expressions and statements.
27
28use crate::span::{AddSpan as _, WithSpan};
29pub use analyzer::{ExpressionInfo, FunctionInfo, GlobalUse, Uniformity, UniformityRequirements};
30pub use compose::ComposeError;
31pub use expression::{check_literal_value, LiteralError};
32pub use expression::{ConstExpressionError, ExpressionError};
33pub use function::{CallError, FunctionError, LocalVariableError, SubgroupError};
34pub use immediates::{ImmediateSlots, ImmediateSlotsOverflowError, ImmediateUsage};
35pub use interface::{EntryPointError, GlobalVariableError, VaryingError};
36pub use r#type::{Disalignment, ImmediateError, TypeError, TypeFlags, WidthError};
37
38use self::handles::InvalidHandleError;
39
40/// Maximum size of a type, in bytes.
41pub const MAX_TYPE_SIZE: u32 = i32::MAX as u32;
42
43bitflags::bitflags! {
44    /// Validation flags.
45    ///
46    /// If you are working with trusted shaders, then you may be able
47    /// to save some time by skipping validation.
48    ///
49    /// If you do not perform full validation, invalid shaders may
50    /// cause Naga to panic. If you do perform full validation and
51    /// [`Validator::validate`] returns `Ok`, then Naga promises that
52    /// code generation will either succeed or return an error; it
53    /// should never panic.
54    ///
55    /// The default value for `ValidationFlags` is
56    /// `ValidationFlags::all()`.
57    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
58    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
59    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
60    pub struct ValidationFlags: u8 {
61        /// Expressions.
62        const EXPRESSIONS = 0x1;
63        /// Statements and blocks of them.
64        const BLOCKS = 0x2;
65        /// Uniformity of control flow for operations that require it.
66        const CONTROL_FLOW_UNIFORMITY = 0x4;
67        /// Host-shareable structure layouts.
68        const STRUCT_LAYOUTS = 0x8;
69        /// Constants.
70        const CONSTANTS = 0x10;
71        /// Group, binding, and location attributes.
72        const BINDINGS = 0x20;
73    }
74}
75
76impl Default for ValidationFlags {
77    fn default() -> Self {
78        Self::all()
79    }
80}
81
82bitflags::bitflags! {
83    /// Allowed IR capabilities.
84    #[must_use]
85    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
86    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
87    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
88    pub struct Capabilities: u64 {
89        /// Support for [`AddressSpace::Immediate`][1].
90        ///
91        /// [1]: crate::AddressSpace::Immediate
92        const IMMEDIATES = 1 << 0;
93        /// Float values with width = 8.
94        const FLOAT64 = 1 << 1;
95        /// Support for [`BuiltIn::PrimitiveIndex`][1].
96        ///
97        /// [1]: crate::BuiltIn::PrimitiveIndex
98        const PRIMITIVE_INDEX = 1 << 2;
99        /// Support for binding arrays of sampled textures and samplers.
100        const TEXTURE_AND_SAMPLER_BINDING_ARRAY = 1 << 3;
101        /// Support for binding arrays of uniform buffers.
102        const BUFFER_BINDING_ARRAY = 1 << 4;
103        /// Support for binding arrays of storage textures.
104        const STORAGE_TEXTURE_BINDING_ARRAY = 1 << 5;
105        /// Support for binding arrays of storage buffers.
106        const STORAGE_BUFFER_BINDING_ARRAY = 1 << 6;
107        /// Support for [`BuiltIn::ClipDistances`].
108        ///
109        /// [`BuiltIn::ClipDistances`]: crate::BuiltIn::ClipDistances
110        const CLIP_DISTANCES = 1 << 7;
111        /// Support for [`BuiltIn::CullDistance`].
112        ///
113        /// [`BuiltIn::CullDistance`]: crate::BuiltIn::CullDistance
114        const CULL_DISTANCE = 1 << 8;
115        /// Support for 16-bit normalized storage texture formats.
116        const STORAGE_TEXTURE_16BIT_NORM_FORMATS = 1 << 9;
117        /// Support for [`BuiltIn::ViewIndex`].
118        ///
119        /// [`BuiltIn::ViewIndex`]: crate::BuiltIn::ViewIndex
120        const MULTIVIEW = 1 << 10;
121        /// Support for `early_depth_test`.
122        const EARLY_DEPTH_TEST = 1 << 11;
123        /// Support for [`BuiltIn::SampleIndex`] and [`Sampling::Sample`].
124        ///
125        /// [`BuiltIn::SampleIndex`]: crate::BuiltIn::SampleIndex
126        /// [`Sampling::Sample`]: crate::Sampling::Sample
127        const MULTISAMPLED_SHADING = 1 << 12;
128        /// Support for ray queries and acceleration structures.
129        const RAY_QUERY = 1 << 13;
130        /// Support for generating two sources for blending from fragment shaders.
131        const DUAL_SOURCE_BLENDING = 1 << 14;
132        /// Support for arrayed cube textures.
133        const CUBE_ARRAY_TEXTURES = 1 << 15;
134        /// Support for 64-bit signed and unsigned integers.
135        const SHADER_INT64 = 1 << 16;
136        /// Support for subgroup operations (except barriers) in fragment and compute shaders.
137        ///
138        /// Subgroup operations in the vertex stage require
139        /// [`Capabilities::SUBGROUP_VERTEX_STAGE`] in addition to `Capabilities::SUBGROUP`.
140        /// (But note that `create_validator` automatically sets
141        /// `Capabilities::SUBGROUP` whenever `Features::SUBGROUP_VERTEX` is
142        /// available.)
143        ///
144        /// Subgroup barriers require [`Capabilities::SUBGROUP_BARRIER`] in addition to
145        /// `Capabilities::SUBGROUP`.
146        const SUBGROUP = 1 << 17;
147        /// Support for subgroup barriers in compute shaders.
148        ///
149        /// Requires [`Capabilities::SUBGROUP`]. Without it, enables nothing.
150        const SUBGROUP_BARRIER = 1 << 18;
151        /// Support for subgroup operations (not including barriers) in the vertex stage.
152        ///
153        /// Without [`Capabilities::SUBGROUP`], enables nothing. (But note that
154        /// `create_validator` automatically sets `Capabilities::SUBGROUP`
155        /// whenever `Features::SUBGROUP_VERTEX` is available.)
156        const SUBGROUP_VERTEX_STAGE = 1 << 19;
157        /// Support for [`AtomicFunction::Min`] and [`AtomicFunction::Max`] on
158        /// 64-bit integers in the [`Storage`] address space, when the return
159        /// value is not used.
160        ///
161        /// This is the only 64-bit atomic functionality available on Metal 3.1.
162        ///
163        /// [`AtomicFunction::Min`]: crate::AtomicFunction::Min
164        /// [`AtomicFunction::Max`]: crate::AtomicFunction::Max
165        /// [`Storage`]: crate::AddressSpace::Storage
166        const SHADER_INT64_ATOMIC_MIN_MAX = 1 << 20;
167        /// Support for all atomic operations on 64-bit integers.
168        const SHADER_INT64_ATOMIC_ALL_OPS = 1 << 21;
169        /// Support for [`AtomicFunction::Add`], [`AtomicFunction::Sub`],
170        /// and [`AtomicFunction::Exchange { compare: None }`] on 32-bit floating-point numbers
171        /// in the [`Storage`] address space.
172        ///
173        /// [`AtomicFunction::Add`]: crate::AtomicFunction::Add
174        /// [`AtomicFunction::Sub`]: crate::AtomicFunction::Sub
175        /// [`AtomicFunction::Exchange { compare: None }`]: crate::AtomicFunction::Exchange
176        /// [`Storage`]: crate::AddressSpace::Storage
177        const SHADER_FLOAT32_ATOMIC = 1 << 22;
178        /// Support for atomic operations on images.
179        const TEXTURE_ATOMIC = 1 << 23;
180        /// Support for atomic operations on 64-bit images.
181        const TEXTURE_INT64_ATOMIC = 1 << 24;
182        /// Support for ray queries returning vertex position
183        const RAY_HIT_VERTEX_POSITION = 1 << 25;
184        /// Support for 16-bit floating-point types.
185        const SHADER_FLOAT16 = 1 << 26;
186        /// Support for [`ImageClass::External`]
187        const TEXTURE_EXTERNAL = 1 << 27;
188        /// Support for `quantizeToF16`, `pack2x16float`, and `unpack2x16float`, which store
189        /// `f16`-precision values in `f32`s.
190        const SHADER_FLOAT16_IN_FLOAT32 = 1 << 28;
191        /// Support for fragment shader barycentric coordinates.
192        const SHADER_BARYCENTRICS = 1 << 29;
193        /// Support for task shaders, mesh shaders, and per-primitive fragment inputs
194        const MESH_SHADER = 1 << 30;
195        /// Support for mesh shaders which output points.
196        const MESH_SHADER_POINT_TOPOLOGY = 1 << 31;
197        /// Support for non-uniform indexing of binding arrays of sampled textures and samplers.
198        const TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 32;
199        /// Support for non-uniform indexing of binding arrays of uniform buffers.
200        const BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 33;
201        /// Support for non-uniform indexing of binding arrays of storage textures.
202        const STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 34;
203        /// Support for non-uniform indexing of binding arrays of storage buffers.
204        const STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 35;
205        /// Support for cooperative matrix types and operations
206        const COOPERATIVE_MATRIX = 1 << 36;
207        /// Support for per-vertex fragment input.
208        const PER_VERTEX = 1 << 37;
209        /// Support for ray generation, any hit, closest hit, and miss shaders.
210        const RAY_TRACING_PIPELINE = 1 << 38;
211        /// Support for draw index builtin
212        const DRAW_INDEX = 1 << 39;
213        /// Support for binding arrays of acceleration structures.
214        const ACCELERATION_STRUCTURE_BINDING_ARRAY = 1 << 40;
215        /// Support for the `@coherent` memory decoration on storage buffers.
216        const MEMORY_DECORATION_COHERENT = 1 << 41;
217        /// Support for the `@volatile` memory decoration on storage buffers.
218        const MEMORY_DECORATION_VOLATILE = 1 << 42;
219        /// Support for 16-bit integer types.
220        const SHADER_INT16 = 1 << 43;
221        /// Support for [`Interpolation::Linear`] (`@interpolate(linear)` in WGSL).
222        ///
223        /// This is core WebGPU, but GLSL ES (and thus WebGL) has no `noperspective` qualifier (unless enabled by extensions).
224        ///
225        /// [`Interpolation::Linear`]: crate::Interpolation::Linear
226        const LINEAR_INTERPOLATION = 1 << 44;
227        /// Support for `debugPrintf`.
228        const DEBUG_PRINTF = 1 << 45;
229    }
230}
231
232impl Capabilities {
233    /// Returns the extension corresponding to this capability, if there is one.
234    ///
235    /// This is used by integration tests.
236    #[cfg(feature = "wgsl-in")]
237    #[doc(hidden)]
238    pub const fn extension(&self) -> Option<crate::front::wgsl::ImplementedEnableExtension> {
239        use crate::front::wgsl::ImplementedEnableExtension as Ext;
240        match *self {
241            Self::DUAL_SOURCE_BLENDING => Some(Ext::DualSourceBlending),
242            // NOTE: `SHADER_FLOAT16_IN_FLOAT32` _does not_ require the `f16` extension
243            Self::SHADER_FLOAT16 => Some(Ext::F16),
244            Self::SHADER_INT16 => Some(Ext::WgpuInt16),
245            Self::CLIP_DISTANCES => Some(Ext::ClipDistances),
246            Self::MESH_SHADER => Some(Ext::WgpuMeshShader),
247            Self::RAY_QUERY => Some(Ext::WgpuRayQuery),
248            Self::RAY_HIT_VERTEX_POSITION => Some(Ext::WgpuRayQueryVertexReturn),
249            Self::COOPERATIVE_MATRIX => Some(Ext::WgpuCooperativeMatrix),
250            Self::RAY_TRACING_PIPELINE => Some(Ext::WgpuRayTracingPipeline),
251            Self::PER_VERTEX => Some(Ext::WgpuPerVertex),
252            Self::BUFFER_BINDING_ARRAY
253            | Self::BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
254            | Self::STORAGE_BUFFER_BINDING_ARRAY
255            | Self::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
256            | Self::STORAGE_TEXTURE_BINDING_ARRAY
257            | Self::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
258            | Self::TEXTURE_AND_SAMPLER_BINDING_ARRAY
259            | Self::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING => {
260                Some(Ext::WgpuBindingArray)
261            }
262            Self::DEBUG_PRINTF => Some(Ext::WgpuDebugPrintf),
263            _ => None,
264        }
265    }
266}
267
268impl Default for Capabilities {
269    fn default() -> Self {
270        Self::MULTISAMPLED_SHADING | Self::CUBE_ARRAY_TEXTURES
271    }
272}
273
274bitflags::bitflags! {
275    /// Supported subgroup operations
276    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
277    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
278    #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
279    pub struct SubgroupOperationSet: u8 {
280        /// Barriers
281        // Possibly elections, when that is supported.
282        // https://github.com/gfx-rs/wgpu/issues/6042#issuecomment-3272603431
283        // Contrary to what the name "basic" suggests, HLSL/DX12 support the
284        // other subgroup operations, but do not support subgroup barriers.
285        const BASIC = 1 << 0;
286        /// Any, All
287        const VOTE = 1 << 1;
288        /// reductions, scans
289        const ARITHMETIC = 1 << 2;
290        /// ballot, broadcast
291        const BALLOT = 1 << 3;
292        /// shuffle, shuffle xor
293        const SHUFFLE = 1 << 4;
294        /// shuffle up, down
295        const SHUFFLE_RELATIVE = 1 << 5;
296        // We don't support these operations yet
297        // /// Clustered
298        // const CLUSTERED = 1 << 6;
299        /// Quad supported
300        const QUAD_FRAGMENT_COMPUTE = 1 << 7;
301        // /// Quad supported in all stages
302        // const QUAD_ALL_STAGES = 1 << 8;
303    }
304}
305
306impl super::SubgroupOperation {
307    const fn required_operations(&self) -> SubgroupOperationSet {
308        use SubgroupOperationSet as S;
309        match *self {
310            Self::All | Self::Any => S::VOTE,
311            Self::Add | Self::Mul | Self::Min | Self::Max | Self::And | Self::Or | Self::Xor => {
312                S::ARITHMETIC
313            }
314        }
315    }
316}
317
318impl super::GatherMode {
319    const fn required_operations(&self) -> SubgroupOperationSet {
320        use SubgroupOperationSet as S;
321        match *self {
322            Self::BroadcastFirst | Self::Broadcast(_) => S::BALLOT,
323            Self::Shuffle(_) | Self::ShuffleXor(_) => S::SHUFFLE,
324            Self::ShuffleUp(_) | Self::ShuffleDown(_) => S::SHUFFLE_RELATIVE,
325            Self::QuadBroadcast(_) | Self::QuadSwap(_) => S::QUAD_FRAGMENT_COMPUTE,
326        }
327    }
328}
329
330bitflags::bitflags! {
331    /// Validation flags.
332    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
333    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
334    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
335    pub struct ShaderStages: u16 {
336        const VERTEX = 0x1;
337        const FRAGMENT = 0x2;
338        const COMPUTE = 0x4;
339        const MESH = 0x8;
340        const TASK = 0x10;
341        const RAY_GENERATION = 0x20;
342        const ANY_HIT = 0x40;
343        const CLOSEST_HIT = 0x80;
344        const MISS = 0x100;
345        const COMPUTE_LIKE = Self::COMPUTE.bits() | Self::TASK.bits() | Self::MESH.bits();
346    }
347}
348
349#[derive(Debug, Clone, Default)]
350#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
351#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
352pub struct ModuleInfo {
353    type_flags: Vec<TypeFlags>,
354    functions: Vec<FunctionInfo>,
355    entry_points: Vec<FunctionInfo>,
356    const_expression_types: Box<[TypeResolution]>,
357}
358
359impl ops::Index<Handle<crate::Type>> for ModuleInfo {
360    type Output = TypeFlags;
361    fn index(&self, handle: Handle<crate::Type>) -> &Self::Output {
362        &self.type_flags[handle.index()]
363    }
364}
365
366impl ops::Index<Handle<crate::Function>> for ModuleInfo {
367    type Output = FunctionInfo;
368    fn index(&self, handle: Handle<crate::Function>) -> &Self::Output {
369        &self.functions[handle.index()]
370    }
371}
372
373impl ops::Index<Handle<crate::Expression>> for ModuleInfo {
374    type Output = TypeResolution;
375    fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
376        &self.const_expression_types[handle.index()]
377    }
378}
379
380#[derive(Debug)]
381pub struct Validator {
382    flags: ValidationFlags,
383    capabilities: Capabilities,
384    subgroup_stages: ShaderStages,
385    subgroup_operations: SubgroupOperationSet,
386    types: Vec<r#type::TypeInfo>,
387    layouter: Layouter,
388    location_mask: BitSet,
389    ep_resource_bindings: FastHashSet<crate::ResourceBinding>,
390    switch_values: FastHashSet<crate::SwitchValue>,
391    valid_expression_list: Vec<Handle<crate::Expression>>,
392    valid_expression_set: HandleSet<crate::Expression>,
393    override_ids: FastHashSet<u16>,
394
395    /// Treat overrides whose initializers are not fully-evaluated
396    /// constant expressions as errors.
397    overrides_resolved: bool,
398
399    /// A checklist of expressions that must be visited by a specific kind of
400    /// statement.
401    ///
402    /// For example:
403    ///
404    /// - [`CallResult`] expressions must be visited by a [`Call`] statement.
405    /// - [`AtomicResult`] expressions must be visited by an [`Atomic`] statement.
406    ///
407    /// Be sure not to remove any [`Expression`] handle from this set unless
408    /// you've explicitly checked that it is the right kind of expression for
409    /// the visiting [`Statement`].
410    ///
411    /// [`CallResult`]: crate::Expression::CallResult
412    /// [`Call`]: crate::Statement::Call
413    /// [`AtomicResult`]: crate::Expression::AtomicResult
414    /// [`Atomic`]: crate::Statement::Atomic
415    /// [`Expression`]: crate::Expression
416    /// [`Statement`]: crate::Statement
417    needs_visit: HandleSet<crate::Expression>,
418
419    /// Whether any trace rays call is called, and whether all have vertex return.
420    /// If one call doesn't use vertex ruturn, builtins for triangle vertex positions
421    /// (not yet implemented) are not allowed.
422    trace_rays_vertex_return: TraceRayVertexReturnState,
423
424    /// The type of the ray payload, this must always be the same type in a particular
425    /// entrypoint
426    trace_rays_payload_type: Option<Handle<crate::Type>>,
427}
428
429#[derive(Debug)]
430enum TraceRayVertexReturnState {
431    /// No trace ray calls yet have been found.
432    NoTraceRays,
433    /// Trace ray calls have been found, at least
434    /// one uses an acceleration structure that
435    /// does not have the flag enabling vertex return.
436    #[expect(
437        unused,
438        reason = "Don't yet have vertex return builtins to return this error for."
439    )]
440    NoVertexReturn(crate::Span),
441    /// Trace ray calls have been found, all
442    /// acceleration structures have the flag enabling
443    /// vertex return.
444    VertexReturn,
445}
446
447#[derive(Clone, Debug, thiserror::Error)]
448#[cfg_attr(test, derive(PartialEq))]
449pub enum ConstantError {
450    #[error("Initializer must be a const-expression")]
451    InitializerExprType,
452    #[error("The type doesn't match the constant")]
453    InvalidType,
454    #[error("The type is not constructible")]
455    NonConstructibleType,
456}
457
458#[derive(Clone, Debug, thiserror::Error)]
459#[cfg_attr(test, derive(PartialEq))]
460pub enum OverrideError {
461    #[error("Override name and ID are missing")]
462    MissingNameAndID,
463    #[error("Override ID must be unique")]
464    DuplicateID,
465    #[error("Initializer must be a const-expression or override-expression")]
466    InitializerExprType,
467    #[error("The type doesn't match the override")]
468    InvalidType,
469    #[error("The type is not constructible")]
470    NonConstructibleType,
471    #[error("The type is not a scalar")]
472    TypeNotScalar,
473    #[error("Override declarations are not allowed")]
474    NotAllowed,
475    #[error("Override is uninitialized")]
476    UninitializedOverride,
477    #[error("Constant expression {handle:?} is invalid")]
478    ConstExpression {
479        handle: Handle<crate::Expression>,
480        source: ConstExpressionError,
481    },
482}
483
484#[derive(Clone, Debug, thiserror::Error)]
485#[cfg_attr(test, derive(PartialEq))]
486pub enum ValidationError {
487    #[error(transparent)]
488    InvalidHandle(#[from] InvalidHandleError),
489    #[error(transparent)]
490    Layouter(#[from] LayoutError),
491    #[error("Type {handle:?} '{name}' is invalid")]
492    Type {
493        handle: Handle<crate::Type>,
494        name: String,
495        source: TypeError,
496    },
497    #[error("Constant expression {handle:?} is invalid")]
498    ConstExpression {
499        handle: Handle<crate::Expression>,
500        source: ConstExpressionError,
501    },
502    #[error("Array size expression {handle:?} is not strictly positive")]
503    ArraySizeError { handle: Handle<crate::Expression> },
504    #[error("Constant {handle:?} '{name}' is invalid")]
505    Constant {
506        handle: Handle<crate::Constant>,
507        name: String,
508        source: ConstantError,
509    },
510    #[error("Override {handle:?} '{name}' is invalid")]
511    Override {
512        handle: Handle<crate::Override>,
513        name: String,
514        source: OverrideError,
515    },
516    #[error("Global variable {handle:?} '{name}' is invalid")]
517    GlobalVariable {
518        handle: Handle<crate::GlobalVariable>,
519        name: String,
520        source: GlobalVariableError,
521    },
522    #[error("Function {handle:?} '{name}' is invalid")]
523    Function {
524        handle: Handle<crate::Function>,
525        name: String,
526        source: FunctionError,
527    },
528    #[error("Entry point {name} at {stage:?} is invalid")]
529    EntryPoint {
530        stage: crate::ShaderStage,
531        name: String,
532        source: EntryPointError,
533    },
534    #[error("Module is corrupted")]
535    Corrupted,
536}
537
538impl crate::TypeInner {
539    const fn is_sized(&self) -> bool {
540        match *self {
541            Self::Scalar { .. }
542            | Self::Vector { .. }
543            | Self::Matrix { .. }
544            | Self::CooperativeMatrix { .. }
545            | Self::Array {
546                size: crate::ArraySize::Constant(_),
547                ..
548            }
549            | Self::Atomic { .. }
550            | Self::Pointer { .. }
551            | Self::ValuePointer { .. }
552            | Self::Struct { .. } => true,
553            Self::Array { .. }
554            | Self::Image { .. }
555            | Self::Sampler { .. }
556            | Self::AccelerationStructure { .. }
557            | Self::RayQuery { .. }
558            | Self::BindingArray { .. } => false,
559        }
560    }
561
562    /// Return the `ImageDimension` for which `self` is an appropriate coordinate.
563    const fn image_storage_coordinates(&self) -> Option<crate::ImageDimension> {
564        match *self {
565            Self::Scalar(crate::Scalar {
566                kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
567                ..
568            }) => Some(crate::ImageDimension::D1),
569            Self::Vector {
570                size: crate::VectorSize::Bi,
571                scalar:
572                    crate::Scalar {
573                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
574                        ..
575                    },
576            } => Some(crate::ImageDimension::D2),
577            Self::Vector {
578                size: crate::VectorSize::Tri,
579                scalar:
580                    crate::Scalar {
581                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
582                        ..
583                    },
584            } => Some(crate::ImageDimension::D3),
585            _ => None,
586        }
587    }
588}
589
590impl Validator {
591    /// Create a validator for Naga [`Module`]s.
592    ///
593    /// The `flags` argument indicates which stages of validation the
594    /// returned `Validator` should perform. Skipping stages can make
595    /// validation somewhat faster, but the validator may not reject some
596    /// invalid modules. Regardless of `flags`, validation always returns
597    /// a usable [`ModuleInfo`] value on success.
598    ///
599    /// If `flags` contains everything in `ValidationFlags::default()`,
600    /// then the returned Naga [`Validator`] will reject any [`Module`]
601    /// that would use capabilities not included in `capabilities`.
602    ///
603    /// [`Module`]: crate::Module
604    pub fn new(flags: ValidationFlags, capabilities: Capabilities) -> Self {
605        let subgroup_operations = if capabilities.contains(Capabilities::SUBGROUP) {
606            use SubgroupOperationSet as S;
607            S::BASIC
608                | S::VOTE
609                | S::ARITHMETIC
610                | S::BALLOT
611                | S::SHUFFLE
612                | S::SHUFFLE_RELATIVE
613                | S::QUAD_FRAGMENT_COMPUTE
614        } else {
615            SubgroupOperationSet::empty()
616        };
617        let subgroup_stages = {
618            let mut stages = ShaderStages::empty();
619            if capabilities.contains(Capabilities::SUBGROUP_VERTEX_STAGE) {
620                stages |= ShaderStages::VERTEX;
621            }
622            if capabilities.contains(Capabilities::SUBGROUP) {
623                stages |= ShaderStages::FRAGMENT | ShaderStages::COMPUTE_LIKE;
624            }
625            stages
626        };
627
628        Validator {
629            flags,
630            capabilities,
631            subgroup_stages,
632            subgroup_operations,
633            types: Vec::new(),
634            layouter: Layouter::default(),
635            location_mask: BitSet::new(),
636            ep_resource_bindings: FastHashSet::default(),
637            switch_values: FastHashSet::default(),
638            valid_expression_list: Vec::new(),
639            valid_expression_set: HandleSet::new(),
640            override_ids: FastHashSet::default(),
641            overrides_resolved: false,
642            needs_visit: HandleSet::new(),
643            trace_rays_vertex_return: TraceRayVertexReturnState::NoTraceRays,
644            trace_rays_payload_type: None,
645        }
646    }
647
648    // TODO(https://github.com/gfx-rs/wgpu/issues/8207): Consider removing this
649    pub const fn subgroup_stages(&mut self, stages: ShaderStages) -> &mut Self {
650        self.subgroup_stages = stages;
651        self
652    }
653
654    // TODO(https://github.com/gfx-rs/wgpu/issues/8207): Consider removing this
655    pub const fn subgroup_operations(&mut self, operations: SubgroupOperationSet) -> &mut Self {
656        self.subgroup_operations = operations;
657        self
658    }
659
660    /// Reset the validator internals
661    pub fn reset(&mut self) {
662        self.types.clear();
663        self.layouter.clear();
664        self.location_mask.make_empty();
665        self.ep_resource_bindings.clear();
666        self.switch_values.clear();
667        self.valid_expression_list.clear();
668        self.valid_expression_set.clear();
669        self.override_ids.clear();
670    }
671
672    fn validate_constant(
673        &self,
674        handle: Handle<crate::Constant>,
675        gctx: crate::proc::GlobalCtx,
676        mod_info: &ModuleInfo,
677        global_expr_kind: &ExpressionKindTracker,
678    ) -> Result<(), ConstantError> {
679        let con = &gctx.constants[handle];
680
681        let type_info = &self.types[con.ty.index()];
682        if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
683            return Err(ConstantError::NonConstructibleType);
684        }
685
686        if !global_expr_kind.is_const(con.init) {
687            return Err(ConstantError::InitializerExprType);
688        }
689
690        if !gctx.compare_types(&TypeResolution::Handle(con.ty), &mod_info[con.init]) {
691            return Err(ConstantError::InvalidType);
692        }
693
694        Ok(())
695    }
696
697    fn validate_override(
698        &mut self,
699        handle: Handle<crate::Override>,
700        gctx: crate::proc::GlobalCtx,
701        mod_info: &ModuleInfo,
702    ) -> Result<(), OverrideError> {
703        let o = &gctx.overrides[handle];
704
705        if let Some(id) = o.id {
706            if !self.override_ids.insert(id) {
707                return Err(OverrideError::DuplicateID);
708            }
709        }
710
711        let type_info = &self.types[o.ty.index()];
712        if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
713            return Err(OverrideError::NonConstructibleType);
714        }
715
716        match gctx.types[o.ty].inner {
717            crate::TypeInner::Scalar(
718                crate::Scalar::BOOL
719                | crate::Scalar::I16
720                | crate::Scalar::U16
721                | crate::Scalar::I32
722                | crate::Scalar::U32
723                | crate::Scalar::F16
724                | crate::Scalar::F32
725                | crate::Scalar::F64,
726            ) => {}
727            _ => return Err(OverrideError::TypeNotScalar),
728        }
729
730        if let Some(init) = o.init {
731            if !gctx.compare_types(&TypeResolution::Handle(o.ty), &mod_info[init]) {
732                return Err(OverrideError::InvalidType);
733            }
734        } else if self.overrides_resolved {
735            return Err(OverrideError::UninitializedOverride);
736        }
737
738        Ok(())
739    }
740
741    /// Check the given module to be valid.
742    pub fn validate(
743        &mut self,
744        module: &crate::Module,
745    ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
746        self.overrides_resolved = false;
747        self.validate_impl(module)
748    }
749
750    /// Check the given module to be valid, requiring overrides to be resolved.
751    ///
752    /// This is the same as [`validate`], except that any override
753    /// whose value is not a fully-evaluated constant expression is
754    /// treated as an error.
755    ///
756    /// [`validate`]: Validator::validate
757    pub fn validate_resolved_overrides(
758        &mut self,
759        module: &crate::Module,
760    ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
761        self.overrides_resolved = true;
762        self.validate_impl(module)
763    }
764
765    fn validate_impl(
766        &mut self,
767        module: &crate::Module,
768    ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
769        self.reset();
770        self.reset_types(module.types.len());
771
772        Self::validate_module_handles(module).map_err(|e| Box::new((*e).with_span()))?;
773
774        self.layouter.update(module.to_ctx()).map_err(|e| {
775            let handle = e.ty;
776            ValidationError::from(e).with_span_handle(handle, &module.types)
777        })?;
778
779        // These should all get overwritten.
780        let placeholder = TypeResolution::Value(crate::TypeInner::Scalar(crate::Scalar {
781            kind: crate::ScalarKind::Bool,
782            width: 0,
783        }));
784
785        let mut mod_info = ModuleInfo {
786            type_flags: Vec::with_capacity(module.types.len()),
787            functions: Vec::with_capacity(module.functions.len()),
788            entry_points: Vec::with_capacity(module.entry_points.len()),
789            const_expression_types: vec![placeholder; module.global_expressions.len()]
790                .into_boxed_slice(),
791        };
792
793        for (handle, ty) in module.types.iter() {
794            let ty_info = self
795                .validate_type(handle, module.to_ctx())
796                .map_err(|source| {
797                    ValidationError::Type {
798                        handle,
799                        name: ty.name.clone().unwrap_or_default(),
800                        source,
801                    }
802                    .with_span_handle(handle, &module.types)
803                })?;
804            debug_assert!(
805                ty_info.flags.contains(TypeFlags::CONSTRUCTIBLE)
806                    == module.types[handle].inner.is_constructible(&module.types)
807            );
808            mod_info.type_flags.push(ty_info.flags);
809            self.types[handle.index()] = ty_info;
810        }
811
812        {
813            let t = crate::Arena::new();
814            let resolve_context = crate::proc::ResolveContext::with_locals(module, &t, &[]);
815            for (handle, _) in module.global_expressions.iter() {
816                mod_info
817                    .process_const_expression(handle, &resolve_context, module.to_ctx())
818                    .map_err(|source| {
819                        ValidationError::ConstExpression { handle, source }
820                            .with_span_handle(handle, &module.global_expressions)
821                    })?
822            }
823        }
824
825        let global_expr_kind = ExpressionKindTracker::from_arena(&module.global_expressions);
826
827        if self.flags.contains(ValidationFlags::CONSTANTS) {
828            for (handle, _) in module.global_expressions.iter() {
829                self.validate_const_expression(
830                    handle,
831                    module.to_ctx(),
832                    &mod_info,
833                    &global_expr_kind,
834                )
835                .map_err(|source| {
836                    ValidationError::ConstExpression { handle, source }
837                        .with_span_handle(handle, &module.global_expressions)
838                })?
839            }
840
841            for (handle, constant) in module.constants.iter() {
842                self.validate_constant(handle, module.to_ctx(), &mod_info, &global_expr_kind)
843                    .map_err(|source| {
844                        ValidationError::Constant {
845                            handle,
846                            name: constant.name.clone().unwrap_or_default(),
847                            source,
848                        }
849                        .with_span_handle(handle, &module.constants)
850                    })?
851            }
852
853            for (handle, r#override) in module.overrides.iter() {
854                self.validate_override(handle, module.to_ctx(), &mod_info)
855                    .map_err(|source| {
856                        ValidationError::Override {
857                            handle,
858                            name: r#override.name.clone().unwrap_or_default(),
859                            source,
860                        }
861                        .with_span_handle(handle, &module.overrides)
862                    })?;
863            }
864        }
865
866        for (var_handle, var) in module.global_variables.iter() {
867            self.validate_global_var(var, module.to_ctx(), &mod_info, &global_expr_kind)
868                .map_err(|source| {
869                    ValidationError::GlobalVariable {
870                        handle: var_handle,
871                        name: var.name.clone().unwrap_or_default(),
872                        source,
873                    }
874                    .with_span_handle(var_handle, &module.global_variables)
875                })?;
876        }
877
878        for (handle, fun) in module.functions.iter() {
879            match self.validate_function(fun, module, &mod_info, false) {
880                Ok(info) => mod_info.functions.push(info),
881                Err(error) => {
882                    return Err(Box::new(error.and_then(|source| {
883                        ValidationError::Function {
884                            handle,
885                            name: fun.name.clone().unwrap_or_default(),
886                            source,
887                        }
888                        .with_span_handle(handle, &module.functions)
889                    })))
890                }
891            }
892        }
893
894        let mut ep_map = FastHashSet::default();
895        for ep in module.entry_points.iter() {
896            if !ep_map.insert((ep.stage, &ep.name)) {
897                return Err(Box::new(
898                    ValidationError::EntryPoint {
899                        stage: ep.stage,
900                        name: ep.name.clone(),
901                        source: EntryPointError::Conflict,
902                    }
903                    .with_span(),
904                )); // TODO: keep some EP span information?
905            }
906
907            match self.validate_entry_point(ep, module, &mod_info) {
908                Ok(info) => {
909                    mod_info.entry_points.push(info);
910                }
911                Err(error) => {
912                    return Err(Box::new(error.and_then(|source| {
913                        ValidationError::EntryPoint {
914                            stage: ep.stage,
915                            name: ep.name.clone(),
916                            source,
917                        }
918                        .with_span()
919                    })));
920                }
921            }
922        }
923
924        Ok(mod_info)
925    }
926}
927
928fn validate_atomic_compare_exchange_struct(
929    types: &crate::UniqueArena<crate::Type>,
930    members: &[crate::StructMember],
931    scalar_predicate: impl FnOnce(&crate::TypeInner) -> bool,
932) -> bool {
933    members.len() == 2
934        && members[0].name.as_deref() == Some("old_value")
935        && scalar_predicate(&types[members[0].ty].inner)
936        && members[1].name.as_deref() == Some("exchanged")
937        && types[members[1].ty].inner == crate::TypeInner::Scalar(crate::Scalar::BOOL)
938}