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