@@ -560,6 +560,28 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
560560 }
561561 }
562562
563+ fn const_eval_expr_to_bool ( & self , handle : Handle < ir:: Expression > ) -> Option < bool > {
564+ match self . expr_type {
565+ ExpressionContextType :: Runtime ( ref ctx) => {
566+ if !ctx. local_expression_kind_tracker . is_const ( handle) {
567+ return None ;
568+ }
569+
570+ self . module
571+ . to_ctx ( )
572+ . eval_expr_to_bool_from ( handle, & ctx. function . expressions )
573+ }
574+ ExpressionContextType :: Constant ( Some ( ref ctx) ) => {
575+ assert ! ( ctx. local_expression_kind_tracker. is_const( handle) ) ;
576+ self . module
577+ . to_ctx ( )
578+ . eval_expr_to_bool_from ( handle, & ctx. function . expressions )
579+ }
580+ ExpressionContextType :: Constant ( None ) => self . module . to_ctx ( ) . eval_expr_to_bool ( handle) ,
581+ ExpressionContextType :: Override => None ,
582+ }
583+ }
584+
563585 /// Return `true` if `handle` is a constant expression.
564586 fn is_const ( & self , handle : Handle < ir:: Expression > ) -> bool {
565587 use ExpressionContextType as Ect ;
@@ -2538,22 +2560,27 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
25382560 result_var,
25392561 ) ) )
25402562 } else {
2541- let left_expr = ctx. get ( left) ;
2542- // Constant or override context in either function or module scope
2543- let & crate :: Expression :: Literal ( crate :: Literal :: Bool ( left_val) ) = left_expr else {
2544- return Err ( Box :: new ( Error :: NotBool ( span) ) ) ;
2545- } ;
2563+ let left_val = ctx. const_eval_expr_to_bool ( left) ;
2564+
2565+ if left_val. is_some_and ( |left_val| {
2566+ op == crate :: BinaryOperator :: LogicalAnd && !left_val
2567+ || op == crate :: BinaryOperator :: LogicalOr && left_val
2568+ } ) {
2569+ // Short-circuit behavior: don't evaluate the RHS.
25462570
2547- if op == crate :: BinaryOperator :: LogicalAnd && !left_val
2548- || op == crate :: BinaryOperator :: LogicalOr && left_val
2549- {
2550- // Short-circuit behavior: don't evaluate the RHS. Ideally we
2551- // would do _some_ validity checks of the RHS here, but that's
2552- // tricky, because the RHS is allowed to have things that aren't
2553- // legal in const contexts.
2571+ // TODO(https://github.com/gfx-rs/wgpu/issues/8440): We shouldn't ignore the
2572+ // RHS completely, it should still be type-checked. Preserving it for type
2573+ // checking is a bit tricky, because we're trying to produce an expression
2574+ // for a const context, but the RHS is allowed to have things that aren't
2575+ // const.
25542576
2555- Ok ( Typed :: Plain ( left_expr . clone ( ) ) )
2577+ Ok ( Typed :: Plain ( ctx . get ( left ) . clone ( ) ) )
25562578 } else {
2579+ // Evaluate the RHS and construct the entire binary expression as we
2580+ // normally would. This case applies to well-formed constant logical
2581+ // expressions that don't short-circuit (handled by the constant evaluator
2582+ // shortly), to override expressions (handled when overrides are processed)
2583+ // and to non-well-formed expressions (rejected by type checking).
25572584 let right = self . expression_for_abstract ( right, ctx) ?;
25582585 ctx. grow_types ( right) ?;
25592586
0 commit comments