naga/front/glsl/parser/
declarations.rs

1use alloc::{string::String, vec, vec::Vec};
2
3use super::{DeclarationContext, ParsingContext, Result};
4use crate::{
5    front::glsl::{
6        ast::{
7            GlobalLookup, GlobalLookupKind, Precision, QualifierKey, QualifierValue,
8            StorageQualifier, StructLayout, TypeQualifiers,
9        },
10        context::{Context, ExprPos},
11        error::ExpectedToken,
12        offset,
13        token::{Token, TokenValue},
14        types::scalar_components,
15        variables::{GlobalOrConstant, VarDeclaration},
16        Error, ErrorKind, Frontend, Span,
17    },
18    proc::Alignment,
19    AddressSpace, Expression, FunctionResult, Handle, Scalar, ScalarKind, Statement, StructMember,
20    Type, TypeInner,
21};
22
23/// Helper method used to retrieve the child type of `ty` at
24/// index `i`.
25///
26/// # Note
27///
28/// Does not check if the index is valid and returns the same type
29/// when indexing out-of-bounds a struct or indexing a non indexable
30/// type.
31fn element_or_member_type(
32    ty: Handle<Type>,
33    i: usize,
34    types: &mut crate::UniqueArena<Type>,
35) -> Handle<Type> {
36    match types[ty].inner {
37        // The child type of a vector is a scalar of the same kind and width
38        TypeInner::Vector { scalar, .. } => types.insert(
39            Type {
40                name: None,
41                inner: TypeInner::Scalar(scalar),
42            },
43            Default::default(),
44        ),
45        // The child type of a matrix is a vector of floats with the same
46        // width and the size of the matrix rows.
47        TypeInner::Matrix { rows, scalar, .. } => types.insert(
48            Type {
49                name: None,
50                inner: TypeInner::Vector { size: rows, scalar },
51            },
52            Default::default(),
53        ),
54        // The child type of an array is the base type of the array
55        TypeInner::Array { base, .. } => base,
56        // The child type of a struct at index `i` is the type of it's
57        // member at that same index.
58        //
59        // In case the index is out of bounds the same type is returned
60        TypeInner::Struct { ref members, .. } => {
61            members.get(i).map(|member| member.ty).unwrap_or(ty)
62        }
63        // The type isn't indexable, the same type is returned
64        _ => ty,
65    }
66}
67
68impl ParsingContext<'_> {
69    pub fn parse_external_declaration(
70        &mut self,
71        frontend: &mut Frontend,
72        global_ctx: &mut Context,
73    ) -> Result<()> {
74        if self
75            .parse_declaration(frontend, global_ctx, true, false)?
76            .is_none()
77        {
78            let token = self.bump(frontend)?;
79            match token.value {
80                TokenValue::Semicolon if frontend.meta.version == 460 => Ok(()),
81                _ => {
82                    let expected = match frontend.meta.version {
83                        460 => vec![TokenValue::Semicolon.into(), ExpectedToken::Eof],
84                        _ => vec![ExpectedToken::Eof],
85                    };
86                    Err(Error {
87                        kind: ErrorKind::InvalidToken(token.value, expected),
88                        meta: token.meta,
89                    })
90                }
91            }
92        } else {
93            Ok(())
94        }
95    }
96
97    pub fn parse_initializer(
98        &mut self,
99        frontend: &mut Frontend,
100        ty: Handle<Type>,
101        ctx: &mut Context,
102    ) -> Result<(Handle<Expression>, Span)> {
103        // initializer:
104        //     assignment_expression
105        //     LEFT_BRACE initializer_list RIGHT_BRACE
106        //     LEFT_BRACE initializer_list COMMA RIGHT_BRACE
107        //
108        // initializer_list:
109        //     initializer
110        //     initializer_list COMMA initializer
111        if let Some(Token { mut meta, .. }) = self.bump_if(frontend, TokenValue::LeftBrace) {
112            // initializer_list
113            let mut components = Vec::new();
114            loop {
115                // The type expected to be parsed inside the initializer list
116                let new_ty = element_or_member_type(ty, components.len(), &mut ctx.module.types);
117
118                components.push(self.parse_initializer(frontend, new_ty, ctx)?.0);
119
120                let token = self.bump(frontend)?;
121                match token.value {
122                    TokenValue::Comma => {
123                        if let Some(Token { meta: end_meta, .. }) =
124                            self.bump_if(frontend, TokenValue::RightBrace)
125                        {
126                            meta.subsume(end_meta);
127                            break;
128                        }
129                    }
130                    TokenValue::RightBrace => {
131                        meta.subsume(token.meta);
132                        break;
133                    }
134                    _ => {
135                        return Err(Error {
136                            kind: ErrorKind::InvalidToken(
137                                token.value,
138                                vec![TokenValue::Comma.into(), TokenValue::RightBrace.into()],
139                            ),
140                            meta: token.meta,
141                        })
142                    }
143                }
144            }
145
146            Ok((
147                ctx.add_expression(Expression::Compose { ty, components }, meta)?,
148                meta,
149            ))
150        } else {
151            let mut stmt = ctx.stmt_ctx();
152            let expr = self.parse_assignment(frontend, ctx, &mut stmt)?;
153            let (mut init, init_meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
154
155            let scalar_components = scalar_components(&ctx.module.types[ty].inner);
156            if let Some(scalar) = scalar_components {
157                ctx.implicit_conversion(&mut init, init_meta, scalar)?;
158            }
159
160            Ok((init, init_meta))
161        }
162    }
163
164    // Note: caller preparsed the type and qualifiers
165    // Note: caller skips this if the fallthrough token is not expected to be consumed here so this
166    // produced Error::InvalidToken if it isn't consumed
167    pub fn parse_init_declarator_list(
168        &mut self,
169        frontend: &mut Frontend,
170        mut ty: Handle<Type>,
171        ctx: &mut DeclarationContext,
172    ) -> Result<()> {
173        // init_declarator_list:
174        //     single_declaration
175        //     init_declarator_list COMMA IDENTIFIER
176        //     init_declarator_list COMMA IDENTIFIER array_specifier
177        //     init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer
178        //     init_declarator_list COMMA IDENTIFIER EQUAL initializer
179        //
180        // single_declaration:
181        //     fully_specified_type
182        //     fully_specified_type IDENTIFIER
183        //     fully_specified_type IDENTIFIER array_specifier
184        //     fully_specified_type IDENTIFIER array_specifier EQUAL initializer
185        //     fully_specified_type IDENTIFIER EQUAL initializer
186
187        // Consume any leading comma, e.g. this is valid: `float, a=1;`
188        if self
189            .peek(frontend)
190            .is_some_and(|t| t.value == TokenValue::Comma)
191        {
192            self.next(frontend);
193        }
194
195        loop {
196            let token = self.bump(frontend)?;
197            let name = match token.value {
198                TokenValue::Semicolon => break,
199                TokenValue::Identifier(name) => name,
200                _ => {
201                    return Err(Error {
202                        kind: ErrorKind::InvalidToken(
203                            token.value,
204                            vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()],
205                        ),
206                        meta: token.meta,
207                    })
208                }
209            };
210            let mut meta = token.meta;
211
212            // array_specifier
213            // array_specifier EQUAL initializer
214            // EQUAL initializer
215
216            // parse an array specifier if it exists
217            // NOTE: unlike other parse methods this one doesn't expect an array specifier and
218            // returns Ok(None) rather than an error if there is not one
219            self.parse_array_specifier(frontend, ctx.ctx, &mut meta, &mut ty)?;
220
221            let is_global_const =
222                ctx.qualifiers.storage.0 == StorageQualifier::Const && ctx.external;
223
224            let init = self
225                .bump_if(frontend, TokenValue::Assign)
226                .map::<Result<_>, _>(|_| {
227                    let prev_const = ctx.ctx.is_const;
228                    ctx.ctx.is_const = is_global_const;
229
230                    let (mut expr, init_meta) = self.parse_initializer(frontend, ty, ctx.ctx)?;
231
232                    let scalar_components = scalar_components(&ctx.ctx.module.types[ty].inner);
233                    if let Some(scalar) = scalar_components {
234                        ctx.ctx.implicit_conversion(&mut expr, init_meta, scalar)?;
235                    }
236
237                    ctx.ctx.is_const = prev_const;
238
239                    meta.subsume(init_meta);
240
241                    Ok(expr)
242                })
243                .transpose()?;
244
245            let decl_initializer;
246            let late_initializer;
247            if is_global_const {
248                decl_initializer = init;
249                late_initializer = None;
250            } else if ctx.external {
251                decl_initializer =
252                    init.and_then(|expr| ctx.ctx.lift_up_const_expression(expr).ok());
253                late_initializer = None;
254            } else if let Some(init) = init {
255                if ctx.is_inside_loop || !ctx.ctx.local_expression_kind_tracker.is_const(init) {
256                    decl_initializer = None;
257                    late_initializer = Some(init);
258                } else {
259                    decl_initializer = Some(init);
260                    late_initializer = None;
261                }
262            } else {
263                decl_initializer = None;
264                late_initializer = None;
265            };
266
267            let pointer = ctx.add_var(frontend, ty, name, decl_initializer, meta)?;
268
269            if let Some(value) = late_initializer {
270                ctx.ctx.emit_restart();
271                ctx.ctx.body.push(Statement::Store { pointer, value }, meta);
272            }
273
274            let token = self.bump(frontend)?;
275            match token.value {
276                TokenValue::Semicolon => break,
277                TokenValue::Comma => {}
278                _ => {
279                    return Err(Error {
280                        kind: ErrorKind::InvalidToken(
281                            token.value,
282                            vec![TokenValue::Comma.into(), TokenValue::Semicolon.into()],
283                        ),
284                        meta: token.meta,
285                    })
286                }
287            }
288        }
289
290        Ok(())
291    }
292
293    /// `external` whether or not we are in a global or local context
294    pub fn parse_declaration(
295        &mut self,
296        frontend: &mut Frontend,
297        ctx: &mut Context,
298        external: bool,
299        is_inside_loop: bool,
300    ) -> Result<Option<Span>> {
301        //declaration:
302        //    function_prototype  SEMICOLON
303        //
304        //    init_declarator_list SEMICOLON
305        //    PRECISION precision_qualifier type_specifier SEMICOLON
306        //
307        //    type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE SEMICOLON
308        //    type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON
309        //    type_qualifier IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE IDENTIFIER array_specifier SEMICOLON
310        //    type_qualifier SEMICOLON type_qualifier IDENTIFIER SEMICOLON
311        //    type_qualifier IDENTIFIER identifier_list SEMICOLON
312
313        if self.peek_type_qualifier(frontend) || self.peek_type_name(frontend) {
314            let mut qualifiers = self.parse_type_qualifiers(frontend, ctx)?;
315
316            if self.peek_type_name(frontend) {
317                // This branch handles variables and function prototypes and if
318                // external is true also function definitions
319                let (ty, mut meta) = self.parse_type(frontend, ctx)?;
320
321                let token = self.bump(frontend)?;
322                let token_fallthrough = match token.value {
323                    TokenValue::Identifier(name) => match self.expect_peek(frontend)?.value {
324                        TokenValue::LeftParen => {
325                            // This branch handles function definition and prototypes
326                            self.bump(frontend)?;
327
328                            let result = ty.map(|ty| FunctionResult { ty, binding: None });
329
330                            let mut context = Context::new(
331                                frontend,
332                                ctx.module,
333                                false,
334                                ctx.global_expression_kind_tracker,
335                            )?;
336
337                            self.parse_function_args(frontend, &mut context)?;
338
339                            let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;
340                            meta.subsume(end_meta);
341
342                            let token = self.bump(frontend)?;
343                            return match token.value {
344                                TokenValue::Semicolon => {
345                                    // This branch handles function prototypes
346                                    frontend.add_prototype(context, name, result, meta);
347
348                                    Ok(Some(meta))
349                                }
350                                TokenValue::LeftBrace if external => {
351                                    // This branch handles function definitions
352                                    // as you can see by the guard this branch
353                                    // only happens if external is also true
354
355                                    // parse the body
356                                    self.parse_compound_statement(
357                                        token.meta,
358                                        frontend,
359                                        &mut context,
360                                        &mut None,
361                                        false,
362                                    )?;
363
364                                    frontend.add_function(context, name, result, meta);
365
366                                    Ok(Some(meta))
367                                }
368                                _ if external => Err(Error {
369                                    kind: ErrorKind::InvalidToken(
370                                        token.value,
371                                        vec![
372                                            TokenValue::LeftBrace.into(),
373                                            TokenValue::Semicolon.into(),
374                                        ],
375                                    ),
376                                    meta: token.meta,
377                                }),
378                                _ => Err(Error {
379                                    kind: ErrorKind::InvalidToken(
380                                        token.value,
381                                        vec![TokenValue::Semicolon.into()],
382                                    ),
383                                    meta: token.meta,
384                                }),
385                            };
386                        }
387                        // Pass the token to the init_declarator_list parser
388                        _ => Token {
389                            value: TokenValue::Identifier(name),
390                            meta: token.meta,
391                        },
392                    },
393                    // Pass the token to the init_declarator_list parser
394                    _ => token,
395                };
396
397                // If program execution has reached here then this will be a
398                // init_declarator_list
399                // token_fallthrough will have a token that was already bumped
400                if let Some(ty) = ty {
401                    let mut ctx = DeclarationContext {
402                        qualifiers,
403                        external,
404                        is_inside_loop,
405                        ctx,
406                    };
407
408                    self.backtrack(token_fallthrough)?;
409                    self.parse_init_declarator_list(frontend, ty, &mut ctx)?;
410                } else {
411                    frontend.errors.push(Error {
412                        kind: ErrorKind::SemanticError("Declaration cannot have void type".into()),
413                        meta,
414                    })
415                }
416
417                Ok(Some(meta))
418            } else {
419                // This branch handles struct definitions and modifiers like
420                // ```glsl
421                // layout(early_fragment_tests);
422                // ```
423                let token = self.bump(frontend)?;
424                match token.value {
425                    TokenValue::Identifier(ty_name) => {
426                        if self.bump_if(frontend, TokenValue::LeftBrace).is_some() {
427                            self.parse_block_declaration(
428                                frontend,
429                                ctx,
430                                &mut qualifiers,
431                                ty_name,
432                                token.meta,
433                            )
434                            .map(Some)
435                        } else {
436                            if qualifiers.invariant.take().is_some() {
437                                frontend.make_variable_invariant(ctx, &ty_name, token.meta)?;
438
439                                qualifiers.unused_errors(&mut frontend.errors);
440                                self.expect(frontend, TokenValue::Semicolon)?;
441                                return Ok(Some(qualifiers.span));
442                            }
443
444                            //TODO: declaration
445                            // type_qualifier IDENTIFIER SEMICOLON
446                            // type_qualifier IDENTIFIER identifier_list SEMICOLON
447                            Err(Error {
448                                kind: ErrorKind::NotImplemented("variable qualifier"),
449                                meta: token.meta,
450                            })
451                        }
452                    }
453                    TokenValue::Semicolon => {
454                        if let Some(value) =
455                            qualifiers.uint_layout_qualifier("local_size_x", &mut frontend.errors)
456                        {
457                            frontend.meta.workgroup_size[0] = value;
458                        }
459                        if let Some(value) =
460                            qualifiers.uint_layout_qualifier("local_size_y", &mut frontend.errors)
461                        {
462                            frontend.meta.workgroup_size[1] = value;
463                        }
464                        if let Some(value) =
465                            qualifiers.uint_layout_qualifier("local_size_z", &mut frontend.errors)
466                        {
467                            frontend.meta.workgroup_size[2] = value;
468                        }
469
470                        frontend.meta.early_fragment_tests |= qualifiers
471                            .none_layout_qualifier("early_fragment_tests", &mut frontend.errors);
472
473                        qualifiers.unused_errors(&mut frontend.errors);
474
475                        Ok(Some(qualifiers.span))
476                    }
477                    _ => Err(Error {
478                        kind: ErrorKind::InvalidToken(
479                            token.value,
480                            vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()],
481                        ),
482                        meta: token.meta,
483                    }),
484                }
485            }
486        } else {
487            match self.peek(frontend).map(|t| &t.value) {
488                Some(&TokenValue::Precision) => {
489                    // PRECISION precision_qualifier type_specifier SEMICOLON
490                    self.bump(frontend)?;
491
492                    let token = self.bump(frontend)?;
493                    let _ = match token.value {
494                        TokenValue::PrecisionQualifier(p) => p,
495                        _ => {
496                            return Err(Error {
497                                kind: ErrorKind::InvalidToken(
498                                    token.value,
499                                    vec![
500                                        TokenValue::PrecisionQualifier(Precision::High).into(),
501                                        TokenValue::PrecisionQualifier(Precision::Medium).into(),
502                                        TokenValue::PrecisionQualifier(Precision::Low).into(),
503                                    ],
504                                ),
505                                meta: token.meta,
506                            })
507                        }
508                    };
509
510                    let (ty, meta) = self.parse_type_non_void(frontend, ctx)?;
511
512                    match ctx.module.types[ty].inner {
513                        TypeInner::Scalar(Scalar {
514                            kind: ScalarKind::Float | ScalarKind::Sint,
515                            ..
516                        }) => {}
517                        _ => frontend.errors.push(Error {
518                            kind: ErrorKind::SemanticError(
519                                "Precision statement can only work on floats and ints".into(),
520                            ),
521                            meta,
522                        }),
523                    }
524
525                    self.expect(frontend, TokenValue::Semicolon)?;
526
527                    Ok(Some(meta))
528                }
529                _ => Ok(None),
530            }
531        }
532    }
533
534    pub fn parse_block_declaration(
535        &mut self,
536        frontend: &mut Frontend,
537        ctx: &mut Context,
538        qualifiers: &mut TypeQualifiers,
539        ty_name: String,
540        mut meta: Span,
541    ) -> Result<Span> {
542        let layout = match qualifiers.layout_qualifiers.remove(&QualifierKey::Layout) {
543            Some((QualifierValue::Layout(l), _)) => l,
544            None => {
545                if let StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) =
546                    qualifiers.storage.0
547                {
548                    StructLayout::Std430
549                } else {
550                    StructLayout::Std140
551                }
552            }
553            _ => unreachable!(),
554        };
555
556        let mut members = Vec::new();
557        let span = self.parse_struct_declaration_list(frontend, ctx, &mut members, layout)?;
558        self.expect(frontend, TokenValue::RightBrace)?;
559
560        let mut ty = ctx.module.types.insert(
561            Type {
562                name: Some(ty_name),
563                inner: TypeInner::Struct {
564                    members: members.clone(),
565                    span,
566                },
567            },
568            Default::default(),
569        );
570
571        let token = self.bump(frontend)?;
572        let name = match token.value {
573            TokenValue::Semicolon => None,
574            TokenValue::Identifier(name) => {
575                self.parse_array_specifier(frontend, ctx, &mut meta, &mut ty)?;
576
577                self.expect(frontend, TokenValue::Semicolon)?;
578
579                Some(name)
580            }
581            _ => {
582                return Err(Error {
583                    kind: ErrorKind::InvalidToken(
584                        token.value,
585                        vec![ExpectedToken::Identifier, TokenValue::Semicolon.into()],
586                    ),
587                    meta: token.meta,
588                })
589            }
590        };
591
592        let global = frontend.add_global_var(
593            ctx,
594            VarDeclaration {
595                qualifiers,
596                ty,
597                name,
598                init: None,
599                meta,
600            },
601        )?;
602
603        for (i, k, ty) in members.into_iter().enumerate().filter_map(|(i, m)| {
604            let ty = m.ty;
605            m.name.map(|s| (i as u32, s, ty))
606        }) {
607            let lookup = GlobalLookup {
608                kind: match global {
609                    GlobalOrConstant::Global(handle) => GlobalLookupKind::BlockSelect(handle, i),
610                    GlobalOrConstant::Constant(handle) => GlobalLookupKind::Constant(handle, ty),
611                },
612                entry_arg: None,
613                mutable: true,
614            };
615            ctx.add_global(&k, lookup)?;
616
617            frontend.global_variables.push((k, lookup));
618        }
619
620        Ok(meta)
621    }
622
623    // TODO: Accept layout arguments
624    pub fn parse_struct_declaration_list(
625        &mut self,
626        frontend: &mut Frontend,
627        ctx: &mut Context,
628        members: &mut Vec<StructMember>,
629        layout: StructLayout,
630    ) -> Result<u32> {
631        let mut span = 0;
632        let mut align = Alignment::ONE;
633
634        loop {
635            // TODO: type_qualifier
636
637            let (base_ty, mut meta) = self.parse_type_non_void(frontend, ctx)?;
638
639            loop {
640                let (name, name_meta) = self.expect_ident(frontend)?;
641                let mut ty = base_ty;
642                self.parse_array_specifier(frontend, ctx, &mut meta, &mut ty)?;
643
644                meta.subsume(name_meta);
645
646                let info = offset::calculate_offset(
647                    ty,
648                    meta,
649                    layout,
650                    &mut ctx.module.types,
651                    &mut frontend.errors,
652                );
653
654                let member_alignment = info.align;
655                span = member_alignment.round_up(span);
656                align = member_alignment.max(align);
657
658                members.push(StructMember {
659                    name: Some(name),
660                    ty: info.ty,
661                    binding: None,
662                    offset: span,
663                });
664
665                span += info.span;
666
667                if self.bump_if(frontend, TokenValue::Comma).is_none() {
668                    break;
669                }
670            }
671
672            self.expect(frontend, TokenValue::Semicolon)?;
673
674            if let TokenValue::RightBrace = self.expect_peek(frontend)?.value {
675                break;
676            }
677        }
678
679        span = align.round_up(span);
680
681        Ok(span)
682    }
683}