1mod analyzer;
6mod compose;
7mod expression;
8mod function;
9mod handles;
10mod interface;
11mod r#type;
12
13use alloc::{boxed::Box, string::String, vec, vec::Vec};
14use core::ops;
15
16use bit_set::BitSet;
17
18use crate::{
19 arena::{Handle, HandleSet},
20 proc::{ExpressionKindTracker, LayoutError, Layouter, TypeResolution},
21 FastHashSet,
22};
23
24use crate::span::{AddSpan as _, WithSpan};
28pub use analyzer::{ExpressionInfo, FunctionInfo, GlobalUse, Uniformity, UniformityRequirements};
29pub use compose::ComposeError;
30pub use expression::{check_literal_value, LiteralError};
31pub use expression::{ConstExpressionError, ExpressionError};
32pub use function::{CallError, FunctionError, LocalVariableError, SubgroupError};
33pub use interface::{EntryPointError, GlobalVariableError, VaryingError};
34pub use r#type::{Disalignment, ImmediateError, TypeError, TypeFlags, WidthError};
35
36use self::handles::InvalidHandleError;
37
38pub const MAX_TYPE_SIZE: u32 = 0x4000_0000; bitflags::bitflags! {
42 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
56 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
57 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
58 pub struct ValidationFlags: u8 {
59 const EXPRESSIONS = 0x1;
61 const BLOCKS = 0x2;
63 const CONTROL_FLOW_UNIFORMITY = 0x4;
65 const STRUCT_LAYOUTS = 0x8;
67 const CONSTANTS = 0x10;
69 const BINDINGS = 0x20;
71 }
72}
73
74impl Default for ValidationFlags {
75 fn default() -> Self {
76 Self::all()
77 }
78}
79
80bitflags::bitflags! {
81 #[must_use]
83 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
84 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
85 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
86 pub struct Capabilities: u64 {
87 const IMMEDIATES = 1 << 0;
91 const FLOAT64 = 1 << 1;
93 const PRIMITIVE_INDEX = 1 << 2;
97 const TEXTURE_AND_SAMPLER_BINDING_ARRAY = 1 << 3;
99 const BUFFER_BINDING_ARRAY = 1 << 4;
101 const STORAGE_TEXTURE_BINDING_ARRAY = 1 << 5;
103 const STORAGE_BUFFER_BINDING_ARRAY = 1 << 6;
105 const CLIP_DISTANCE = 1 << 7;
109 const CULL_DISTANCE = 1 << 8;
113 const STORAGE_TEXTURE_16BIT_NORM_FORMATS = 1 << 9;
115 const MULTIVIEW = 1 << 10;
119 const EARLY_DEPTH_TEST = 1 << 11;
121 const MULTISAMPLED_SHADING = 1 << 12;
126 const RAY_QUERY = 1 << 13;
128 const DUAL_SOURCE_BLENDING = 1 << 14;
130 const CUBE_ARRAY_TEXTURES = 1 << 15;
132 const SHADER_INT64 = 1 << 16;
134 const SUBGROUP = 1 << 17;
145 const SUBGROUP_BARRIER = 1 << 18;
149 const SUBGROUP_VERTEX_STAGE = 1 << 19;
155 const SHADER_INT64_ATOMIC_MIN_MAX = 1 << 20;
165 const SHADER_INT64_ATOMIC_ALL_OPS = 1 << 21;
167 const SHADER_FLOAT32_ATOMIC = 1 << 22;
176 const TEXTURE_ATOMIC = 1 << 23;
178 const TEXTURE_INT64_ATOMIC = 1 << 24;
180 const RAY_HIT_VERTEX_POSITION = 1 << 25;
182 const SHADER_FLOAT16 = 1 << 26;
184 const TEXTURE_EXTERNAL = 1 << 27;
186 const SHADER_FLOAT16_IN_FLOAT32 = 1 << 28;
189 const SHADER_BARYCENTRICS = 1 << 29;
191 const MESH_SHADER = 1 << 30;
193 const MESH_SHADER_POINT_TOPOLOGY = 1 << 31;
195 const TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 32;
197 const BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 33;
199 const STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 34;
201 const STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 35;
203 }
204}
205
206impl Capabilities {
207 #[cfg(feature = "wgsl-in")]
211 #[doc(hidden)]
212 pub const fn extension(&self) -> Option<crate::front::wgsl::ImplementedEnableExtension> {
213 use crate::front::wgsl::ImplementedEnableExtension as Ext;
214 match *self {
215 Self::DUAL_SOURCE_BLENDING => Some(Ext::DualSourceBlending),
216 Self::SHADER_FLOAT16 => Some(Ext::F16),
218 Self::CLIP_DISTANCE => Some(Ext::ClipDistances),
219 Self::RAY_QUERY => Some(Ext::WgpuRayQuery),
220 Self::RAY_HIT_VERTEX_POSITION => Some(Ext::WgpuRayQueryVertexReturn),
221 _ => None,
222 }
223 }
224}
225
226impl Default for Capabilities {
227 fn default() -> Self {
228 Self::MULTISAMPLED_SHADING | Self::CUBE_ARRAY_TEXTURES
229 }
230}
231
232bitflags::bitflags! {
233 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
235 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
236 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
237 pub struct SubgroupOperationSet: u8 {
238 const BASIC = 1 << 0;
244 const VOTE = 1 << 1;
246 const ARITHMETIC = 1 << 2;
248 const BALLOT = 1 << 3;
250 const SHUFFLE = 1 << 4;
252 const SHUFFLE_RELATIVE = 1 << 5;
254 const QUAD_FRAGMENT_COMPUTE = 1 << 7;
259 }
262}
263
264impl super::SubgroupOperation {
265 const fn required_operations(&self) -> SubgroupOperationSet {
266 use SubgroupOperationSet as S;
267 match *self {
268 Self::All | Self::Any => S::VOTE,
269 Self::Add | Self::Mul | Self::Min | Self::Max | Self::And | Self::Or | Self::Xor => {
270 S::ARITHMETIC
271 }
272 }
273 }
274}
275
276impl super::GatherMode {
277 const fn required_operations(&self) -> SubgroupOperationSet {
278 use SubgroupOperationSet as S;
279 match *self {
280 Self::BroadcastFirst | Self::Broadcast(_) => S::BALLOT,
281 Self::Shuffle(_) | Self::ShuffleXor(_) => S::SHUFFLE,
282 Self::ShuffleUp(_) | Self::ShuffleDown(_) => S::SHUFFLE_RELATIVE,
283 Self::QuadBroadcast(_) | Self::QuadSwap(_) => S::QUAD_FRAGMENT_COMPUTE,
284 }
285 }
286}
287
288bitflags::bitflags! {
289 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
291 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
292 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
293 pub struct ShaderStages: u8 {
294 const VERTEX = 0x1;
295 const FRAGMENT = 0x2;
296 const COMPUTE = 0x4;
297 const MESH = 0x8;
298 const TASK = 0x10;
299 }
300}
301
302#[derive(Debug, Clone, Default)]
303#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
304#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
305pub struct ModuleInfo {
306 type_flags: Vec<TypeFlags>,
307 functions: Vec<FunctionInfo>,
308 entry_points: Vec<FunctionInfo>,
309 const_expression_types: Box<[TypeResolution]>,
310}
311
312impl ops::Index<Handle<crate::Type>> for ModuleInfo {
313 type Output = TypeFlags;
314 fn index(&self, handle: Handle<crate::Type>) -> &Self::Output {
315 &self.type_flags[handle.index()]
316 }
317}
318
319impl ops::Index<Handle<crate::Function>> for ModuleInfo {
320 type Output = FunctionInfo;
321 fn index(&self, handle: Handle<crate::Function>) -> &Self::Output {
322 &self.functions[handle.index()]
323 }
324}
325
326impl ops::Index<Handle<crate::Expression>> for ModuleInfo {
327 type Output = TypeResolution;
328 fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
329 &self.const_expression_types[handle.index()]
330 }
331}
332
333#[derive(Debug)]
334pub struct Validator {
335 flags: ValidationFlags,
336 capabilities: Capabilities,
337 subgroup_stages: ShaderStages,
338 subgroup_operations: SubgroupOperationSet,
339 types: Vec<r#type::TypeInfo>,
340 layouter: Layouter,
341 location_mask: BitSet,
342 blend_src_mask: BitSet,
343 ep_resource_bindings: FastHashSet<crate::ResourceBinding>,
344 #[allow(dead_code)]
345 switch_values: FastHashSet<crate::SwitchValue>,
346 valid_expression_list: Vec<Handle<crate::Expression>>,
347 valid_expression_set: HandleSet<crate::Expression>,
348 override_ids: FastHashSet<u16>,
349
350 overrides_resolved: bool,
353
354 needs_visit: HandleSet<crate::Expression>,
373}
374
375#[derive(Clone, Debug, thiserror::Error)]
376#[cfg_attr(test, derive(PartialEq))]
377pub enum ConstantError {
378 #[error("Initializer must be a const-expression")]
379 InitializerExprType,
380 #[error("The type doesn't match the constant")]
381 InvalidType,
382 #[error("The type is not constructible")]
383 NonConstructibleType,
384}
385
386#[derive(Clone, Debug, thiserror::Error)]
387#[cfg_attr(test, derive(PartialEq))]
388pub enum OverrideError {
389 #[error("Override name and ID are missing")]
390 MissingNameAndID,
391 #[error("Override ID must be unique")]
392 DuplicateID,
393 #[error("Initializer must be a const-expression or override-expression")]
394 InitializerExprType,
395 #[error("The type doesn't match the override")]
396 InvalidType,
397 #[error("The type is not constructible")]
398 NonConstructibleType,
399 #[error("The type is not a scalar")]
400 TypeNotScalar,
401 #[error("Override declarations are not allowed")]
402 NotAllowed,
403 #[error("Override is uninitialized")]
404 UninitializedOverride,
405 #[error("Constant expression {handle:?} is invalid")]
406 ConstExpression {
407 handle: Handle<crate::Expression>,
408 source: ConstExpressionError,
409 },
410}
411
412#[derive(Clone, Debug, thiserror::Error)]
413#[cfg_attr(test, derive(PartialEq))]
414pub enum ValidationError {
415 #[error(transparent)]
416 InvalidHandle(#[from] InvalidHandleError),
417 #[error(transparent)]
418 Layouter(#[from] LayoutError),
419 #[error("Type {handle:?} '{name}' is invalid")]
420 Type {
421 handle: Handle<crate::Type>,
422 name: String,
423 source: TypeError,
424 },
425 #[error("Constant expression {handle:?} is invalid")]
426 ConstExpression {
427 handle: Handle<crate::Expression>,
428 source: ConstExpressionError,
429 },
430 #[error("Array size expression {handle:?} is not strictly positive")]
431 ArraySizeError { handle: Handle<crate::Expression> },
432 #[error("Constant {handle:?} '{name}' is invalid")]
433 Constant {
434 handle: Handle<crate::Constant>,
435 name: String,
436 source: ConstantError,
437 },
438 #[error("Override {handle:?} '{name}' is invalid")]
439 Override {
440 handle: Handle<crate::Override>,
441 name: String,
442 source: OverrideError,
443 },
444 #[error("Global variable {handle:?} '{name}' is invalid")]
445 GlobalVariable {
446 handle: Handle<crate::GlobalVariable>,
447 name: String,
448 source: GlobalVariableError,
449 },
450 #[error("Function {handle:?} '{name}' is invalid")]
451 Function {
452 handle: Handle<crate::Function>,
453 name: String,
454 source: FunctionError,
455 },
456 #[error("Entry point {name} at {stage:?} is invalid")]
457 EntryPoint {
458 stage: crate::ShaderStage,
459 name: String,
460 source: EntryPointError,
461 },
462 #[error("Module is corrupted")]
463 Corrupted,
464}
465
466impl crate::TypeInner {
467 const fn is_sized(&self) -> bool {
468 match *self {
469 Self::Scalar { .. }
470 | Self::Vector { .. }
471 | Self::Matrix { .. }
472 | Self::Array {
473 size: crate::ArraySize::Constant(_),
474 ..
475 }
476 | Self::Atomic { .. }
477 | Self::Pointer { .. }
478 | Self::ValuePointer { .. }
479 | Self::Struct { .. } => true,
480 Self::Array { .. }
481 | Self::Image { .. }
482 | Self::Sampler { .. }
483 | Self::AccelerationStructure { .. }
484 | Self::RayQuery { .. }
485 | Self::BindingArray { .. } => false,
486 }
487 }
488
489 const fn image_storage_coordinates(&self) -> Option<crate::ImageDimension> {
491 match *self {
492 Self::Scalar(crate::Scalar {
493 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
494 ..
495 }) => Some(crate::ImageDimension::D1),
496 Self::Vector {
497 size: crate::VectorSize::Bi,
498 scalar:
499 crate::Scalar {
500 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
501 ..
502 },
503 } => Some(crate::ImageDimension::D2),
504 Self::Vector {
505 size: crate::VectorSize::Tri,
506 scalar:
507 crate::Scalar {
508 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
509 ..
510 },
511 } => Some(crate::ImageDimension::D3),
512 _ => None,
513 }
514 }
515}
516
517impl Validator {
518 pub fn new(flags: ValidationFlags, capabilities: Capabilities) -> Self {
532 let subgroup_operations = if capabilities.contains(Capabilities::SUBGROUP) {
533 use SubgroupOperationSet as S;
534 S::BASIC
535 | S::VOTE
536 | S::ARITHMETIC
537 | S::BALLOT
538 | S::SHUFFLE
539 | S::SHUFFLE_RELATIVE
540 | S::QUAD_FRAGMENT_COMPUTE
541 } else {
542 SubgroupOperationSet::empty()
543 };
544 let subgroup_stages = {
545 let mut stages = ShaderStages::empty();
546 if capabilities.contains(Capabilities::SUBGROUP_VERTEX_STAGE) {
547 stages |= ShaderStages::VERTEX;
548 }
549 if capabilities.contains(Capabilities::SUBGROUP) {
550 stages |= ShaderStages::FRAGMENT | ShaderStages::COMPUTE;
551 }
552 stages
553 };
554
555 Validator {
556 flags,
557 capabilities,
558 subgroup_stages,
559 subgroup_operations,
560 types: Vec::new(),
561 layouter: Layouter::default(),
562 location_mask: BitSet::new(),
563 blend_src_mask: BitSet::new(),
564 ep_resource_bindings: FastHashSet::default(),
565 switch_values: FastHashSet::default(),
566 valid_expression_list: Vec::new(),
567 valid_expression_set: HandleSet::new(),
568 override_ids: FastHashSet::default(),
569 overrides_resolved: false,
570 needs_visit: HandleSet::new(),
571 }
572 }
573
574 pub fn subgroup_stages(&mut self, stages: ShaderStages) -> &mut Self {
576 self.subgroup_stages = stages;
577 self
578 }
579
580 pub fn subgroup_operations(&mut self, operations: SubgroupOperationSet) -> &mut Self {
582 self.subgroup_operations = operations;
583 self
584 }
585
586 pub fn reset(&mut self) {
588 self.types.clear();
589 self.layouter.clear();
590 self.location_mask.clear();
591 self.blend_src_mask.clear();
592 self.ep_resource_bindings.clear();
593 self.switch_values.clear();
594 self.valid_expression_list.clear();
595 self.valid_expression_set.clear();
596 self.override_ids.clear();
597 }
598
599 fn validate_constant(
600 &self,
601 handle: Handle<crate::Constant>,
602 gctx: crate::proc::GlobalCtx,
603 mod_info: &ModuleInfo,
604 global_expr_kind: &ExpressionKindTracker,
605 ) -> Result<(), ConstantError> {
606 let con = &gctx.constants[handle];
607
608 let type_info = &self.types[con.ty.index()];
609 if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
610 return Err(ConstantError::NonConstructibleType);
611 }
612
613 if !global_expr_kind.is_const(con.init) {
614 return Err(ConstantError::InitializerExprType);
615 }
616
617 if !gctx.compare_types(&TypeResolution::Handle(con.ty), &mod_info[con.init]) {
618 return Err(ConstantError::InvalidType);
619 }
620
621 Ok(())
622 }
623
624 fn validate_override(
625 &mut self,
626 handle: Handle<crate::Override>,
627 gctx: crate::proc::GlobalCtx,
628 mod_info: &ModuleInfo,
629 ) -> Result<(), OverrideError> {
630 let o = &gctx.overrides[handle];
631
632 if let Some(id) = o.id {
633 if !self.override_ids.insert(id) {
634 return Err(OverrideError::DuplicateID);
635 }
636 }
637
638 let type_info = &self.types[o.ty.index()];
639 if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
640 return Err(OverrideError::NonConstructibleType);
641 }
642
643 match gctx.types[o.ty].inner {
644 crate::TypeInner::Scalar(
645 crate::Scalar::BOOL
646 | crate::Scalar::I32
647 | crate::Scalar::U32
648 | crate::Scalar::F16
649 | crate::Scalar::F32
650 | crate::Scalar::F64,
651 ) => {}
652 _ => return Err(OverrideError::TypeNotScalar),
653 }
654
655 if let Some(init) = o.init {
656 if !gctx.compare_types(&TypeResolution::Handle(o.ty), &mod_info[init]) {
657 return Err(OverrideError::InvalidType);
658 }
659 } else if self.overrides_resolved {
660 return Err(OverrideError::UninitializedOverride);
661 }
662
663 Ok(())
664 }
665
666 pub fn validate(
668 &mut self,
669 module: &crate::Module,
670 ) -> Result<ModuleInfo, WithSpan<ValidationError>> {
671 self.overrides_resolved = false;
672 self.validate_impl(module)
673 }
674
675 pub fn validate_resolved_overrides(
683 &mut self,
684 module: &crate::Module,
685 ) -> Result<ModuleInfo, WithSpan<ValidationError>> {
686 self.overrides_resolved = true;
687 self.validate_impl(module)
688 }
689
690 fn validate_impl(
691 &mut self,
692 module: &crate::Module,
693 ) -> Result<ModuleInfo, WithSpan<ValidationError>> {
694 self.reset();
695 self.reset_types(module.types.len());
696
697 Self::validate_module_handles(module).map_err(|e| e.with_span())?;
698
699 self.layouter.update(module.to_ctx()).map_err(|e| {
700 let handle = e.ty;
701 ValidationError::from(e).with_span_handle(handle, &module.types)
702 })?;
703
704 let placeholder = TypeResolution::Value(crate::TypeInner::Scalar(crate::Scalar {
706 kind: crate::ScalarKind::Bool,
707 width: 0,
708 }));
709
710 let mut mod_info = ModuleInfo {
711 type_flags: Vec::with_capacity(module.types.len()),
712 functions: Vec::with_capacity(module.functions.len()),
713 entry_points: Vec::with_capacity(module.entry_points.len()),
714 const_expression_types: vec![placeholder; module.global_expressions.len()]
715 .into_boxed_slice(),
716 };
717
718 for (handle, ty) in module.types.iter() {
719 let ty_info = self
720 .validate_type(handle, module.to_ctx())
721 .map_err(|source| {
722 ValidationError::Type {
723 handle,
724 name: ty.name.clone().unwrap_or_default(),
725 source,
726 }
727 .with_span_handle(handle, &module.types)
728 })?;
729 mod_info.type_flags.push(ty_info.flags);
730 self.types[handle.index()] = ty_info;
731 }
732
733 {
734 let t = crate::Arena::new();
735 let resolve_context = crate::proc::ResolveContext::with_locals(module, &t, &[]);
736 for (handle, _) in module.global_expressions.iter() {
737 mod_info
738 .process_const_expression(handle, &resolve_context, module.to_ctx())
739 .map_err(|source| {
740 ValidationError::ConstExpression { handle, source }
741 .with_span_handle(handle, &module.global_expressions)
742 })?
743 }
744 }
745
746 let global_expr_kind = ExpressionKindTracker::from_arena(&module.global_expressions);
747
748 if self.flags.contains(ValidationFlags::CONSTANTS) {
749 for (handle, _) in module.global_expressions.iter() {
750 self.validate_const_expression(
751 handle,
752 module.to_ctx(),
753 &mod_info,
754 &global_expr_kind,
755 )
756 .map_err(|source| {
757 ValidationError::ConstExpression { handle, source }
758 .with_span_handle(handle, &module.global_expressions)
759 })?
760 }
761
762 for (handle, constant) in module.constants.iter() {
763 self.validate_constant(handle, module.to_ctx(), &mod_info, &global_expr_kind)
764 .map_err(|source| {
765 ValidationError::Constant {
766 handle,
767 name: constant.name.clone().unwrap_or_default(),
768 source,
769 }
770 .with_span_handle(handle, &module.constants)
771 })?
772 }
773
774 for (handle, r#override) in module.overrides.iter() {
775 self.validate_override(handle, module.to_ctx(), &mod_info)
776 .map_err(|source| {
777 ValidationError::Override {
778 handle,
779 name: r#override.name.clone().unwrap_or_default(),
780 source,
781 }
782 .with_span_handle(handle, &module.overrides)
783 })?;
784 }
785 }
786
787 for (var_handle, var) in module.global_variables.iter() {
788 self.validate_global_var(var, module.to_ctx(), &mod_info, &global_expr_kind)
789 .map_err(|source| {
790 ValidationError::GlobalVariable {
791 handle: var_handle,
792 name: var.name.clone().unwrap_or_default(),
793 source,
794 }
795 .with_span_handle(var_handle, &module.global_variables)
796 })?;
797 }
798
799 for (handle, fun) in module.functions.iter() {
800 match self.validate_function(fun, module, &mod_info, false) {
801 Ok(info) => mod_info.functions.push(info),
802 Err(error) => {
803 return Err(error.and_then(|source| {
804 ValidationError::Function {
805 handle,
806 name: fun.name.clone().unwrap_or_default(),
807 source,
808 }
809 .with_span_handle(handle, &module.functions)
810 }))
811 }
812 }
813 }
814
815 let mut ep_map = FastHashSet::default();
816 for ep in module.entry_points.iter() {
817 if !ep_map.insert((ep.stage, &ep.name)) {
818 return Err(ValidationError::EntryPoint {
819 stage: ep.stage,
820 name: ep.name.clone(),
821 source: EntryPointError::Conflict,
822 }
823 .with_span()); }
825
826 match self.validate_entry_point(ep, module, &mod_info) {
827 Ok(info) => mod_info.entry_points.push(info),
828 Err(error) => {
829 return Err(error.and_then(|source| {
830 ValidationError::EntryPoint {
831 stage: ep.stage,
832 name: ep.name.clone(),
833 source,
834 }
835 .with_span()
836 }));
837 }
838 }
839 }
840
841 Ok(mod_info)
842 }
843}
844
845fn validate_atomic_compare_exchange_struct(
846 types: &crate::UniqueArena<crate::Type>,
847 members: &[crate::StructMember],
848 scalar_predicate: impl FnOnce(&crate::TypeInner) -> bool,
849) -> bool {
850 members.len() == 2
851 && members[0].name.as_deref() == Some("old_value")
852 && scalar_predicate(&types[members[0].ty].inner)
853 && members[1].name.as_deref() == Some("exchanged")
854 && types[members[1].ty].inner == crate::TypeInner::Scalar(crate::Scalar::BOOL)
855}