1mod analyzer;
6mod compose;
7mod expression;
8mod function;
9mod handles;
10pub(crate) mod immediates;
11mod interface;
12mod r#type;
13
14use alloc::{boxed::Box, string::String, vec, vec::Vec};
15use core::ops;
16
17use bit_set::BitSet;
18
19use crate::{
20 arena::{Handle, HandleSet},
21 proc::{ExpressionKindTracker, LayoutError, Layouter, TypeResolution},
22 FastHashSet,
23};
24
25use crate::span::{AddSpan as _, WithSpan};
29pub use analyzer::{ExpressionInfo, FunctionInfo, GlobalUse, Uniformity, UniformityRequirements};
30pub use compose::ComposeError;
31pub use expression::{check_literal_value, LiteralError};
32pub use expression::{ConstExpressionError, ExpressionError};
33pub use function::{CallError, FunctionError, LocalVariableError, SubgroupError};
34pub use immediates::{ImmediateSlots, ImmediateSlotsOverflowError, ImmediateUsage};
35pub use interface::{EntryPointError, GlobalVariableError, VaryingError};
36pub use r#type::{Disalignment, ImmediateError, TypeError, TypeFlags, WidthError};
37
38use self::handles::InvalidHandleError;
39
40pub const MAX_TYPE_SIZE: u32 = i32::MAX as u32;
42
43bitflags::bitflags! {
44 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
58 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
59 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
60 pub struct ValidationFlags: u8 {
61 const EXPRESSIONS = 0x1;
63 const BLOCKS = 0x2;
65 const CONTROL_FLOW_UNIFORMITY = 0x4;
67 const STRUCT_LAYOUTS = 0x8;
69 const CONSTANTS = 0x10;
71 const BINDINGS = 0x20;
73 }
74}
75
76impl Default for ValidationFlags {
77 fn default() -> Self {
78 Self::all()
79 }
80}
81
82bitflags::bitflags! {
83 #[must_use]
85 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
86 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
87 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
88 pub struct Capabilities: u64 {
89 const IMMEDIATES = 1 << 0;
93 const FLOAT64 = 1 << 1;
95 const PRIMITIVE_INDEX = 1 << 2;
99 const TEXTURE_AND_SAMPLER_BINDING_ARRAY = 1 << 3;
101 const BUFFER_BINDING_ARRAY = 1 << 4;
103 const STORAGE_TEXTURE_BINDING_ARRAY = 1 << 5;
105 const STORAGE_BUFFER_BINDING_ARRAY = 1 << 6;
107 const CLIP_DISTANCES = 1 << 7;
111 const CULL_DISTANCE = 1 << 8;
115 const STORAGE_TEXTURE_16BIT_NORM_FORMATS = 1 << 9;
117 const MULTIVIEW = 1 << 10;
121 const EARLY_DEPTH_TEST = 1 << 11;
123 const MULTISAMPLED_SHADING = 1 << 12;
128 const RAY_QUERY = 1 << 13;
130 const DUAL_SOURCE_BLENDING = 1 << 14;
132 const CUBE_ARRAY_TEXTURES = 1 << 15;
134 const SHADER_INT64 = 1 << 16;
136 const SUBGROUP = 1 << 17;
147 const SUBGROUP_BARRIER = 1 << 18;
151 const SUBGROUP_VERTEX_STAGE = 1 << 19;
157 const SHADER_INT64_ATOMIC_MIN_MAX = 1 << 20;
167 const SHADER_INT64_ATOMIC_ALL_OPS = 1 << 21;
169 const SHADER_FLOAT32_ATOMIC = 1 << 22;
178 const TEXTURE_ATOMIC = 1 << 23;
180 const TEXTURE_INT64_ATOMIC = 1 << 24;
182 const RAY_HIT_VERTEX_POSITION = 1 << 25;
184 const SHADER_FLOAT16 = 1 << 26;
186 const TEXTURE_EXTERNAL = 1 << 27;
188 const SHADER_FLOAT16_IN_FLOAT32 = 1 << 28;
191 const SHADER_BARYCENTRICS = 1 << 29;
193 const MESH_SHADER = 1 << 30;
195 const MESH_SHADER_POINT_TOPOLOGY = 1 << 31;
197 const TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 32;
199 const BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 33;
201 const STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 34;
203 const STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING = 1 << 35;
205 const COOPERATIVE_MATRIX = 1 << 36;
207 const PER_VERTEX = 1 << 37;
209 const RAY_TRACING_PIPELINE = 1 << 38;
211 const DRAW_INDEX = 1 << 39;
213 const ACCELERATION_STRUCTURE_BINDING_ARRAY = 1 << 40;
215 const MEMORY_DECORATION_COHERENT = 1 << 41;
217 const MEMORY_DECORATION_VOLATILE = 1 << 42;
219 const SHADER_INT16 = 1 << 43;
221 const LINEAR_INTERPOLATION = 1 << 44;
227 }
228}
229
230impl Capabilities {
231 #[cfg(feature = "wgsl-in")]
235 #[doc(hidden)]
236 pub const fn extension(&self) -> Option<crate::front::wgsl::ImplementedEnableExtension> {
237 use crate::front::wgsl::ImplementedEnableExtension as Ext;
238 match *self {
239 Self::DUAL_SOURCE_BLENDING => Some(Ext::DualSourceBlending),
240 Self::SHADER_FLOAT16 => Some(Ext::F16),
242 Self::SHADER_INT16 => Some(Ext::WgpuInt16),
243 Self::CLIP_DISTANCES => Some(Ext::ClipDistances),
244 Self::MESH_SHADER => Some(Ext::WgpuMeshShader),
245 Self::RAY_QUERY => Some(Ext::WgpuRayQuery),
246 Self::RAY_HIT_VERTEX_POSITION => Some(Ext::WgpuRayQueryVertexReturn),
247 Self::COOPERATIVE_MATRIX => Some(Ext::WgpuCooperativeMatrix),
248 Self::RAY_TRACING_PIPELINE => Some(Ext::WgpuRayTracingPipeline),
249 Self::PER_VERTEX => Some(Ext::WgpuPerVertex),
250 Self::BUFFER_BINDING_ARRAY
251 | Self::BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
252 | Self::STORAGE_BUFFER_BINDING_ARRAY
253 | Self::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
254 | Self::STORAGE_TEXTURE_BINDING_ARRAY
255 | Self::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
256 | Self::TEXTURE_AND_SAMPLER_BINDING_ARRAY
257 | Self::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING => {
258 Some(Ext::WgpuBindingArray)
259 }
260 _ => None,
261 }
262 }
263}
264
265impl Default for Capabilities {
266 fn default() -> Self {
267 Self::MULTISAMPLED_SHADING | Self::CUBE_ARRAY_TEXTURES
268 }
269}
270
271bitflags::bitflags! {
272 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
274 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
275 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
276 pub struct SubgroupOperationSet: u8 {
277 const BASIC = 1 << 0;
283 const VOTE = 1 << 1;
285 const ARITHMETIC = 1 << 2;
287 const BALLOT = 1 << 3;
289 const SHUFFLE = 1 << 4;
291 const SHUFFLE_RELATIVE = 1 << 5;
293 const QUAD_FRAGMENT_COMPUTE = 1 << 7;
298 }
301}
302
303impl super::SubgroupOperation {
304 const fn required_operations(&self) -> SubgroupOperationSet {
305 use SubgroupOperationSet as S;
306 match *self {
307 Self::All | Self::Any => S::VOTE,
308 Self::Add | Self::Mul | Self::Min | Self::Max | Self::And | Self::Or | Self::Xor => {
309 S::ARITHMETIC
310 }
311 }
312 }
313}
314
315impl super::GatherMode {
316 const fn required_operations(&self) -> SubgroupOperationSet {
317 use SubgroupOperationSet as S;
318 match *self {
319 Self::BroadcastFirst | Self::Broadcast(_) => S::BALLOT,
320 Self::Shuffle(_) | Self::ShuffleXor(_) => S::SHUFFLE,
321 Self::ShuffleUp(_) | Self::ShuffleDown(_) => S::SHUFFLE_RELATIVE,
322 Self::QuadBroadcast(_) | Self::QuadSwap(_) => S::QUAD_FRAGMENT_COMPUTE,
323 }
324 }
325}
326
327bitflags::bitflags! {
328 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
330 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
331 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
332 pub struct ShaderStages: u16 {
333 const VERTEX = 0x1;
334 const FRAGMENT = 0x2;
335 const COMPUTE = 0x4;
336 const MESH = 0x8;
337 const TASK = 0x10;
338 const RAY_GENERATION = 0x20;
339 const ANY_HIT = 0x40;
340 const CLOSEST_HIT = 0x80;
341 const MISS = 0x100;
342 const COMPUTE_LIKE = Self::COMPUTE.bits() | Self::TASK.bits() | Self::MESH.bits();
343 }
344}
345
346#[derive(Debug, Clone, Default)]
347#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
348#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
349pub struct ModuleInfo {
350 type_flags: Vec<TypeFlags>,
351 functions: Vec<FunctionInfo>,
352 entry_points: Vec<FunctionInfo>,
353 const_expression_types: Box<[TypeResolution]>,
354}
355
356impl ops::Index<Handle<crate::Type>> for ModuleInfo {
357 type Output = TypeFlags;
358 fn index(&self, handle: Handle<crate::Type>) -> &Self::Output {
359 &self.type_flags[handle.index()]
360 }
361}
362
363impl ops::Index<Handle<crate::Function>> for ModuleInfo {
364 type Output = FunctionInfo;
365 fn index(&self, handle: Handle<crate::Function>) -> &Self::Output {
366 &self.functions[handle.index()]
367 }
368}
369
370impl ops::Index<Handle<crate::Expression>> for ModuleInfo {
371 type Output = TypeResolution;
372 fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
373 &self.const_expression_types[handle.index()]
374 }
375}
376
377#[derive(Debug)]
378pub struct Validator {
379 flags: ValidationFlags,
380 capabilities: Capabilities,
381 subgroup_stages: ShaderStages,
382 subgroup_operations: SubgroupOperationSet,
383 types: Vec<r#type::TypeInfo>,
384 layouter: Layouter,
385 location_mask: BitSet,
386 ep_resource_bindings: FastHashSet<crate::ResourceBinding>,
387 switch_values: FastHashSet<crate::SwitchValue>,
388 valid_expression_list: Vec<Handle<crate::Expression>>,
389 valid_expression_set: HandleSet<crate::Expression>,
390 override_ids: FastHashSet<u16>,
391
392 overrides_resolved: bool,
395
396 needs_visit: HandleSet<crate::Expression>,
415
416 trace_rays_vertex_return: TraceRayVertexReturnState,
420
421 trace_rays_payload_type: Option<Handle<crate::Type>>,
424}
425
426#[derive(Debug)]
427enum TraceRayVertexReturnState {
428 NoTraceRays,
430 #[expect(
434 unused,
435 reason = "Don't yet have vertex return builtins to return this error for."
436 )]
437 NoVertexReturn(crate::Span),
438 VertexReturn,
442}
443
444#[derive(Clone, Debug, thiserror::Error)]
445#[cfg_attr(test, derive(PartialEq))]
446pub enum ConstantError {
447 #[error("Initializer must be a const-expression")]
448 InitializerExprType,
449 #[error("The type doesn't match the constant")]
450 InvalidType,
451 #[error("The type is not constructible")]
452 NonConstructibleType,
453}
454
455#[derive(Clone, Debug, thiserror::Error)]
456#[cfg_attr(test, derive(PartialEq))]
457pub enum OverrideError {
458 #[error("Override name and ID are missing")]
459 MissingNameAndID,
460 #[error("Override ID must be unique")]
461 DuplicateID,
462 #[error("Initializer must be a const-expression or override-expression")]
463 InitializerExprType,
464 #[error("The type doesn't match the override")]
465 InvalidType,
466 #[error("The type is not constructible")]
467 NonConstructibleType,
468 #[error("The type is not a scalar")]
469 TypeNotScalar,
470 #[error("Override declarations are not allowed")]
471 NotAllowed,
472 #[error("Override is uninitialized")]
473 UninitializedOverride,
474 #[error("Constant expression {handle:?} is invalid")]
475 ConstExpression {
476 handle: Handle<crate::Expression>,
477 source: ConstExpressionError,
478 },
479}
480
481#[derive(Clone, Debug, thiserror::Error)]
482#[cfg_attr(test, derive(PartialEq))]
483pub enum ValidationError {
484 #[error(transparent)]
485 InvalidHandle(#[from] InvalidHandleError),
486 #[error(transparent)]
487 Layouter(#[from] LayoutError),
488 #[error("Type {handle:?} '{name}' is invalid")]
489 Type {
490 handle: Handle<crate::Type>,
491 name: String,
492 source: TypeError,
493 },
494 #[error("Constant expression {handle:?} is invalid")]
495 ConstExpression {
496 handle: Handle<crate::Expression>,
497 source: ConstExpressionError,
498 },
499 #[error("Array size expression {handle:?} is not strictly positive")]
500 ArraySizeError { handle: Handle<crate::Expression> },
501 #[error("Constant {handle:?} '{name}' is invalid")]
502 Constant {
503 handle: Handle<crate::Constant>,
504 name: String,
505 source: ConstantError,
506 },
507 #[error("Override {handle:?} '{name}' is invalid")]
508 Override {
509 handle: Handle<crate::Override>,
510 name: String,
511 source: OverrideError,
512 },
513 #[error("Global variable {handle:?} '{name}' is invalid")]
514 GlobalVariable {
515 handle: Handle<crate::GlobalVariable>,
516 name: String,
517 source: GlobalVariableError,
518 },
519 #[error("Function {handle:?} '{name}' is invalid")]
520 Function {
521 handle: Handle<crate::Function>,
522 name: String,
523 source: FunctionError,
524 },
525 #[error("Entry point {name} at {stage:?} is invalid")]
526 EntryPoint {
527 stage: crate::ShaderStage,
528 name: String,
529 source: EntryPointError,
530 },
531 #[error("Module is corrupted")]
532 Corrupted,
533}
534
535impl crate::TypeInner {
536 const fn is_sized(&self) -> bool {
537 match *self {
538 Self::Scalar { .. }
539 | Self::Vector { .. }
540 | Self::Matrix { .. }
541 | Self::CooperativeMatrix { .. }
542 | Self::Array {
543 size: crate::ArraySize::Constant(_),
544 ..
545 }
546 | Self::Atomic { .. }
547 | Self::Pointer { .. }
548 | Self::ValuePointer { .. }
549 | Self::Struct { .. } => true,
550 Self::Array { .. }
551 | Self::Image { .. }
552 | Self::Sampler { .. }
553 | Self::AccelerationStructure { .. }
554 | Self::RayQuery { .. }
555 | Self::BindingArray { .. } => false,
556 }
557 }
558
559 const fn image_storage_coordinates(&self) -> Option<crate::ImageDimension> {
561 match *self {
562 Self::Scalar(crate::Scalar {
563 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
564 ..
565 }) => Some(crate::ImageDimension::D1),
566 Self::Vector {
567 size: crate::VectorSize::Bi,
568 scalar:
569 crate::Scalar {
570 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
571 ..
572 },
573 } => Some(crate::ImageDimension::D2),
574 Self::Vector {
575 size: crate::VectorSize::Tri,
576 scalar:
577 crate::Scalar {
578 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
579 ..
580 },
581 } => Some(crate::ImageDimension::D3),
582 _ => None,
583 }
584 }
585}
586
587impl Validator {
588 pub fn new(flags: ValidationFlags, capabilities: Capabilities) -> Self {
602 let subgroup_operations = if capabilities.contains(Capabilities::SUBGROUP) {
603 use SubgroupOperationSet as S;
604 S::BASIC
605 | S::VOTE
606 | S::ARITHMETIC
607 | S::BALLOT
608 | S::SHUFFLE
609 | S::SHUFFLE_RELATIVE
610 | S::QUAD_FRAGMENT_COMPUTE
611 } else {
612 SubgroupOperationSet::empty()
613 };
614 let subgroup_stages = {
615 let mut stages = ShaderStages::empty();
616 if capabilities.contains(Capabilities::SUBGROUP_VERTEX_STAGE) {
617 stages |= ShaderStages::VERTEX;
618 }
619 if capabilities.contains(Capabilities::SUBGROUP) {
620 stages |= ShaderStages::FRAGMENT | ShaderStages::COMPUTE_LIKE;
621 }
622 stages
623 };
624
625 Validator {
626 flags,
627 capabilities,
628 subgroup_stages,
629 subgroup_operations,
630 types: Vec::new(),
631 layouter: Layouter::default(),
632 location_mask: BitSet::new(),
633 ep_resource_bindings: FastHashSet::default(),
634 switch_values: FastHashSet::default(),
635 valid_expression_list: Vec::new(),
636 valid_expression_set: HandleSet::new(),
637 override_ids: FastHashSet::default(),
638 overrides_resolved: false,
639 needs_visit: HandleSet::new(),
640 trace_rays_vertex_return: TraceRayVertexReturnState::NoTraceRays,
641 trace_rays_payload_type: None,
642 }
643 }
644
645 pub const fn subgroup_stages(&mut self, stages: ShaderStages) -> &mut Self {
647 self.subgroup_stages = stages;
648 self
649 }
650
651 pub const fn subgroup_operations(&mut self, operations: SubgroupOperationSet) -> &mut Self {
653 self.subgroup_operations = operations;
654 self
655 }
656
657 pub fn reset(&mut self) {
659 self.types.clear();
660 self.layouter.clear();
661 self.location_mask.make_empty();
662 self.ep_resource_bindings.clear();
663 self.switch_values.clear();
664 self.valid_expression_list.clear();
665 self.valid_expression_set.clear();
666 self.override_ids.clear();
667 }
668
669 fn validate_constant(
670 &self,
671 handle: Handle<crate::Constant>,
672 gctx: crate::proc::GlobalCtx,
673 mod_info: &ModuleInfo,
674 global_expr_kind: &ExpressionKindTracker,
675 ) -> Result<(), ConstantError> {
676 let con = &gctx.constants[handle];
677
678 let type_info = &self.types[con.ty.index()];
679 if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
680 return Err(ConstantError::NonConstructibleType);
681 }
682
683 if !global_expr_kind.is_const(con.init) {
684 return Err(ConstantError::InitializerExprType);
685 }
686
687 if !gctx.compare_types(&TypeResolution::Handle(con.ty), &mod_info[con.init]) {
688 return Err(ConstantError::InvalidType);
689 }
690
691 Ok(())
692 }
693
694 fn validate_override(
695 &mut self,
696 handle: Handle<crate::Override>,
697 gctx: crate::proc::GlobalCtx,
698 mod_info: &ModuleInfo,
699 ) -> Result<(), OverrideError> {
700 let o = &gctx.overrides[handle];
701
702 if let Some(id) = o.id {
703 if !self.override_ids.insert(id) {
704 return Err(OverrideError::DuplicateID);
705 }
706 }
707
708 let type_info = &self.types[o.ty.index()];
709 if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
710 return Err(OverrideError::NonConstructibleType);
711 }
712
713 match gctx.types[o.ty].inner {
714 crate::TypeInner::Scalar(
715 crate::Scalar::BOOL
716 | crate::Scalar::I16
717 | crate::Scalar::U16
718 | crate::Scalar::I32
719 | crate::Scalar::U32
720 | crate::Scalar::F16
721 | crate::Scalar::F32
722 | crate::Scalar::F64,
723 ) => {}
724 _ => return Err(OverrideError::TypeNotScalar),
725 }
726
727 if let Some(init) = o.init {
728 if !gctx.compare_types(&TypeResolution::Handle(o.ty), &mod_info[init]) {
729 return Err(OverrideError::InvalidType);
730 }
731 } else if self.overrides_resolved {
732 return Err(OverrideError::UninitializedOverride);
733 }
734
735 Ok(())
736 }
737
738 pub fn validate(
740 &mut self,
741 module: &crate::Module,
742 ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
743 self.overrides_resolved = false;
744 self.validate_impl(module)
745 }
746
747 pub fn validate_resolved_overrides(
755 &mut self,
756 module: &crate::Module,
757 ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
758 self.overrides_resolved = true;
759 self.validate_impl(module)
760 }
761
762 fn validate_impl(
763 &mut self,
764 module: &crate::Module,
765 ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
766 self.reset();
767 self.reset_types(module.types.len());
768
769 Self::validate_module_handles(module).map_err(|e| Box::new((*e).with_span()))?;
770
771 self.layouter.update(module.to_ctx()).map_err(|e| {
772 let handle = e.ty;
773 ValidationError::from(e).with_span_handle(handle, &module.types)
774 })?;
775
776 let placeholder = TypeResolution::Value(crate::TypeInner::Scalar(crate::Scalar {
778 kind: crate::ScalarKind::Bool,
779 width: 0,
780 }));
781
782 let mut mod_info = ModuleInfo {
783 type_flags: Vec::with_capacity(module.types.len()),
784 functions: Vec::with_capacity(module.functions.len()),
785 entry_points: Vec::with_capacity(module.entry_points.len()),
786 const_expression_types: vec![placeholder; module.global_expressions.len()]
787 .into_boxed_slice(),
788 };
789
790 for (handle, ty) in module.types.iter() {
791 let ty_info = self
792 .validate_type(handle, module.to_ctx())
793 .map_err(|source| {
794 ValidationError::Type {
795 handle,
796 name: ty.name.clone().unwrap_or_default(),
797 source,
798 }
799 .with_span_handle(handle, &module.types)
800 })?;
801 debug_assert!(
802 ty_info.flags.contains(TypeFlags::CONSTRUCTIBLE)
803 == module.types[handle].inner.is_constructible(&module.types)
804 );
805 mod_info.type_flags.push(ty_info.flags);
806 self.types[handle.index()] = ty_info;
807 }
808
809 {
810 let t = crate::Arena::new();
811 let resolve_context = crate::proc::ResolveContext::with_locals(module, &t, &[]);
812 for (handle, _) in module.global_expressions.iter() {
813 mod_info
814 .process_const_expression(handle, &resolve_context, module.to_ctx())
815 .map_err(|source| {
816 ValidationError::ConstExpression { handle, source }
817 .with_span_handle(handle, &module.global_expressions)
818 })?
819 }
820 }
821
822 let global_expr_kind = ExpressionKindTracker::from_arena(&module.global_expressions);
823
824 if self.flags.contains(ValidationFlags::CONSTANTS) {
825 for (handle, _) in module.global_expressions.iter() {
826 self.validate_const_expression(
827 handle,
828 module.to_ctx(),
829 &mod_info,
830 &global_expr_kind,
831 )
832 .map_err(|source| {
833 ValidationError::ConstExpression { handle, source }
834 .with_span_handle(handle, &module.global_expressions)
835 })?
836 }
837
838 for (handle, constant) in module.constants.iter() {
839 self.validate_constant(handle, module.to_ctx(), &mod_info, &global_expr_kind)
840 .map_err(|source| {
841 ValidationError::Constant {
842 handle,
843 name: constant.name.clone().unwrap_or_default(),
844 source,
845 }
846 .with_span_handle(handle, &module.constants)
847 })?
848 }
849
850 for (handle, r#override) in module.overrides.iter() {
851 self.validate_override(handle, module.to_ctx(), &mod_info)
852 .map_err(|source| {
853 ValidationError::Override {
854 handle,
855 name: r#override.name.clone().unwrap_or_default(),
856 source,
857 }
858 .with_span_handle(handle, &module.overrides)
859 })?;
860 }
861 }
862
863 for (var_handle, var) in module.global_variables.iter() {
864 self.validate_global_var(var, module.to_ctx(), &mod_info, &global_expr_kind)
865 .map_err(|source| {
866 ValidationError::GlobalVariable {
867 handle: var_handle,
868 name: var.name.clone().unwrap_or_default(),
869 source,
870 }
871 .with_span_handle(var_handle, &module.global_variables)
872 })?;
873 }
874
875 for (handle, fun) in module.functions.iter() {
876 match self.validate_function(fun, module, &mod_info, false) {
877 Ok(info) => mod_info.functions.push(info),
878 Err(error) => {
879 return Err(Box::new(error.and_then(|source| {
880 ValidationError::Function {
881 handle,
882 name: fun.name.clone().unwrap_or_default(),
883 source,
884 }
885 .with_span_handle(handle, &module.functions)
886 })))
887 }
888 }
889 }
890
891 let mut ep_map = FastHashSet::default();
892 for ep in module.entry_points.iter() {
893 if !ep_map.insert((ep.stage, &ep.name)) {
894 return Err(Box::new(
895 ValidationError::EntryPoint {
896 stage: ep.stage,
897 name: ep.name.clone(),
898 source: EntryPointError::Conflict,
899 }
900 .with_span(),
901 )); }
903
904 match self.validate_entry_point(ep, module, &mod_info) {
905 Ok(info) => {
906 mod_info.entry_points.push(info);
907 }
908 Err(error) => {
909 return Err(Box::new(error.and_then(|source| {
910 ValidationError::EntryPoint {
911 stage: ep.stage,
912 name: ep.name.clone(),
913 source,
914 }
915 .with_span()
916 })));
917 }
918 }
919 }
920
921 Ok(mod_info)
922 }
923}
924
925fn validate_atomic_compare_exchange_struct(
926 types: &crate::UniqueArena<crate::Type>,
927 members: &[crate::StructMember],
928 scalar_predicate: impl FnOnce(&crate::TypeInner) -> bool,
929) -> bool {
930 members.len() == 2
931 && members[0].name.as_deref() == Some("old_value")
932 && scalar_predicate(&types[members[0].ty].inner)
933 && members[1].name.as_deref() == Some("exchanged")
934 && types[members[1].ty].inner == crate::TypeInner::Scalar(crate::Scalar::BOOL)
935}