naga/front/glsl/parser/
functions.rs

1use alloc::{vec, vec::Vec};
2
3use crate::front::glsl::context::ExprPos;
4use crate::front::glsl::Span;
5use crate::Literal;
6use crate::{
7    front::glsl::{
8        ast::ParameterQualifier,
9        context::Context,
10        parser::ParsingContext,
11        token::{Token, TokenValue},
12        variables::VarDeclaration,
13        Error, ErrorKind, Frontend, Result,
14    },
15    Block, Expression, Statement, SwitchCase, UnaryOperator,
16};
17
18impl ParsingContext<'_> {
19    pub fn peek_parameter_qualifier(&mut self, frontend: &mut Frontend) -> bool {
20        self.peek(frontend).is_some_and(|t| match t.value {
21            TokenValue::In | TokenValue::Out | TokenValue::InOut | TokenValue::Const => true,
22            _ => false,
23        })
24    }
25
26    /// Returns the parsed `ParameterQualifier` or `ParameterQualifier::In`
27    pub fn parse_parameter_qualifier(&mut self, frontend: &mut Frontend) -> ParameterQualifier {
28        if self.peek_parameter_qualifier(frontend) {
29            match self.bump(frontend).unwrap().value {
30                TokenValue::In => ParameterQualifier::In,
31                TokenValue::Out => ParameterQualifier::Out,
32                TokenValue::InOut => ParameterQualifier::InOut,
33                TokenValue::Const => ParameterQualifier::Const,
34                _ => unreachable!(),
35            }
36        } else {
37            ParameterQualifier::In
38        }
39    }
40
41    pub fn parse_statement(
42        &mut self,
43        frontend: &mut Frontend,
44        ctx: &mut Context,
45        terminator: &mut Option<usize>,
46        is_inside_loop: bool,
47    ) -> Result<Option<Span>> {
48        // Type qualifiers always identify a declaration statement
49        if self.peek_type_qualifier(frontend) {
50            return self.parse_declaration(frontend, ctx, false, is_inside_loop);
51        }
52
53        // Type names can identify either declaration statements or type constructors
54        // depending on whether the token following the type name is a `(` (LeftParen)
55        if self.peek_type_name(frontend) {
56            // Start by consuming the type name so that we can peek the token after it
57            let token = self.bump(frontend)?;
58            // Peek the next token and check if it's a `(` (LeftParen) if so the statement
59            // is a constructor, otherwise it's a declaration. We need to do the check
60            // beforehand and not in the if since we will backtrack before the if
61            let declaration = TokenValue::LeftParen != self.expect_peek(frontend)?.value;
62
63            self.backtrack(token)?;
64
65            if declaration {
66                return self.parse_declaration(frontend, ctx, false, is_inside_loop);
67            }
68        }
69
70        let new_break = || {
71            let mut block = Block::new();
72            block.push(Statement::Break, Span::default());
73            block
74        };
75
76        let &Token {
77            ref value,
78            mut meta,
79        } = self.expect_peek(frontend)?;
80
81        let meta_rest = match *value {
82            TokenValue::Continue => {
83                let meta = self.bump(frontend)?.meta;
84                ctx.body.push(Statement::Continue, meta);
85                terminator.get_or_insert(ctx.body.len());
86                self.expect(frontend, TokenValue::Semicolon)?.meta
87            }
88            TokenValue::Break => {
89                let meta = self.bump(frontend)?.meta;
90                ctx.body.push(Statement::Break, meta);
91                terminator.get_or_insert(ctx.body.len());
92                self.expect(frontend, TokenValue::Semicolon)?.meta
93            }
94            TokenValue::Return => {
95                self.bump(frontend)?;
96                let (value, meta) = match self.expect_peek(frontend)?.value {
97                    TokenValue::Semicolon => (None, self.bump(frontend)?.meta),
98                    _ => {
99                        // TODO: Implicit conversions
100                        let mut stmt = ctx.stmt_ctx();
101                        let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
102                        self.expect(frontend, TokenValue::Semicolon)?;
103                        let (handle, meta) =
104                            ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
105                        (Some(handle), meta)
106                    }
107                };
108
109                ctx.emit_restart();
110
111                ctx.body.push(Statement::Return { value }, meta);
112                terminator.get_or_insert(ctx.body.len());
113
114                meta
115            }
116            TokenValue::Discard => {
117                let meta = self.bump(frontend)?.meta;
118                ctx.body.push(Statement::Kill, meta);
119                terminator.get_or_insert(ctx.body.len());
120
121                self.expect(frontend, TokenValue::Semicolon)?.meta
122            }
123            TokenValue::If => {
124                let mut meta = self.bump(frontend)?.meta;
125
126                self.expect(frontend, TokenValue::LeftParen)?;
127                let condition = {
128                    let mut stmt = ctx.stmt_ctx();
129                    let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
130                    let (handle, more_meta) =
131                        ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
132                    meta.subsume(more_meta);
133                    handle
134                };
135                self.expect(frontend, TokenValue::RightParen)?;
136
137                let accept = ctx.new_body(|ctx| {
138                    if let Some(more_meta) =
139                        self.parse_statement(frontend, ctx, &mut None, is_inside_loop)?
140                    {
141                        meta.subsume(more_meta);
142                    }
143                    Ok(())
144                })?;
145
146                let reject = ctx.new_body(|ctx| {
147                    if self.bump_if(frontend, TokenValue::Else).is_some() {
148                        if let Some(more_meta) =
149                            self.parse_statement(frontend, ctx, &mut None, is_inside_loop)?
150                        {
151                            meta.subsume(more_meta);
152                        }
153                    }
154                    Ok(())
155                })?;
156
157                ctx.body.push(
158                    Statement::If {
159                        condition,
160                        accept,
161                        reject,
162                    },
163                    meta,
164                );
165
166                meta
167            }
168            TokenValue::Switch => {
169                let mut meta = self.bump(frontend)?.meta;
170                let end_meta;
171
172                self.expect(frontend, TokenValue::LeftParen)?;
173
174                let (selector, uint) = {
175                    let mut stmt = ctx.stmt_ctx();
176                    let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
177                    let (root, meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
178                    let uint = ctx.resolve_type(root, meta)?.scalar_kind()
179                        == Some(crate::ScalarKind::Uint);
180                    (root, uint)
181                };
182
183                self.expect(frontend, TokenValue::RightParen)?;
184
185                ctx.emit_restart();
186
187                let mut cases = Vec::new();
188                // Track if any default case is present in the switch statement.
189                let mut default_present = false;
190
191                self.expect(frontend, TokenValue::LeftBrace)?;
192                loop {
193                    let value = match self.expect_peek(frontend)?.value {
194                        TokenValue::Case => {
195                            self.bump(frontend)?;
196
197                            let (const_expr, meta) = self.parse_constant_expression(
198                                frontend,
199                                ctx.module,
200                                ctx.global_expression_kind_tracker,
201                            )?;
202
203                            match ctx.module.global_expressions[const_expr] {
204                                Expression::Literal(Literal::I32(value)) => match uint {
205                                    // This unchecked cast isn't good, but since
206                                    // we only reach this code when the selector
207                                    // is unsigned but the case label is signed,
208                                    // verification will reject the module
209                                    // anyway (which also matches GLSL's rules).
210                                    true => crate::SwitchValue::U32(value as u32),
211                                    false => crate::SwitchValue::I32(value),
212                                },
213                                Expression::Literal(Literal::U32(value)) => {
214                                    crate::SwitchValue::U32(value)
215                                }
216                                _ => {
217                                    frontend.errors.push(Error {
218                                        kind: ErrorKind::SemanticError(
219                                            "Case values can only be integers".into(),
220                                        ),
221                                        meta,
222                                    });
223
224                                    crate::SwitchValue::I32(0)
225                                }
226                            }
227                        }
228                        TokenValue::Default => {
229                            self.bump(frontend)?;
230                            default_present = true;
231                            crate::SwitchValue::Default
232                        }
233                        TokenValue::RightBrace => {
234                            end_meta = self.bump(frontend)?.meta;
235                            break;
236                        }
237                        _ => {
238                            let Token { value, meta } = self.bump(frontend)?;
239                            return Err(Error {
240                                kind: ErrorKind::InvalidToken(
241                                    value,
242                                    vec![
243                                        TokenValue::Case.into(),
244                                        TokenValue::Default.into(),
245                                        TokenValue::RightBrace.into(),
246                                    ],
247                                ),
248                                meta,
249                            });
250                        }
251                    };
252
253                    self.expect(frontend, TokenValue::Colon)?;
254
255                    let mut fall_through = true;
256
257                    let body = ctx.new_body(|ctx| {
258                        let mut case_terminator = None;
259                        loop {
260                            match self.expect_peek(frontend)?.value {
261                                TokenValue::Case | TokenValue::Default | TokenValue::RightBrace => {
262                                    break
263                                }
264                                _ => {
265                                    self.parse_statement(
266                                        frontend,
267                                        ctx,
268                                        &mut case_terminator,
269                                        is_inside_loop,
270                                    )?;
271                                }
272                            }
273                        }
274
275                        if let Some(mut idx) = case_terminator {
276                            if let Statement::Break = ctx.body[idx - 1] {
277                                fall_through = false;
278                                idx -= 1;
279                            }
280
281                            ctx.body.cull(idx..)
282                        }
283
284                        Ok(())
285                    })?;
286
287                    cases.push(SwitchCase {
288                        value,
289                        body,
290                        fall_through,
291                    })
292                }
293
294                meta.subsume(end_meta);
295
296                // NOTE: do not unwrap here since a switch statement isn't required
297                // to have any cases.
298                if let Some(case) = cases.last_mut() {
299                    // GLSL requires that the last case not be empty, so we check
300                    // that here and produce an error otherwise (fall_through must
301                    // also be checked because `break`s count as statements but
302                    // they aren't added to the body)
303                    if case.body.is_empty() && case.fall_through {
304                        frontend.errors.push(Error {
305                            kind: ErrorKind::SemanticError(
306                                "last case/default label must be followed by statements".into(),
307                            ),
308                            meta,
309                        })
310                    }
311
312                    // GLSL allows the last case to not have any `break` statement,
313                    // this would mark it as fall through but naga's IR requires that
314                    // the last case must not be fall through, so we mark need to mark
315                    // the last case as not fall through always.
316                    case.fall_through = false;
317                }
318
319                // Add an empty default case in case non was present, this is needed because
320                // naga's IR requires that all switch statements must have a default case but
321                // GLSL doesn't require that, so we might need to add an empty default case.
322                if !default_present {
323                    cases.push(SwitchCase {
324                        value: crate::SwitchValue::Default,
325                        body: Block::new(),
326                        fall_through: false,
327                    })
328                }
329
330                ctx.body.push(Statement::Switch { selector, cases }, meta);
331
332                meta
333            }
334            TokenValue::While => {
335                let mut meta = self.bump(frontend)?.meta;
336
337                let loop_body = ctx.new_body(|ctx| {
338                    let mut stmt = ctx.stmt_ctx();
339                    self.expect(frontend, TokenValue::LeftParen)?;
340                    let root = self.parse_expression(frontend, ctx, &mut stmt)?;
341                    meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);
342
343                    let (expr, expr_meta) = ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?;
344                    let condition = ctx.add_expression(
345                        Expression::Unary {
346                            op: UnaryOperator::LogicalNot,
347                            expr,
348                        },
349                        expr_meta,
350                    )?;
351
352                    ctx.emit_restart();
353
354                    ctx.body.push(
355                        Statement::If {
356                            condition,
357                            accept: new_break(),
358                            reject: Block::new(),
359                        },
360                        Span::default(),
361                    );
362
363                    meta.subsume(expr_meta);
364
365                    if let Some(body_meta) = self.parse_statement(frontend, ctx, &mut None, true)? {
366                        meta.subsume(body_meta);
367                    }
368                    Ok(())
369                })?;
370
371                ctx.body.push(
372                    Statement::Loop {
373                        body: loop_body,
374                        continuing: Block::new(),
375                        break_if: None,
376                    },
377                    meta,
378                );
379
380                meta
381            }
382            TokenValue::Do => {
383                let mut meta = self.bump(frontend)?.meta;
384
385                let loop_body = ctx.new_body(|ctx| {
386                    let mut terminator = None;
387                    self.parse_statement(frontend, ctx, &mut terminator, true)?;
388
389                    let mut stmt = ctx.stmt_ctx();
390
391                    self.expect(frontend, TokenValue::While)?;
392                    self.expect(frontend, TokenValue::LeftParen)?;
393                    let root = self.parse_expression(frontend, ctx, &mut stmt)?;
394                    let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;
395
396                    meta.subsume(end_meta);
397
398                    let (expr, expr_meta) = ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?;
399                    let condition = ctx.add_expression(
400                        Expression::Unary {
401                            op: UnaryOperator::LogicalNot,
402                            expr,
403                        },
404                        expr_meta,
405                    )?;
406
407                    ctx.emit_restart();
408
409                    ctx.body.push(
410                        Statement::If {
411                            condition,
412                            accept: new_break(),
413                            reject: Block::new(),
414                        },
415                        Span::default(),
416                    );
417
418                    if let Some(idx) = terminator {
419                        ctx.body.cull(idx..)
420                    }
421                    Ok(())
422                })?;
423
424                ctx.body.push(
425                    Statement::Loop {
426                        body: loop_body,
427                        continuing: Block::new(),
428                        break_if: None,
429                    },
430                    meta,
431                );
432
433                meta
434            }
435            TokenValue::For => {
436                let mut meta = self.bump(frontend)?.meta;
437
438                ctx.symbol_table.push_scope();
439                self.expect(frontend, TokenValue::LeftParen)?;
440
441                if self.bump_if(frontend, TokenValue::Semicolon).is_none() {
442                    if self.peek_type_name(frontend) || self.peek_type_qualifier(frontend) {
443                        self.parse_declaration(frontend, ctx, false, is_inside_loop)?;
444                    } else {
445                        let mut stmt = ctx.stmt_ctx();
446                        let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
447                        ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;
448                        self.expect(frontend, TokenValue::Semicolon)?;
449                    }
450                }
451
452                let loop_body = ctx.new_body(|ctx| {
453                    if self.bump_if(frontend, TokenValue::Semicolon).is_none() {
454                        let (expr, expr_meta) = if self.peek_type_name(frontend)
455                            || self.peek_type_qualifier(frontend)
456                        {
457                            let mut qualifiers = self.parse_type_qualifiers(frontend, ctx)?;
458                            let (ty, mut meta) = self.parse_type_non_void(frontend, ctx)?;
459                            let name = self.expect_ident(frontend)?.0;
460
461                            self.expect(frontend, TokenValue::Assign)?;
462
463                            let (value, end_meta) = self.parse_initializer(frontend, ty, ctx)?;
464                            meta.subsume(end_meta);
465
466                            let decl = VarDeclaration {
467                                qualifiers: &mut qualifiers,
468                                ty,
469                                name: Some(name),
470                                init: None,
471                                meta,
472                            };
473
474                            let pointer = frontend.add_local_var(ctx, decl)?;
475
476                            ctx.emit_restart();
477
478                            ctx.body.push(Statement::Store { pointer, value }, meta);
479
480                            (value, end_meta)
481                        } else {
482                            let mut stmt = ctx.stmt_ctx();
483                            let root = self.parse_expression(frontend, ctx, &mut stmt)?;
484                            ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?
485                        };
486
487                        let condition = ctx.add_expression(
488                            Expression::Unary {
489                                op: UnaryOperator::LogicalNot,
490                                expr,
491                            },
492                            expr_meta,
493                        )?;
494
495                        ctx.emit_restart();
496
497                        ctx.body.push(
498                            Statement::If {
499                                condition,
500                                accept: new_break(),
501                                reject: Block::new(),
502                            },
503                            Span::default(),
504                        );
505
506                        self.expect(frontend, TokenValue::Semicolon)?;
507                    }
508                    Ok(())
509                })?;
510
511                let continuing = ctx.new_body(|ctx| {
512                    match self.expect_peek(frontend)?.value {
513                        TokenValue::RightParen => {}
514                        _ => {
515                            let mut stmt = ctx.stmt_ctx();
516                            let rest = self.parse_expression(frontend, ctx, &mut stmt)?;
517                            ctx.lower(stmt, frontend, rest, ExprPos::Rhs)?;
518                        }
519                    }
520                    Ok(())
521                })?;
522
523                meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);
524
525                let loop_body = ctx.with_body(loop_body, |ctx| {
526                    if let Some(stmt_meta) = self.parse_statement(frontend, ctx, &mut None, true)? {
527                        meta.subsume(stmt_meta);
528                    }
529                    Ok(())
530                })?;
531
532                ctx.body.push(
533                    Statement::Loop {
534                        body: loop_body,
535                        continuing,
536                        break_if: None,
537                    },
538                    meta,
539                );
540
541                ctx.symbol_table.pop_scope();
542
543                meta
544            }
545            TokenValue::LeftBrace => {
546                let mut meta = self.bump(frontend)?.meta;
547
548                let mut block_terminator = None;
549
550                let block = ctx.new_body(|ctx| {
551                    let block_meta = self.parse_compound_statement(
552                        meta,
553                        frontend,
554                        ctx,
555                        &mut block_terminator,
556                        is_inside_loop,
557                    )?;
558                    meta.subsume(block_meta);
559                    Ok(())
560                })?;
561
562                ctx.body.push(Statement::Block(block), meta);
563                if block_terminator.is_some() {
564                    terminator.get_or_insert(ctx.body.len());
565                }
566
567                meta
568            }
569            TokenValue::Semicolon => self.bump(frontend)?.meta,
570            _ => {
571                // Attempt to force expression parsing for remainder of the
572                // tokens. Unknown or invalid tokens will be caught there and
573                // turned into an error.
574                let mut stmt = ctx.stmt_ctx();
575                let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
576                ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;
577                self.expect(frontend, TokenValue::Semicolon)?.meta
578            }
579        };
580
581        meta.subsume(meta_rest);
582        Ok(Some(meta))
583    }
584
585    pub fn parse_compound_statement(
586        &mut self,
587        mut meta: Span,
588        frontend: &mut Frontend,
589        ctx: &mut Context,
590        terminator: &mut Option<usize>,
591        is_inside_loop: bool,
592    ) -> Result<Span> {
593        ctx.symbol_table.push_scope();
594
595        loop {
596            if let Some(Token {
597                meta: brace_meta, ..
598            }) = self.bump_if(frontend, TokenValue::RightBrace)
599            {
600                meta.subsume(brace_meta);
601                break;
602            }
603
604            let stmt = self.parse_statement(frontend, ctx, terminator, is_inside_loop)?;
605
606            if let Some(stmt_meta) = stmt {
607                meta.subsume(stmt_meta);
608            }
609        }
610
611        if let Some(idx) = *terminator {
612            ctx.body.cull(idx..)
613        }
614
615        ctx.symbol_table.pop_scope();
616
617        Ok(meta)
618    }
619
620    pub fn parse_function_args(
621        &mut self,
622        frontend: &mut Frontend,
623        ctx: &mut Context,
624    ) -> Result<()> {
625        if self.bump_if(frontend, TokenValue::Void).is_some() {
626            return Ok(());
627        }
628
629        loop {
630            if self.peek_type_name(frontend) || self.peek_parameter_qualifier(frontend) {
631                let qualifier = self.parse_parameter_qualifier(frontend);
632                let mut ty = self.parse_type_non_void(frontend, ctx)?.0;
633
634                match self.expect_peek(frontend)?.value {
635                    TokenValue::Comma => {
636                        self.bump(frontend)?;
637                        ctx.add_function_arg(None, ty, qualifier)?;
638                        continue;
639                    }
640                    TokenValue::Identifier(_) => {
641                        let mut name = self.expect_ident(frontend)?;
642                        self.parse_array_specifier(frontend, ctx, &mut name.1, &mut ty)?;
643
644                        ctx.add_function_arg(Some(name), ty, qualifier)?;
645
646                        if self.bump_if(frontend, TokenValue::Comma).is_some() {
647                            continue;
648                        }
649
650                        break;
651                    }
652                    _ => break,
653                }
654            }
655
656            break;
657        }
658
659        Ok(())
660    }
661}