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 const DEBUG_PRINTF = 1 << 45;
229 }
230}
231
232impl Capabilities {
233 #[cfg(feature = "wgsl-in")]
237 #[doc(hidden)]
238 pub const fn extension(&self) -> Option<crate::front::wgsl::ImplementedEnableExtension> {
239 use crate::front::wgsl::ImplementedEnableExtension as Ext;
240 match *self {
241 Self::DUAL_SOURCE_BLENDING => Some(Ext::DualSourceBlending),
242 Self::SHADER_FLOAT16 => Some(Ext::F16),
244 Self::SHADER_INT16 => Some(Ext::WgpuInt16),
245 Self::CLIP_DISTANCES => Some(Ext::ClipDistances),
246 Self::MESH_SHADER => Some(Ext::WgpuMeshShader),
247 Self::RAY_QUERY => Some(Ext::WgpuRayQuery),
248 Self::RAY_HIT_VERTEX_POSITION => Some(Ext::WgpuRayQueryVertexReturn),
249 Self::COOPERATIVE_MATRIX => Some(Ext::WgpuCooperativeMatrix),
250 Self::RAY_TRACING_PIPELINE => Some(Ext::WgpuRayTracingPipeline),
251 Self::PER_VERTEX => Some(Ext::WgpuPerVertex),
252 Self::BUFFER_BINDING_ARRAY
253 | Self::BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
254 | Self::STORAGE_BUFFER_BINDING_ARRAY
255 | Self::STORAGE_BUFFER_BINDING_ARRAY_NON_UNIFORM_INDEXING
256 | Self::STORAGE_TEXTURE_BINDING_ARRAY
257 | Self::STORAGE_TEXTURE_BINDING_ARRAY_NON_UNIFORM_INDEXING
258 | Self::TEXTURE_AND_SAMPLER_BINDING_ARRAY
259 | Self::TEXTURE_AND_SAMPLER_BINDING_ARRAY_NON_UNIFORM_INDEXING => {
260 Some(Ext::WgpuBindingArray)
261 }
262 Self::DEBUG_PRINTF => Some(Ext::WgpuDebugPrintf),
263 _ => None,
264 }
265 }
266}
267
268impl Default for Capabilities {
269 fn default() -> Self {
270 Self::MULTISAMPLED_SHADING | Self::CUBE_ARRAY_TEXTURES
271 }
272}
273
274bitflags::bitflags! {
275 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
277 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
278 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
279 pub struct SubgroupOperationSet: u8 {
280 const BASIC = 1 << 0;
286 const VOTE = 1 << 1;
288 const ARITHMETIC = 1 << 2;
290 const BALLOT = 1 << 3;
292 const SHUFFLE = 1 << 4;
294 const SHUFFLE_RELATIVE = 1 << 5;
296 const QUAD_FRAGMENT_COMPUTE = 1 << 7;
301 }
304}
305
306impl super::SubgroupOperation {
307 const fn required_operations(&self) -> SubgroupOperationSet {
308 use SubgroupOperationSet as S;
309 match *self {
310 Self::All | Self::Any => S::VOTE,
311 Self::Add | Self::Mul | Self::Min | Self::Max | Self::And | Self::Or | Self::Xor => {
312 S::ARITHMETIC
313 }
314 }
315 }
316}
317
318impl super::GatherMode {
319 const fn required_operations(&self) -> SubgroupOperationSet {
320 use SubgroupOperationSet as S;
321 match *self {
322 Self::BroadcastFirst | Self::Broadcast(_) => S::BALLOT,
323 Self::Shuffle(_) | Self::ShuffleXor(_) => S::SHUFFLE,
324 Self::ShuffleUp(_) | Self::ShuffleDown(_) => S::SHUFFLE_RELATIVE,
325 Self::QuadBroadcast(_) | Self::QuadSwap(_) => S::QUAD_FRAGMENT_COMPUTE,
326 }
327 }
328}
329
330bitflags::bitflags! {
331 #[cfg_attr(feature = "serialize", derive(serde::Serialize))]
333 #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
334 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
335 pub struct ShaderStages: u16 {
336 const VERTEX = 0x1;
337 const FRAGMENT = 0x2;
338 const COMPUTE = 0x4;
339 const MESH = 0x8;
340 const TASK = 0x10;
341 const RAY_GENERATION = 0x20;
342 const ANY_HIT = 0x40;
343 const CLOSEST_HIT = 0x80;
344 const MISS = 0x100;
345 const COMPUTE_LIKE = Self::COMPUTE.bits() | Self::TASK.bits() | Self::MESH.bits();
346 }
347}
348
349#[derive(Debug, Clone, Default)]
350#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
351#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
352pub struct ModuleInfo {
353 type_flags: Vec<TypeFlags>,
354 functions: Vec<FunctionInfo>,
355 entry_points: Vec<FunctionInfo>,
356 const_expression_types: Box<[TypeResolution]>,
357}
358
359impl ops::Index<Handle<crate::Type>> for ModuleInfo {
360 type Output = TypeFlags;
361 fn index(&self, handle: Handle<crate::Type>) -> &Self::Output {
362 &self.type_flags[handle.index()]
363 }
364}
365
366impl ops::Index<Handle<crate::Function>> for ModuleInfo {
367 type Output = FunctionInfo;
368 fn index(&self, handle: Handle<crate::Function>) -> &Self::Output {
369 &self.functions[handle.index()]
370 }
371}
372
373impl ops::Index<Handle<crate::Expression>> for ModuleInfo {
374 type Output = TypeResolution;
375 fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
376 &self.const_expression_types[handle.index()]
377 }
378}
379
380#[derive(Debug)]
381pub struct Validator {
382 flags: ValidationFlags,
383 capabilities: Capabilities,
384 subgroup_stages: ShaderStages,
385 subgroup_operations: SubgroupOperationSet,
386 types: Vec<r#type::TypeInfo>,
387 layouter: Layouter,
388 location_mask: BitSet,
389 ep_resource_bindings: FastHashSet<crate::ResourceBinding>,
390 switch_values: FastHashSet<crate::SwitchValue>,
391 valid_expression_list: Vec<Handle<crate::Expression>>,
392 valid_expression_set: HandleSet<crate::Expression>,
393 override_ids: FastHashSet<u16>,
394
395 overrides_resolved: bool,
398
399 needs_visit: HandleSet<crate::Expression>,
418
419 trace_rays_vertex_return: TraceRayVertexReturnState,
423
424 trace_rays_payload_type: Option<Handle<crate::Type>>,
427}
428
429#[derive(Debug)]
430enum TraceRayVertexReturnState {
431 NoTraceRays,
433 #[expect(
437 unused,
438 reason = "Don't yet have vertex return builtins to return this error for."
439 )]
440 NoVertexReturn(crate::Span),
441 VertexReturn,
445}
446
447#[derive(Clone, Debug, thiserror::Error)]
448#[cfg_attr(test, derive(PartialEq))]
449pub enum ConstantError {
450 #[error("Initializer must be a const-expression")]
451 InitializerExprType,
452 #[error("The type doesn't match the constant")]
453 InvalidType,
454 #[error("The type is not constructible")]
455 NonConstructibleType,
456}
457
458#[derive(Clone, Debug, thiserror::Error)]
459#[cfg_attr(test, derive(PartialEq))]
460pub enum OverrideError {
461 #[error("Override name and ID are missing")]
462 MissingNameAndID,
463 #[error("Override ID must be unique")]
464 DuplicateID,
465 #[error("Initializer must be a const-expression or override-expression")]
466 InitializerExprType,
467 #[error("The type doesn't match the override")]
468 InvalidType,
469 #[error("The type is not constructible")]
470 NonConstructibleType,
471 #[error("The type is not a scalar")]
472 TypeNotScalar,
473 #[error("Override declarations are not allowed")]
474 NotAllowed,
475 #[error("Override is uninitialized")]
476 UninitializedOverride,
477 #[error("Constant expression {handle:?} is invalid")]
478 ConstExpression {
479 handle: Handle<crate::Expression>,
480 source: ConstExpressionError,
481 },
482}
483
484#[derive(Clone, Debug, thiserror::Error)]
485#[cfg_attr(test, derive(PartialEq))]
486pub enum ValidationError {
487 #[error(transparent)]
488 InvalidHandle(#[from] InvalidHandleError),
489 #[error(transparent)]
490 Layouter(#[from] LayoutError),
491 #[error("Type {handle:?} '{name}' is invalid")]
492 Type {
493 handle: Handle<crate::Type>,
494 name: String,
495 source: TypeError,
496 },
497 #[error("Constant expression {handle:?} is invalid")]
498 ConstExpression {
499 handle: Handle<crate::Expression>,
500 source: ConstExpressionError,
501 },
502 #[error("Array size expression {handle:?} is not strictly positive")]
503 ArraySizeError { handle: Handle<crate::Expression> },
504 #[error("Constant {handle:?} '{name}' is invalid")]
505 Constant {
506 handle: Handle<crate::Constant>,
507 name: String,
508 source: ConstantError,
509 },
510 #[error("Override {handle:?} '{name}' is invalid")]
511 Override {
512 handle: Handle<crate::Override>,
513 name: String,
514 source: OverrideError,
515 },
516 #[error("Global variable {handle:?} '{name}' is invalid")]
517 GlobalVariable {
518 handle: Handle<crate::GlobalVariable>,
519 name: String,
520 source: GlobalVariableError,
521 },
522 #[error("Function {handle:?} '{name}' is invalid")]
523 Function {
524 handle: Handle<crate::Function>,
525 name: String,
526 source: FunctionError,
527 },
528 #[error("Entry point {name} at {stage:?} is invalid")]
529 EntryPoint {
530 stage: crate::ShaderStage,
531 name: String,
532 source: EntryPointError,
533 },
534 #[error("Module is corrupted")]
535 Corrupted,
536}
537
538impl crate::TypeInner {
539 const fn is_sized(&self) -> bool {
540 match *self {
541 Self::Scalar { .. }
542 | Self::Vector { .. }
543 | Self::Matrix { .. }
544 | Self::CooperativeMatrix { .. }
545 | Self::Array {
546 size: crate::ArraySize::Constant(_),
547 ..
548 }
549 | Self::Atomic { .. }
550 | Self::Pointer { .. }
551 | Self::ValuePointer { .. }
552 | Self::Struct { .. } => true,
553 Self::Array { .. }
554 | Self::Image { .. }
555 | Self::Sampler { .. }
556 | Self::AccelerationStructure { .. }
557 | Self::RayQuery { .. }
558 | Self::BindingArray { .. } => false,
559 }
560 }
561
562 const fn image_storage_coordinates(&self) -> Option<crate::ImageDimension> {
564 match *self {
565 Self::Scalar(crate::Scalar {
566 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
567 ..
568 }) => Some(crate::ImageDimension::D1),
569 Self::Vector {
570 size: crate::VectorSize::Bi,
571 scalar:
572 crate::Scalar {
573 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
574 ..
575 },
576 } => Some(crate::ImageDimension::D2),
577 Self::Vector {
578 size: crate::VectorSize::Tri,
579 scalar:
580 crate::Scalar {
581 kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
582 ..
583 },
584 } => Some(crate::ImageDimension::D3),
585 _ => None,
586 }
587 }
588}
589
590impl Validator {
591 pub fn new(flags: ValidationFlags, capabilities: Capabilities) -> Self {
605 let subgroup_operations = if capabilities.contains(Capabilities::SUBGROUP) {
606 use SubgroupOperationSet as S;
607 S::BASIC
608 | S::VOTE
609 | S::ARITHMETIC
610 | S::BALLOT
611 | S::SHUFFLE
612 | S::SHUFFLE_RELATIVE
613 | S::QUAD_FRAGMENT_COMPUTE
614 } else {
615 SubgroupOperationSet::empty()
616 };
617 let subgroup_stages = {
618 let mut stages = ShaderStages::empty();
619 if capabilities.contains(Capabilities::SUBGROUP_VERTEX_STAGE) {
620 stages |= ShaderStages::VERTEX;
621 }
622 if capabilities.contains(Capabilities::SUBGROUP) {
623 stages |= ShaderStages::FRAGMENT | ShaderStages::COMPUTE_LIKE;
624 }
625 stages
626 };
627
628 Validator {
629 flags,
630 capabilities,
631 subgroup_stages,
632 subgroup_operations,
633 types: Vec::new(),
634 layouter: Layouter::default(),
635 location_mask: BitSet::new(),
636 ep_resource_bindings: FastHashSet::default(),
637 switch_values: FastHashSet::default(),
638 valid_expression_list: Vec::new(),
639 valid_expression_set: HandleSet::new(),
640 override_ids: FastHashSet::default(),
641 overrides_resolved: false,
642 needs_visit: HandleSet::new(),
643 trace_rays_vertex_return: TraceRayVertexReturnState::NoTraceRays,
644 trace_rays_payload_type: None,
645 }
646 }
647
648 pub const fn subgroup_stages(&mut self, stages: ShaderStages) -> &mut Self {
650 self.subgroup_stages = stages;
651 self
652 }
653
654 pub const fn subgroup_operations(&mut self, operations: SubgroupOperationSet) -> &mut Self {
656 self.subgroup_operations = operations;
657 self
658 }
659
660 pub fn reset(&mut self) {
662 self.types.clear();
663 self.layouter.clear();
664 self.location_mask.make_empty();
665 self.ep_resource_bindings.clear();
666 self.switch_values.clear();
667 self.valid_expression_list.clear();
668 self.valid_expression_set.clear();
669 self.override_ids.clear();
670 }
671
672 fn validate_constant(
673 &self,
674 handle: Handle<crate::Constant>,
675 gctx: crate::proc::GlobalCtx,
676 mod_info: &ModuleInfo,
677 global_expr_kind: &ExpressionKindTracker,
678 ) -> Result<(), ConstantError> {
679 let con = &gctx.constants[handle];
680
681 let type_info = &self.types[con.ty.index()];
682 if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
683 return Err(ConstantError::NonConstructibleType);
684 }
685
686 if !global_expr_kind.is_const(con.init) {
687 return Err(ConstantError::InitializerExprType);
688 }
689
690 if !gctx.compare_types(&TypeResolution::Handle(con.ty), &mod_info[con.init]) {
691 return Err(ConstantError::InvalidType);
692 }
693
694 Ok(())
695 }
696
697 fn validate_override(
698 &mut self,
699 handle: Handle<crate::Override>,
700 gctx: crate::proc::GlobalCtx,
701 mod_info: &ModuleInfo,
702 ) -> Result<(), OverrideError> {
703 let o = &gctx.overrides[handle];
704
705 if let Some(id) = o.id {
706 if !self.override_ids.insert(id) {
707 return Err(OverrideError::DuplicateID);
708 }
709 }
710
711 let type_info = &self.types[o.ty.index()];
712 if !type_info.flags.contains(TypeFlags::CONSTRUCTIBLE) {
713 return Err(OverrideError::NonConstructibleType);
714 }
715
716 match gctx.types[o.ty].inner {
717 crate::TypeInner::Scalar(
718 crate::Scalar::BOOL
719 | crate::Scalar::I16
720 | crate::Scalar::U16
721 | crate::Scalar::I32
722 | crate::Scalar::U32
723 | crate::Scalar::F16
724 | crate::Scalar::F32
725 | crate::Scalar::F64,
726 ) => {}
727 _ => return Err(OverrideError::TypeNotScalar),
728 }
729
730 if let Some(init) = o.init {
731 if !gctx.compare_types(&TypeResolution::Handle(o.ty), &mod_info[init]) {
732 return Err(OverrideError::InvalidType);
733 }
734 } else if self.overrides_resolved {
735 return Err(OverrideError::UninitializedOverride);
736 }
737
738 Ok(())
739 }
740
741 pub fn validate(
743 &mut self,
744 module: &crate::Module,
745 ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
746 self.overrides_resolved = false;
747 self.validate_impl(module)
748 }
749
750 pub fn validate_resolved_overrides(
758 &mut self,
759 module: &crate::Module,
760 ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
761 self.overrides_resolved = true;
762 self.validate_impl(module)
763 }
764
765 fn validate_impl(
766 &mut self,
767 module: &crate::Module,
768 ) -> Result<ModuleInfo, Box<WithSpan<ValidationError>>> {
769 self.reset();
770 self.reset_types(module.types.len());
771
772 Self::validate_module_handles(module).map_err(|e| Box::new((*e).with_span()))?;
773
774 self.layouter.update(module.to_ctx()).map_err(|e| {
775 let handle = e.ty;
776 ValidationError::from(e).with_span_handle(handle, &module.types)
777 })?;
778
779 let placeholder = TypeResolution::Value(crate::TypeInner::Scalar(crate::Scalar {
781 kind: crate::ScalarKind::Bool,
782 width: 0,
783 }));
784
785 let mut mod_info = ModuleInfo {
786 type_flags: Vec::with_capacity(module.types.len()),
787 functions: Vec::with_capacity(module.functions.len()),
788 entry_points: Vec::with_capacity(module.entry_points.len()),
789 const_expression_types: vec![placeholder; module.global_expressions.len()]
790 .into_boxed_slice(),
791 };
792
793 for (handle, ty) in module.types.iter() {
794 let ty_info = self
795 .validate_type(handle, module.to_ctx())
796 .map_err(|source| {
797 ValidationError::Type {
798 handle,
799 name: ty.name.clone().unwrap_or_default(),
800 source,
801 }
802 .with_span_handle(handle, &module.types)
803 })?;
804 debug_assert!(
805 ty_info.flags.contains(TypeFlags::CONSTRUCTIBLE)
806 == module.types[handle].inner.is_constructible(&module.types)
807 );
808 mod_info.type_flags.push(ty_info.flags);
809 self.types[handle.index()] = ty_info;
810 }
811
812 {
813 let t = crate::Arena::new();
814 let resolve_context = crate::proc::ResolveContext::with_locals(module, &t, &[]);
815 for (handle, _) in module.global_expressions.iter() {
816 mod_info
817 .process_const_expression(handle, &resolve_context, module.to_ctx())
818 .map_err(|source| {
819 ValidationError::ConstExpression { handle, source }
820 .with_span_handle(handle, &module.global_expressions)
821 })?
822 }
823 }
824
825 let global_expr_kind = ExpressionKindTracker::from_arena(&module.global_expressions);
826
827 if self.flags.contains(ValidationFlags::CONSTANTS) {
828 for (handle, _) in module.global_expressions.iter() {
829 self.validate_const_expression(
830 handle,
831 module.to_ctx(),
832 &mod_info,
833 &global_expr_kind,
834 )
835 .map_err(|source| {
836 ValidationError::ConstExpression { handle, source }
837 .with_span_handle(handle, &module.global_expressions)
838 })?
839 }
840
841 for (handle, constant) in module.constants.iter() {
842 self.validate_constant(handle, module.to_ctx(), &mod_info, &global_expr_kind)
843 .map_err(|source| {
844 ValidationError::Constant {
845 handle,
846 name: constant.name.clone().unwrap_or_default(),
847 source,
848 }
849 .with_span_handle(handle, &module.constants)
850 })?
851 }
852
853 for (handle, r#override) in module.overrides.iter() {
854 self.validate_override(handle, module.to_ctx(), &mod_info)
855 .map_err(|source| {
856 ValidationError::Override {
857 handle,
858 name: r#override.name.clone().unwrap_or_default(),
859 source,
860 }
861 .with_span_handle(handle, &module.overrides)
862 })?;
863 }
864 }
865
866 for (var_handle, var) in module.global_variables.iter() {
867 self.validate_global_var(var, module.to_ctx(), &mod_info, &global_expr_kind)
868 .map_err(|source| {
869 ValidationError::GlobalVariable {
870 handle: var_handle,
871 name: var.name.clone().unwrap_or_default(),
872 source,
873 }
874 .with_span_handle(var_handle, &module.global_variables)
875 })?;
876 }
877
878 for (handle, fun) in module.functions.iter() {
879 match self.validate_function(fun, module, &mod_info, false) {
880 Ok(info) => mod_info.functions.push(info),
881 Err(error) => {
882 return Err(Box::new(error.and_then(|source| {
883 ValidationError::Function {
884 handle,
885 name: fun.name.clone().unwrap_or_default(),
886 source,
887 }
888 .with_span_handle(handle, &module.functions)
889 })))
890 }
891 }
892 }
893
894 let mut ep_map = FastHashSet::default();
895 for ep in module.entry_points.iter() {
896 if !ep_map.insert((ep.stage, &ep.name)) {
897 return Err(Box::new(
898 ValidationError::EntryPoint {
899 stage: ep.stage,
900 name: ep.name.clone(),
901 source: EntryPointError::Conflict,
902 }
903 .with_span(),
904 )); }
906
907 match self.validate_entry_point(ep, module, &mod_info) {
908 Ok(info) => {
909 mod_info.entry_points.push(info);
910 }
911 Err(error) => {
912 return Err(Box::new(error.and_then(|source| {
913 ValidationError::EntryPoint {
914 stage: ep.stage,
915 name: ep.name.clone(),
916 source,
917 }
918 .with_span()
919 })));
920 }
921 }
922 }
923
924 Ok(mod_info)
925 }
926}
927
928fn validate_atomic_compare_exchange_struct(
929 types: &crate::UniqueArena<crate::Type>,
930 members: &[crate::StructMember],
931 scalar_predicate: impl FnOnce(&crate::TypeInner) -> bool,
932) -> bool {
933 members.len() == 2
934 && members[0].name.as_deref() == Some("old_value")
935 && scalar_predicate(&types[members[0].ty].inner)
936 && members[1].name.as_deref() == Some("exchanged")
937 && types[members[1].ty].inner == crate::TypeInner::Scalar(crate::Scalar::BOOL)
938}