naga/valid/
type.rs

1use alloc::string::String;
2
3use super::Capabilities;
4use crate::{arena::Handle, proc::Alignment};
5
6bitflags::bitflags! {
7    /// Flags associated with [`Type`]s by [`Validator`].
8    ///
9    /// [`Type`]: crate::Type
10    /// [`Validator`]: crate::valid::Validator
11    #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
12    #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
13    #[repr(transparent)]
14    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
15    pub struct TypeFlags: u8 {
16        /// Can be used for data variables.
17        ///
18        /// This flag is required on types of local variables, function
19        /// arguments, array elements, and struct members.
20        ///
21        /// This includes all types except [`Image`], [`Sampler`],
22        /// and some [`Pointer`] types.
23        ///
24        /// [`Image`]: crate::TypeInner::Image
25        /// [`Sampler`]: crate::TypeInner::Sampler
26        /// [`Pointer`]: crate::TypeInner::Pointer
27        const DATA = 0x1;
28
29        /// The data type has a size known by pipeline creation time.
30        ///
31        /// Unsized types are quite restricted. The only unsized types permitted
32        /// by Naga, other than the non-[`DATA`] types like [`Image`] and
33        /// [`Sampler`], are dynamically-sized [`Array`]s, and [`Struct`]s whose
34        /// last members are such arrays. See the documentation for those types
35        /// for details.
36        ///
37        /// [`DATA`]: TypeFlags::DATA
38        /// [`Image`]: crate::TypeInner::Image
39        /// [`Sampler`]: crate::TypeInner::Sampler
40        /// [`Array`]: crate::TypeInner::Array
41        /// [`Struct`]: crate::TypeInner::Struct
42        const SIZED = 0x2;
43
44        /// The data can be copied around.
45        const COPY = 0x4;
46
47        /// Can be be used for user-defined IO between pipeline stages.
48        ///
49        /// This covers anything that can be in [`Location`] binding:
50        /// non-bool scalars and vectors, matrices, and structs and
51        /// arrays containing only interface types.
52        ///
53        /// [`Location`]: crate::Binding::Location
54        const IO_SHAREABLE = 0x8;
55
56        /// Can be used for host-shareable structures.
57        const HOST_SHAREABLE = 0x10;
58
59        /// The set of types with a fixed size at shader-creation time (ie. everything
60        /// except arrays sized by an override-expression)
61        const CREATION_RESOLVED = 0x20;
62
63        /// This type can be passed as a function argument.
64        const ARGUMENT = 0x40;
65
66        /// A WGSL [constructible] type.
67        ///
68        /// The constructible types are scalars, vectors, matrices, fixed-size
69        /// arrays of constructible types, and structs whose members are all
70        /// constructible.
71        ///
72        /// [constructible]: https://gpuweb.github.io/gpuweb/wgsl/#constructible
73        const CONSTRUCTIBLE = 0x80;
74    }
75}
76
77#[derive(Clone, Copy, Debug, thiserror::Error)]
78#[cfg_attr(test, derive(PartialEq))]
79pub enum Disalignment {
80    #[error("The array stride {stride} is not a multiple of the required alignment {alignment}")]
81    ArrayStride { stride: u32, alignment: Alignment },
82    #[error("The struct span {span}, is not a multiple of the required alignment {alignment}")]
83    StructSpan { span: u32, alignment: Alignment },
84    #[error("The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}")]
85    MemberOffset {
86        index: u32,
87        offset: u32,
88        alignment: Alignment,
89    },
90    #[error("The struct member[{index}] offset {offset} must be at least {expected}")]
91    MemberOffsetAfterStruct {
92        index: u32,
93        offset: u32,
94        expected: u32,
95    },
96    #[error("The struct member[{index}] is not statically sized")]
97    UnsizedMember { index: u32 },
98    #[error("The type is not host-shareable")]
99    NonHostShareable,
100}
101
102#[derive(Clone, Debug, thiserror::Error)]
103#[cfg_attr(test, derive(PartialEq))]
104pub enum TypeError {
105    #[error("Capability {0:?} is required")]
106    MissingCapability(Capabilities),
107    #[error("The {0:?} scalar width {1} is not supported for an atomic")]
108    InvalidAtomicWidth(crate::ScalarKind, crate::Bytes),
109    #[error("Invalid type for pointer target {0:?}")]
110    InvalidPointerBase(Handle<crate::Type>),
111    #[error("Unsized types like {base:?} must be in the `Storage` address space, not `{space:?}`")]
112    InvalidPointerToUnsized {
113        base: Handle<crate::Type>,
114        space: crate::AddressSpace,
115    },
116    #[error("Expected data type, found {0:?}")]
117    InvalidData(Handle<crate::Type>),
118    #[error("Base type {0:?} for the array is invalid")]
119    InvalidArrayBaseType(Handle<crate::Type>),
120    #[error("Matrix elements must always be floating-point types")]
121    MatrixElementNotFloat,
122    #[error("The constant {0:?} is specialized, and cannot be used as an array size")]
123    UnsupportedSpecializedArrayLength(Handle<crate::Constant>),
124    #[error("{} of dimensionality {dim:?} and class {class:?} are not supported", if *.arrayed {"Arrayed images"} else {"Images"})]
125    UnsupportedImageType {
126        dim: crate::ImageDimension,
127        arrayed: bool,
128        class: crate::ImageClass,
129    },
130    #[error("Array stride {stride} does not match the expected {expected}")]
131    InvalidArrayStride { stride: u32, expected: u32 },
132    #[error("Field '{0}' can't be dynamically-sized, has type {1:?}")]
133    InvalidDynamicArray(String, Handle<crate::Type>),
134    #[error("The base handle {0:?} has to be a struct")]
135    BindingArrayBaseTypeNotStruct(Handle<crate::Type>),
136    #[error("Binding arrays of external textures are not yet supported")]
137    BindingArrayBaseExternalTextures,
138    #[error("Structure member[{index}] at {offset} overlaps the previous member")]
139    MemberOverlap { index: u32, offset: u32 },
140    #[error(
141        "Structure member[{index}] at {offset} and size {size} crosses the structure boundary of size {span}"
142    )]
143    MemberOutOfBounds {
144        index: u32,
145        offset: u32,
146        size: u32,
147        span: u32,
148    },
149    #[error("Structure types must have at least one member")]
150    EmptyStruct,
151    #[error(transparent)]
152    WidthError(#[from] WidthError),
153    #[error(
154        "The base handle {0:?} has an override-expression that didn't get resolved to a constant"
155    )]
156    UnresolvedOverride(Handle<crate::Type>),
157    #[error("Override-sized array type {0:?} does not have a positive size")]
158    InvalidArraySize(Handle<crate::Type>),
159}
160
161#[derive(Clone, Debug, thiserror::Error)]
162#[cfg_attr(test, derive(PartialEq))]
163pub enum WidthError {
164    #[error("The {0:?} scalar width {1} is not supported")]
165    Invalid(crate::ScalarKind, crate::Bytes),
166    #[error("Using `{name}` values requires the `naga::valid::Capabilities::{flag}` flag")]
167    MissingCapability {
168        name: &'static str,
169        flag: &'static str,
170    },
171
172    #[error("Abstract types may only appear in constant expressions")]
173    Abstract,
174}
175
176#[derive(Clone, Debug, thiserror::Error)]
177#[cfg_attr(test, derive(PartialEq))]
178pub enum ImmediateError {
179    #[error("The scalar type {0:?} is not supported in immediates")]
180    InvalidScalar(crate::Scalar),
181}
182
183// Only makes sense if `flags.contains(HOST_SHAREABLE)`
184type LayoutCompatibility = Result<Alignment, (Handle<crate::Type>, Disalignment)>;
185type ImmediateCompatibility = Result<(), ImmediateError>;
186
187fn check_member_layout(
188    accum: &mut LayoutCompatibility,
189    member: &crate::StructMember,
190    member_index: u32,
191    member_layout: LayoutCompatibility,
192    parent_handle: Handle<crate::Type>,
193) {
194    *accum = match (*accum, member_layout) {
195        (Ok(cur_alignment), Ok(alignment)) => {
196            if alignment.is_aligned(member.offset) {
197                Ok(cur_alignment.max(alignment))
198            } else {
199                Err((
200                    parent_handle,
201                    Disalignment::MemberOffset {
202                        index: member_index,
203                        offset: member.offset,
204                        alignment,
205                    },
206                ))
207            }
208        }
209        (Err(e), _) | (_, Err(e)) => Err(e),
210    };
211}
212
213/// Determine whether a pointer in `space` can be passed as an argument.
214///
215/// If a pointer in `space` is permitted to be passed as an argument to a
216/// user-defined function, return `TypeFlags::ARGUMENT`. Otherwise, return
217/// `TypeFlags::empty()`.
218///
219/// Pointers passed as arguments to user-defined functions must be in the
220/// `Function` or `Private` address space.
221const fn ptr_space_argument_flag(space: crate::AddressSpace) -> TypeFlags {
222    use crate::AddressSpace as As;
223    match space {
224        As::Function | As::Private => TypeFlags::ARGUMENT,
225        As::Uniform
226        | As::Storage { .. }
227        | As::Handle
228        | As::Immediate
229        | As::WorkGroup
230        | As::TaskPayload => TypeFlags::empty(),
231    }
232}
233
234#[derive(Clone, Debug)]
235pub(super) struct TypeInfo {
236    pub flags: TypeFlags,
237    pub uniform_layout: LayoutCompatibility,
238    pub storage_layout: LayoutCompatibility,
239    pub immediates_compatibility: ImmediateCompatibility,
240}
241
242impl TypeInfo {
243    const fn dummy() -> Self {
244        TypeInfo {
245            flags: TypeFlags::empty(),
246            uniform_layout: Ok(Alignment::ONE),
247            storage_layout: Ok(Alignment::ONE),
248            immediates_compatibility: Ok(()),
249        }
250    }
251
252    const fn new(flags: TypeFlags, alignment: Alignment) -> Self {
253        TypeInfo {
254            flags,
255            uniform_layout: Ok(alignment),
256            storage_layout: Ok(alignment),
257            immediates_compatibility: Ok(()),
258        }
259    }
260}
261
262impl super::Validator {
263    const fn require_type_capability(&self, capability: Capabilities) -> Result<(), TypeError> {
264        if self.capabilities.contains(capability) {
265            Ok(())
266        } else {
267            Err(TypeError::MissingCapability(capability))
268        }
269    }
270
271    /// Check whether `scalar` is a permitted scalar width.
272    ///
273    /// If `scalar` is not a width allowed by the selected [`Capabilities`],
274    /// return an error explaining why.
275    ///
276    /// If `scalar` is allowed, return a [`ImmediateCompatibility`] result
277    /// that says whether `scalar` is allowed specifically in immediates.
278    ///
279    /// [`Capabilities`]: crate::valid::Capabilities
280    pub(super) const fn check_width(
281        &self,
282        scalar: crate::Scalar,
283    ) -> Result<ImmediateCompatibility, WidthError> {
284        let mut immediates_compatibility = Ok(());
285        let good = match scalar.kind {
286            crate::ScalarKind::Bool => scalar.width == crate::BOOL_WIDTH,
287            crate::ScalarKind::Float => match scalar.width {
288                8 => {
289                    if !self.capabilities.contains(Capabilities::FLOAT64) {
290                        return Err(WidthError::MissingCapability {
291                            name: "f64",
292                            flag: "FLOAT64",
293                        });
294                    }
295                    true
296                }
297                2 => {
298                    if !self.capabilities.contains(Capabilities::SHADER_FLOAT16) {
299                        return Err(WidthError::MissingCapability {
300                            name: "f16",
301                            flag: "FLOAT16",
302                        });
303                    }
304
305                    immediates_compatibility = Err(ImmediateError::InvalidScalar(scalar));
306
307                    true
308                }
309                _ => scalar.width == 4,
310            },
311            crate::ScalarKind::Sint => {
312                if scalar.width == 8 {
313                    if !self.capabilities.contains(Capabilities::SHADER_INT64) {
314                        return Err(WidthError::MissingCapability {
315                            name: "i64",
316                            flag: "SHADER_INT64",
317                        });
318                    }
319                    true
320                } else {
321                    scalar.width == 4
322                }
323            }
324            crate::ScalarKind::Uint => {
325                if scalar.width == 8 {
326                    if !self.capabilities.contains(Capabilities::SHADER_INT64) {
327                        return Err(WidthError::MissingCapability {
328                            name: "u64",
329                            flag: "SHADER_INT64",
330                        });
331                    }
332                    true
333                } else {
334                    scalar.width == 4
335                }
336            }
337            crate::ScalarKind::AbstractInt | crate::ScalarKind::AbstractFloat => {
338                return Err(WidthError::Abstract);
339            }
340        };
341        if good {
342            Ok(immediates_compatibility)
343        } else {
344            Err(WidthError::Invalid(scalar.kind, scalar.width))
345        }
346    }
347
348    pub(super) fn reset_types(&mut self, size: usize) {
349        self.types.clear();
350        self.types.resize(size, TypeInfo::dummy());
351        self.layouter.clear();
352    }
353
354    pub(super) fn validate_type(
355        &self,
356        handle: Handle<crate::Type>,
357        gctx: crate::proc::GlobalCtx,
358    ) -> Result<TypeInfo, TypeError> {
359        use crate::TypeInner as Ti;
360        Ok(match gctx.types[handle].inner {
361            Ti::Scalar(scalar) => {
362                let immediates_compatibility = self.check_width(scalar)?;
363                let shareable = if scalar.kind.is_numeric() {
364                    TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
365                } else {
366                    TypeFlags::empty()
367                };
368                let mut type_info = TypeInfo::new(
369                    TypeFlags::DATA
370                        | TypeFlags::SIZED
371                        | TypeFlags::COPY
372                        | TypeFlags::ARGUMENT
373                        | TypeFlags::CONSTRUCTIBLE
374                        | TypeFlags::CREATION_RESOLVED
375                        | shareable,
376                    Alignment::from_width(scalar.width),
377                );
378                type_info.immediates_compatibility = immediates_compatibility;
379                type_info
380            }
381            Ti::Vector { size, scalar } => {
382                let immediates_compatibility = self.check_width(scalar)?;
383                let shareable = if scalar.kind.is_numeric() {
384                    TypeFlags::IO_SHAREABLE | TypeFlags::HOST_SHAREABLE
385                } else {
386                    TypeFlags::empty()
387                };
388                let mut type_info = TypeInfo::new(
389                    TypeFlags::DATA
390                        | TypeFlags::SIZED
391                        | TypeFlags::COPY
392                        | TypeFlags::ARGUMENT
393                        | TypeFlags::CONSTRUCTIBLE
394                        | TypeFlags::CREATION_RESOLVED
395                        | shareable,
396                    Alignment::from(size) * Alignment::from_width(scalar.width),
397                );
398                type_info.immediates_compatibility = immediates_compatibility;
399                type_info
400            }
401            Ti::Matrix {
402                columns: _,
403                rows,
404                scalar,
405            } => {
406                if scalar.kind != crate::ScalarKind::Float {
407                    return Err(TypeError::MatrixElementNotFloat);
408                }
409                let immediates_compatibility = self.check_width(scalar)?;
410                let mut type_info = TypeInfo::new(
411                    TypeFlags::DATA
412                        | TypeFlags::SIZED
413                        | TypeFlags::COPY
414                        | TypeFlags::HOST_SHAREABLE
415                        | TypeFlags::ARGUMENT
416                        | TypeFlags::CONSTRUCTIBLE
417                        | TypeFlags::CREATION_RESOLVED,
418                    Alignment::from(rows) * Alignment::from_width(scalar.width),
419                );
420                type_info.immediates_compatibility = immediates_compatibility;
421                type_info
422            }
423            Ti::CooperativeMatrix {
424                columns: _,
425                rows: _,
426                scalar,
427                role: _,
428            } => {
429                self.require_type_capability(Capabilities::COOPERATIVE_MATRIX)?;
430                // Allow f16 (width 2) and f32 (width 4) for cooperative matrices
431                if scalar.kind != crate::ScalarKind::Float
432                    || (scalar.width != 2 && scalar.width != 4)
433                {
434                    return Err(TypeError::MatrixElementNotFloat);
435                }
436                TypeInfo::new(
437                    TypeFlags::DATA
438                        | TypeFlags::SIZED
439                        | TypeFlags::COPY
440                        | TypeFlags::HOST_SHAREABLE
441                        | TypeFlags::ARGUMENT
442                        | TypeFlags::CONSTRUCTIBLE
443                        | TypeFlags::CREATION_RESOLVED,
444                    Alignment::from_width(scalar.width),
445                )
446            }
447            Ti::Atomic(scalar) => {
448                match scalar {
449                    crate::Scalar {
450                        kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
451                        width: _,
452                    } => {
453                        if scalar.width == 8
454                            && !self.capabilities.intersects(
455                                Capabilities::SHADER_INT64_ATOMIC_ALL_OPS
456                                    | Capabilities::SHADER_INT64_ATOMIC_MIN_MAX,
457                            )
458                        {
459                            return Err(TypeError::MissingCapability(
460                                Capabilities::SHADER_INT64_ATOMIC_ALL_OPS,
461                            ));
462                        }
463                    }
464                    crate::Scalar::F32 => {
465                        if !self
466                            .capabilities
467                            .contains(Capabilities::SHADER_FLOAT32_ATOMIC)
468                        {
469                            return Err(TypeError::MissingCapability(
470                                Capabilities::SHADER_FLOAT32_ATOMIC,
471                            ));
472                        }
473                    }
474                    _ => return Err(TypeError::InvalidAtomicWidth(scalar.kind, scalar.width)),
475                };
476                TypeInfo::new(
477                    TypeFlags::DATA
478                        | TypeFlags::SIZED
479                        | TypeFlags::HOST_SHAREABLE
480                        | TypeFlags::CREATION_RESOLVED,
481                    Alignment::from_width(scalar.width),
482                )
483            }
484            Ti::Pointer { base, space } => {
485                use crate::AddressSpace as As;
486
487                let base_info = &self.types[base.index()];
488                if !base_info.flags.contains(TypeFlags::DATA) {
489                    return Err(TypeError::InvalidPointerBase(base));
490                }
491
492                // Runtime-sized values can only live in the `Storage` address
493                // space, so it's useless to have a pointer to such a type in
494                // any other space.
495                //
496                // Detecting this problem here prevents the definition of
497                // functions like:
498                //
499                //     fn f(p: ptr<workgroup, UnsizedType>) -> ... { ... }
500                //
501                // which would otherwise be permitted, but uncallable. (They
502                // may also present difficulties in code generation).
503                if !base_info.flags.contains(TypeFlags::SIZED) {
504                    match space {
505                        As::Storage { .. } => {}
506                        _ => {
507                            return Err(TypeError::InvalidPointerToUnsized { base, space });
508                        }
509                    }
510                }
511
512                // `Validator::validate_function` actually checks the address
513                // space of pointer arguments explicitly before checking the
514                // `ARGUMENT` flag, to give better error messages. But it seems
515                // best to set `ARGUMENT` accurately anyway.
516                let argument_flag = ptr_space_argument_flag(space);
517
518                // Pointers cannot be stored in variables, structure members, or
519                // array elements, so we do not mark them as `DATA`.
520                TypeInfo::new(
521                    argument_flag
522                        | TypeFlags::SIZED
523                        | TypeFlags::COPY
524                        | TypeFlags::CREATION_RESOLVED,
525                    Alignment::ONE,
526                )
527            }
528            Ti::ValuePointer {
529                size: _,
530                scalar,
531                space,
532            } => {
533                // ValuePointer should be treated the same way as the equivalent
534                // Pointer / Scalar / Vector combination, so each step in those
535                // variants' match arms should have a counterpart here.
536                //
537                // However, some cases are trivial: All our implicit base types
538                // are DATA and SIZED, so we can never return
539                // `InvalidPointerBase` or `InvalidPointerToUnsized`.
540                let _ = self.check_width(scalar)?;
541
542                // `Validator::validate_function` actually checks the address
543                // space of pointer arguments explicitly before checking the
544                // `ARGUMENT` flag, to give better error messages. But it seems
545                // best to set `ARGUMENT` accurately anyway.
546                let argument_flag = ptr_space_argument_flag(space);
547
548                // Pointers cannot be stored in variables, structure members, or
549                // array elements, so we do not mark them as `DATA`.
550                TypeInfo::new(
551                    argument_flag
552                        | TypeFlags::SIZED
553                        | TypeFlags::COPY
554                        | TypeFlags::CREATION_RESOLVED,
555                    Alignment::ONE,
556                )
557            }
558            Ti::Array { base, size, stride } => {
559                let base_info = &self.types[base.index()];
560                if !base_info
561                    .flags
562                    .contains(TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::CREATION_RESOLVED)
563                {
564                    return Err(TypeError::InvalidArrayBaseType(base));
565                }
566
567                if self.overrides_resolved {
568                    // This check only makes sense for override-sized arrays.
569                    // `ArraySize::Constant` holds a `NonZeroU32`.
570                    if let crate::ArraySize::Pending(_) = size {
571                        size.resolve(gctx)
572                            .map_err(|_| TypeError::InvalidArraySize(handle))?;
573                    }
574                }
575
576                let base_layout = self.layouter[base];
577                let general_alignment = base_layout.alignment;
578                let uniform_layout = match base_info.uniform_layout {
579                    Ok(base_alignment) => {
580                        let alignment = base_alignment
581                            .max(general_alignment)
582                            .max(Alignment::MIN_UNIFORM);
583                        if alignment.is_aligned(stride) {
584                            Ok(alignment)
585                        } else {
586                            Err((handle, Disalignment::ArrayStride { stride, alignment }))
587                        }
588                    }
589                    Err(e) => Err(e),
590                };
591                let storage_layout = match base_info.storage_layout {
592                    Ok(base_alignment) => {
593                        let alignment = base_alignment.max(general_alignment);
594                        if alignment.is_aligned(stride) {
595                            Ok(alignment)
596                        } else {
597                            Err((handle, Disalignment::ArrayStride { stride, alignment }))
598                        }
599                    }
600                    Err(e) => Err(e),
601                };
602
603                let type_info_mask = match size {
604                    crate::ArraySize::Constant(_) => {
605                        TypeFlags::DATA
606                            | TypeFlags::SIZED
607                            | TypeFlags::COPY
608                            | TypeFlags::HOST_SHAREABLE
609                            | TypeFlags::ARGUMENT
610                            | TypeFlags::CONSTRUCTIBLE
611                            | TypeFlags::CREATION_RESOLVED
612                    }
613                    crate::ArraySize::Pending(_) => {
614                        TypeFlags::DATA
615                            | TypeFlags::SIZED
616                            | TypeFlags::COPY
617                            | TypeFlags::HOST_SHAREABLE
618                            | TypeFlags::ARGUMENT
619                    }
620                    crate::ArraySize::Dynamic => {
621                        // Non-SIZED types may only appear as the last element of a structure.
622                        // This is enforced by checks for SIZED-ness for all compound types,
623                        // and a special case for structs.
624                        TypeFlags::DATA
625                            | TypeFlags::COPY
626                            | TypeFlags::HOST_SHAREABLE
627                            | TypeFlags::CREATION_RESOLVED
628                    }
629                };
630
631                TypeInfo {
632                    flags: base_info.flags & type_info_mask,
633                    uniform_layout,
634                    storage_layout,
635                    immediates_compatibility: base_info.immediates_compatibility.clone(),
636                }
637            }
638            Ti::Struct { ref members, span } => {
639                if members.is_empty() {
640                    return Err(TypeError::EmptyStruct);
641                }
642
643                let mut ti = TypeInfo::new(
644                    TypeFlags::DATA
645                        | TypeFlags::SIZED
646                        | TypeFlags::COPY
647                        | TypeFlags::HOST_SHAREABLE
648                        | TypeFlags::IO_SHAREABLE
649                        | TypeFlags::ARGUMENT
650                        | TypeFlags::CONSTRUCTIBLE
651                        | TypeFlags::CREATION_RESOLVED,
652                    Alignment::ONE,
653                );
654                ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);
655
656                let mut min_offset = 0;
657                let mut prev_struct_data: Option<(u32, u32)> = None;
658
659                for (i, member) in members.iter().enumerate() {
660                    let base_info = &self.types[member.ty.index()];
661                    if !base_info
662                        .flags
663                        .contains(TypeFlags::DATA | TypeFlags::CREATION_RESOLVED)
664                    {
665                        return Err(TypeError::InvalidData(member.ty));
666                    }
667                    if !base_info.flags.contains(TypeFlags::HOST_SHAREABLE) {
668                        if ti.uniform_layout.is_ok() {
669                            ti.uniform_layout = Err((member.ty, Disalignment::NonHostShareable));
670                        }
671                        if ti.storage_layout.is_ok() {
672                            ti.storage_layout = Err((member.ty, Disalignment::NonHostShareable));
673                        }
674                    }
675                    ti.flags &= base_info.flags;
676
677                    if member.offset < min_offset {
678                        // HACK: this could be nicer. We want to allow some structures
679                        // to not bother with offsets/alignments if they are never
680                        // used for host sharing.
681                        if member.offset == 0 {
682                            ti.flags.set(TypeFlags::HOST_SHAREABLE, false);
683                        } else {
684                            return Err(TypeError::MemberOverlap {
685                                index: i as u32,
686                                offset: member.offset,
687                            });
688                        }
689                    }
690
691                    let base_size = gctx.types[member.ty].inner.size(gctx);
692                    min_offset = member.offset + base_size;
693                    if min_offset > span {
694                        return Err(TypeError::MemberOutOfBounds {
695                            index: i as u32,
696                            offset: member.offset,
697                            size: base_size,
698                            span,
699                        });
700                    }
701
702                    check_member_layout(
703                        &mut ti.uniform_layout,
704                        member,
705                        i as u32,
706                        base_info.uniform_layout,
707                        handle,
708                    );
709                    check_member_layout(
710                        &mut ti.storage_layout,
711                        member,
712                        i as u32,
713                        base_info.storage_layout,
714                        handle,
715                    );
716                    if base_info.immediates_compatibility.is_err() {
717                        ti.immediates_compatibility = base_info.immediates_compatibility.clone();
718                    }
719
720                    // Validate rule: If a structure member itself has a structure type S,
721                    // then the number of bytes between the start of that member and
722                    // the start of any following member must be at least roundUp(16, SizeOf(S)).
723                    if let Some((span, offset)) = prev_struct_data {
724                        let diff = member.offset - offset;
725                        let min = Alignment::MIN_UNIFORM.round_up(span);
726                        if diff < min {
727                            ti.uniform_layout = Err((
728                                handle,
729                                Disalignment::MemberOffsetAfterStruct {
730                                    index: i as u32,
731                                    offset: member.offset,
732                                    expected: offset + min,
733                                },
734                            ));
735                        }
736                    };
737
738                    prev_struct_data = match gctx.types[member.ty].inner {
739                        crate::TypeInner::Struct { span, .. } => Some((span, member.offset)),
740                        _ => None,
741                    };
742
743                    // The last field may be an unsized array.
744                    if !base_info.flags.contains(TypeFlags::SIZED) {
745                        let is_array = match gctx.types[member.ty].inner {
746                            crate::TypeInner::Array { .. } => true,
747                            _ => false,
748                        };
749                        if !is_array || i + 1 != members.len() {
750                            let name = member.name.clone().unwrap_or_default();
751                            return Err(TypeError::InvalidDynamicArray(name, member.ty));
752                        }
753                        if ti.uniform_layout.is_ok() {
754                            ti.uniform_layout =
755                                Err((handle, Disalignment::UnsizedMember { index: i as u32 }));
756                        }
757                    }
758                }
759
760                let alignment = self.layouter[handle].alignment;
761                if !alignment.is_aligned(span) {
762                    ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
763                    ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
764                }
765
766                ti
767            }
768            Ti::Image {
769                dim,
770                arrayed,
771                class,
772            } => {
773                if arrayed && matches!(dim, crate::ImageDimension::D3) {
774                    return Err(TypeError::UnsupportedImageType {
775                        dim,
776                        arrayed,
777                        class,
778                    });
779                }
780                if arrayed && matches!(dim, crate::ImageDimension::Cube) {
781                    self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?;
782                }
783                if matches!(class, crate::ImageClass::External) {
784                    if dim != crate::ImageDimension::D2 || arrayed {
785                        return Err(TypeError::UnsupportedImageType {
786                            dim,
787                            arrayed,
788                            class,
789                        });
790                    }
791                    self.require_type_capability(Capabilities::TEXTURE_EXTERNAL)?;
792                }
793                TypeInfo::new(
794                    TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
795                    Alignment::ONE,
796                )
797            }
798            Ti::Sampler { .. } => TypeInfo::new(
799                TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
800                Alignment::ONE,
801            ),
802            Ti::AccelerationStructure { vertex_return } => {
803                self.require_type_capability(Capabilities::RAY_QUERY)?;
804                if vertex_return {
805                    self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;
806                }
807                TypeInfo::new(
808                    TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED,
809                    Alignment::ONE,
810                )
811            }
812            Ti::RayQuery { vertex_return } => {
813                self.require_type_capability(Capabilities::RAY_QUERY)?;
814                if vertex_return {
815                    self.require_type_capability(Capabilities::RAY_HIT_VERTEX_POSITION)?;
816                }
817                TypeInfo::new(
818                    TypeFlags::DATA
819                        | TypeFlags::CONSTRUCTIBLE
820                        | TypeFlags::SIZED
821                        | TypeFlags::CREATION_RESOLVED,
822                    Alignment::ONE,
823                )
824            }
825            Ti::BindingArray { base, size } => {
826                let type_info_mask = match size {
827                    crate::ArraySize::Constant(_) => {
828                        TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
829                    }
830                    crate::ArraySize::Pending(_) => TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE,
831                    crate::ArraySize::Dynamic => {
832                        // Final type is non-sized
833                        TypeFlags::HOST_SHAREABLE | TypeFlags::CREATION_RESOLVED
834                    }
835                };
836                let base_info = &self.types[base.index()];
837
838                if base_info.flags.contains(TypeFlags::DATA) {
839                    // Currently Naga only supports binding arrays of structs for non-handle types.
840                    // `validate_global_var` relies on ray queries (which are `DATA`) being rejected here
841                    match gctx.types[base].inner {
842                        crate::TypeInner::Struct { .. } => {}
843                        _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)),
844                    };
845                }
846                if matches!(
847                    gctx.types[base].inner,
848                    crate::TypeInner::Image {
849                        class: crate::ImageClass::External,
850                        ..
851                    }
852                ) {
853                    // Binding arrays of external textures are not yet supported.
854                    // See <https://github.com/gfx-rs/wgpu/issues/8027>. Note that
855                    // `validate_global_var` relies on this error being raised here.
856                    return Err(TypeError::BindingArrayBaseExternalTextures);
857                }
858
859                if !base_info.flags.contains(TypeFlags::CREATION_RESOLVED) {
860                    return Err(TypeError::InvalidData(base));
861                }
862
863                TypeInfo::new(base_info.flags & type_info_mask, Alignment::ONE)
864            }
865        })
866    }
867}