diff --git a/naga/src/front/glsl/ast.rs b/naga/src/front/glsl/ast.rs index 2c314aa602d..420c66218d9 100644 --- a/naga/src/front/glsl/ast.rs +++ b/naga/src/front/glsl/ast.rs @@ -116,6 +116,10 @@ pub struct HirExpr { #[derive(Debug, Clone)] pub enum HirExprKind { + /// helper to represent a sequence of expressions where side effects matter, the last one is returned to the caller + Sequence { + exprs: Vec>, + }, Access { base: Handle, index: Handle, diff --git a/naga/src/front/glsl/context.rs b/naga/src/front/glsl/context.rs index 94f09ba022d..689f25ce60a 100644 --- a/naga/src/front/glsl/context.rs +++ b/naga/src/front/glsl/context.rs @@ -1333,6 +1333,7 @@ impl<'a> Context<'a> { } } } + _ => { return Err(Error { kind: ErrorKind::SemanticError( @@ -1343,6 +1344,18 @@ impl<'a> Context<'a> { } } } + HirExprKind::Sequence { ref exprs } if pos != ExprPos::Lhs => { + let mut last_handle = None; + for expr in exprs.iter() { + let (handle, _) = + self.lower_expect_inner(stmt, frontend, *expr, ExprPos::Rhs)?; + last_handle = Some(handle); + } + match last_handle { + Some(handle) => handle, + None => unreachable!(), + } + } _ => { return Err(Error { kind: ErrorKind::SemanticError( diff --git a/naga/src/front/glsl/parser/expressions.rs b/naga/src/front/glsl/parser/expressions.rs index 524863df5ee..5a00bf982f8 100644 --- a/naga/src/front/glsl/parser/expressions.rs +++ b/naga/src/front/glsl/parser/expressions.rs @@ -511,20 +511,43 @@ impl ParsingContext<'_> { }) } + // shader grammar :- + // expression : + // assignment_expression + // expression COMMA assignment_expression pub fn parse_expression( &mut self, frontend: &mut Frontend, ctx: &mut Context, stmt: &mut StmtContext, ) -> Result> { + let mut exprs = Vec::new(); let mut expr = self.parse_assignment(frontend, ctx, stmt)?; + exprs.push(expr); while let TokenValue::Comma = self.expect_peek(frontend)?.value { self.bump(frontend)?; expr = self.parse_assignment(frontend, ctx, stmt)?; + exprs.push(expr); } - Ok(expr) + if exprs.len() == 1 { + Ok(expr) + } else { + let mut meta = stmt.hir_exprs[exprs[0]].meta; + for &e in &exprs[1..] { + meta.subsume(stmt.hir_exprs[e].meta); + } + expr = stmt.hir_exprs.append( + HirExpr { + kind: HirExprKind::Sequence { exprs }, + meta, + }, + Default::default(), + ); + + Ok(expr) + } } } diff --git a/naga/src/front/glsl/parser/functions.rs b/naga/src/front/glsl/parser/functions.rs index 441e99130a0..c6cc24e535b 100644 --- a/naga/src/front/glsl/parser/functions.rs +++ b/naga/src/front/glsl/parser/functions.rs @@ -432,16 +432,29 @@ impl ParsingContext<'_> { meta } + + // shader grammar :- + // iteration_statement : (third option only) + // FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope TokenValue::For => { + //FOR let mut meta = self.bump(frontend)?.meta; - ctx.symbol_table.push_scope(); - self.expect(frontend, TokenValue::LeftParen)?; + self.expect(frontend, TokenValue::LeftParen)?; // LEFT_PAREN + // shader grammar :- + // for_init_statement : + // expression_statement + // declaration_statement if self.bump_if(frontend, TokenValue::Semicolon).is_none() { if self.peek_type_name(frontend) || self.peek_type_qualifier(frontend) { self.parse_declaration(frontend, ctx, false, is_inside_loop)?; + // declaration_statement (basically the same as `declaration`) } else { + // shader grammar :- + // expression_statement : + // SEMICOLON + // expression SEMICOLON let mut stmt = ctx.stmt_ctx(); let expr = self.parse_expression(frontend, ctx, &mut stmt)?; ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?; @@ -449,6 +462,10 @@ impl ParsingContext<'_> { } } + // shader grammar :- + // for_rest_statement : + // conditionopt SEMICOLON + // conditionopt SEMICOLON expression let loop_body = ctx.new_body(|ctx| { if self.bump_if(frontend, TokenValue::Semicolon).is_none() { let (expr, expr_meta) = if self.peek_type_name(frontend) @@ -508,6 +525,7 @@ impl ParsingContext<'_> { Ok(()) })?; + // shader grammar just has a single expression here, which can be multiple expressions separated by commas... let continuing = ctx.new_body(|ctx| { match self.expect_peek(frontend)?.value { TokenValue::RightParen => {} diff --git a/naga/tests/in/glsl/multipart-for-loop.frag b/naga/tests/in/glsl/multipart-for-loop.frag new file mode 100644 index 00000000000..2e14fc6c83a --- /dev/null +++ b/naga/tests/in/glsl/multipart-for-loop.frag @@ -0,0 +1,19 @@ +// issue #6208 https://github.com/gfx-rs/wgpu/issues/6208 +# version 460 + +void main() { + float a = 1.0; + float b = 0.25; + float c = 1.5; + int i = 20; + + // tests for multiple expressions in first part (if it's a expression, not declaration)! + // also the third part! + for (i = 0, c-=1.0; i < 25; i++, b+=0.01) { + a -= 0.02; + } + + // a, b and c should be all ~0.5! + // right now it only ever takes the last expression in the block... + // leading to infinite loops, lost devices and incorrect results. +} \ No newline at end of file diff --git a/naga/tests/out/wgsl/glsl-multipart-for-loop.frag.wgsl b/naga/tests/out/wgsl/glsl-multipart-for-loop.frag.wgsl new file mode 100644 index 00000000000..8443de11098 --- /dev/null +++ b/naga/tests/out/wgsl/glsl-multipart-for-loop.frag.wgsl @@ -0,0 +1,33 @@ +fn main_1() { + var a: f32 = 1f; + var b: f32 = 0.25f; + var c: f32 = 1.5f; + var i: i32 = 20i; + + i = 0i; + let _e9 = c; + c = (_e9 - 1f); + loop { + let _e12 = i; + if !((_e12 < 25i)) { + break; + } + { + let _e22 = a; + a = (_e22 - 0.02f); + } + continuing { + let _e16 = i; + i = (_e16 + 1i); + let _e19 = b; + b = (_e19 + 0.01f); + } + } + return; +} + +@fragment +fn main() { + main_1(); + return; +}