naga/valid/
type.rs

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