@@ -156,6 +156,8 @@ pub enum ExpressionError {
156156 lhs_type : crate :: TypeInner ,
157157 rhs_expr : Handle < crate :: Expression > ,
158158 } ,
159+ #[ error( "Division by zero" ) ]
160+ DivideByZero ,
159161}
160162
161163#[ derive( Clone , Debug , thiserror:: Error ) ]
@@ -319,6 +321,60 @@ impl super::Validator {
319321 }
320322 }
321323
324+ /// Return an error if a constant divisor in `right` evaluates to zero for
325+ /// an integer division or remainder operation.
326+ ///
327+ /// This function promises to return an error in cases where (1) the
328+ /// expression is well-typed, (2) `left_ty` is a concrete integer or a
329+ /// vector, and (3) `right` is a const-expression that evaluates to zero.
330+ /// It does not return an error in cases where the expression is not
331+ /// well-typed (e.g. vector dimension mismatch), because those will be
332+ /// rejected elsewhere.
333+ fn validate_constant_divisor (
334+ left_ty : & crate :: TypeInner ,
335+ right : Handle < crate :: Expression > ,
336+ module : & crate :: Module ,
337+ function : & crate :: Function ,
338+ ) -> Result < ( ) , ExpressionError > {
339+ fn contains_zero (
340+ handle : Handle < crate :: Expression > ,
341+ expressions : & crate :: Arena < crate :: Expression > ,
342+ module : & crate :: Module ,
343+ ) -> bool {
344+ match expressions[ handle] {
345+ crate :: Expression :: Literal ( _) | crate :: Expression :: ZeroValue ( _) => module
346+ . to_ctx ( )
347+ . get_const_val_from :: < u32 , _ > ( handle, expressions)
348+ . ok ( )
349+ . is_some_and ( |v| v == 0 ) ,
350+ crate :: Expression :: Splat { value, .. } => contains_zero ( value, expressions, module) ,
351+ crate :: Expression :: Compose { ref components, .. } => components
352+ . iter ( )
353+ . any ( |& comp| contains_zero ( comp, expressions, module) ) ,
354+ crate :: Expression :: Constant ( c) => {
355+ contains_zero ( module. constants [ c] . init , & module. global_expressions , module)
356+ }
357+ _ => false ,
358+ }
359+ }
360+
361+ let Some ( ( _, scalar) ) = left_ty. vector_size_and_scalar ( ) else {
362+ return Ok ( ( ) ) ;
363+ } ;
364+ if !matches ! (
365+ scalar. kind,
366+ crate :: ScalarKind :: Sint | crate :: ScalarKind :: Uint
367+ ) {
368+ return Ok ( ( ) ) ;
369+ }
370+
371+ if contains_zero ( right, & function. expressions , module) {
372+ Err ( ExpressionError :: DivideByZero )
373+ } else {
374+ Ok ( ( ) )
375+ }
376+ }
377+
322378 #[ allow( clippy:: too_many_arguments) ]
323379 pub ( super ) fn validate_expression (
324380 & self ,
@@ -1071,6 +1127,10 @@ impl super::Validator {
10711127 if matches ! ( op, Bo :: ShiftLeft | Bo :: ShiftRight ) {
10721128 Self :: validate_constant_shift_amounts ( left_inner, right, module, function) ?;
10731129 }
1130+ // For integer division or remainder, check if the constant divisor is zero
1131+ if matches ! ( op, Bo :: Divide | Bo :: Modulo ) {
1132+ Self :: validate_constant_divisor ( left_inner, right, module, function) ?;
1133+ }
10741134 ShaderStages :: all ( )
10751135 }
10761136 E :: Select {
0 commit comments