naga/front/glsl/parser/
types.rs

1use alloc::{vec, vec::Vec};
2use core::num::NonZeroU32;
3
4use crate::{
5    front::glsl::{
6        ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers},
7        context::Context,
8        error::ExpectedToken,
9        parser::ParsingContext,
10        token::{Token, TokenValue},
11        Error, ErrorKind, Frontend, Result,
12    },
13    AddressSpace, ArraySize, Handle, Span, Type, TypeInner,
14};
15
16impl ParsingContext<'_> {
17    /// Parses an optional array_specifier returning whether or not it's present
18    /// and modifying the type handle if it exists
19    pub fn parse_array_specifier(
20        &mut self,
21        frontend: &mut Frontend,
22        ctx: &mut Context,
23        span: &mut Span,
24        ty: &mut Handle<Type>,
25    ) -> Result<()> {
26        while self.parse_array_specifier_single(frontend, ctx, span, ty)? {}
27        Ok(())
28    }
29
30    /// Implementation of [`Self::parse_array_specifier`] for a single array_specifier
31    fn parse_array_specifier_single(
32        &mut self,
33        frontend: &mut Frontend,
34        ctx: &mut Context,
35        span: &mut Span,
36        ty: &mut Handle<Type>,
37    ) -> Result<bool> {
38        if self.bump_if(frontend, TokenValue::LeftBracket).is_some() {
39            let size = if let Some(Token { meta, .. }) =
40                self.bump_if(frontend, TokenValue::RightBracket)
41            {
42                span.subsume(meta);
43                ArraySize::Dynamic
44            } else {
45                let (value, constant_span) = self.parse_uint_constant(frontend, ctx)?;
46                let size = NonZeroU32::new(value).ok_or(Error {
47                    kind: ErrorKind::SemanticError("Array size must be greater than zero".into()),
48                    meta: constant_span,
49                })?;
50                let end_span = self.expect(frontend, TokenValue::RightBracket)?.meta;
51                span.subsume(end_span);
52                ArraySize::Constant(size)
53            };
54
55            frontend.layouter.update(ctx.module.to_ctx()).unwrap();
56            let stride = frontend.layouter[*ty].to_stride();
57            *ty = ctx.module.types.insert(
58                Type {
59                    name: None,
60                    inner: TypeInner::Array {
61                        base: *ty,
62                        size,
63                        stride,
64                    },
65                },
66                *span,
67            );
68
69            Ok(true)
70        } else {
71            Ok(false)
72        }
73    }
74
75    pub fn parse_type(
76        &mut self,
77        frontend: &mut Frontend,
78        ctx: &mut Context,
79    ) -> Result<(Option<Handle<Type>>, Span)> {
80        let token = self.bump(frontend)?;
81        let mut handle = match token.value {
82            TokenValue::Void => return Ok((None, token.meta)),
83            TokenValue::TypeName(ty) => ctx.module.types.insert(ty, token.meta),
84            TokenValue::Struct => {
85                let mut meta = token.meta;
86                let ty_name = self.expect_ident(frontend)?.0;
87                self.expect(frontend, TokenValue::LeftBrace)?;
88                let mut members = Vec::new();
89                let span = self.parse_struct_declaration_list(
90                    frontend,
91                    ctx,
92                    &mut members,
93                    StructLayout::Std140,
94                )?;
95                let end_meta = self.expect(frontend, TokenValue::RightBrace)?.meta;
96                meta.subsume(end_meta);
97                let ty = ctx.module.types.insert(
98                    Type {
99                        name: Some(ty_name.clone()),
100                        inner: TypeInner::Struct { members, span },
101                    },
102                    meta,
103                );
104                frontend.lookup_type.insert(ty_name, ty);
105                ty
106            }
107            TokenValue::Identifier(ident) => match frontend.lookup_type.get(&ident) {
108                Some(ty) => *ty,
109                None => {
110                    return Err(Error {
111                        kind: ErrorKind::UnknownType(ident),
112                        meta: token.meta,
113                    })
114                }
115            },
116            _ => {
117                return Err(Error {
118                    kind: ErrorKind::InvalidToken(
119                        token.value,
120                        vec![
121                            TokenValue::Void.into(),
122                            TokenValue::Struct.into(),
123                            ExpectedToken::TypeName,
124                        ],
125                    ),
126                    meta: token.meta,
127                });
128            }
129        };
130
131        let mut span = token.meta;
132        self.parse_array_specifier(frontend, ctx, &mut span, &mut handle)?;
133        Ok((Some(handle), span))
134    }
135
136    pub fn parse_type_non_void(
137        &mut self,
138        frontend: &mut Frontend,
139        ctx: &mut Context,
140    ) -> Result<(Handle<Type>, Span)> {
141        let (maybe_ty, meta) = self.parse_type(frontend, ctx)?;
142        let ty = maybe_ty.ok_or_else(|| Error {
143            kind: ErrorKind::SemanticError("Type can't be void".into()),
144            meta,
145        })?;
146
147        Ok((ty, meta))
148    }
149
150    pub fn peek_type_qualifier(&mut self, frontend: &mut Frontend) -> bool {
151        self.peek(frontend).is_some_and(|t| match t.value {
152            TokenValue::Invariant
153            | TokenValue::Interpolation(_)
154            | TokenValue::Sampling(_)
155            | TokenValue::PrecisionQualifier(_)
156            | TokenValue::Const
157            | TokenValue::In
158            | TokenValue::Out
159            | TokenValue::Uniform
160            | TokenValue::Shared
161            | TokenValue::Buffer
162            | TokenValue::Restrict
163            | TokenValue::MemoryQualifier(_)
164            | TokenValue::Layout => true,
165            _ => false,
166        })
167    }
168
169    pub fn parse_type_qualifiers<'a>(
170        &mut self,
171        frontend: &mut Frontend,
172        ctx: &mut Context,
173    ) -> Result<TypeQualifiers<'a>> {
174        let mut qualifiers = TypeQualifiers::default();
175
176        while self.peek_type_qualifier(frontend) {
177            let token = self.bump(frontend)?;
178
179            // Handle layout qualifiers outside the match since this can push multiple values
180            if token.value == TokenValue::Layout {
181                self.parse_layout_qualifier_id_list(frontend, ctx, &mut qualifiers)?;
182                continue;
183            }
184
185            qualifiers.span.subsume(token.meta);
186
187            match token.value {
188                TokenValue::Invariant => {
189                    if qualifiers.invariant.is_some() {
190                        frontend.errors.push(Error {
191                            kind: ErrorKind::SemanticError(
192                                "Cannot use more than one invariant qualifier per declaration"
193                                    .into(),
194                            ),
195                            meta: token.meta,
196                        })
197                    }
198
199                    qualifiers.invariant = Some(token.meta);
200                }
201                TokenValue::Interpolation(i) => {
202                    if qualifiers.interpolation.is_some() {
203                        frontend.errors.push(Error {
204                            kind: ErrorKind::SemanticError(
205                                "Cannot use more than one interpolation qualifier per declaration"
206                                    .into(),
207                            ),
208                            meta: token.meta,
209                        })
210                    }
211
212                    qualifiers.interpolation = Some((i, token.meta));
213                }
214                TokenValue::Const
215                | TokenValue::In
216                | TokenValue::Out
217                | TokenValue::Uniform
218                | TokenValue::Shared
219                | TokenValue::Buffer => {
220                    let storage = match token.value {
221                        TokenValue::Const => StorageQualifier::Const,
222                        TokenValue::In => StorageQualifier::Input,
223                        TokenValue::Out => StorageQualifier::Output,
224                        TokenValue::Uniform => {
225                            StorageQualifier::AddressSpace(AddressSpace::Uniform)
226                        }
227                        TokenValue::Shared => {
228                            StorageQualifier::AddressSpace(AddressSpace::WorkGroup)
229                        }
230                        TokenValue::Buffer => {
231                            StorageQualifier::AddressSpace(AddressSpace::Storage {
232                                access: crate::StorageAccess::LOAD | crate::StorageAccess::STORE,
233                            })
234                        }
235                        _ => unreachable!(),
236                    };
237
238                    if StorageQualifier::AddressSpace(AddressSpace::Function)
239                        != qualifiers.storage.0
240                    {
241                        frontend.errors.push(Error {
242                            kind: ErrorKind::SemanticError(
243                                "Cannot use more than one storage qualifier per declaration".into(),
244                            ),
245                            meta: token.meta,
246                        });
247                    }
248
249                    qualifiers.storage = (storage, token.meta);
250                }
251                TokenValue::Sampling(s) => {
252                    if qualifiers.sampling.is_some() {
253                        frontend.errors.push(Error {
254                            kind: ErrorKind::SemanticError(
255                                "Cannot use more than one sampling qualifier per declaration"
256                                    .into(),
257                            ),
258                            meta: token.meta,
259                        })
260                    }
261
262                    qualifiers.sampling = Some((s, token.meta));
263                }
264                TokenValue::PrecisionQualifier(p) => {
265                    if qualifiers.precision.is_some() {
266                        frontend.errors.push(Error {
267                            kind: ErrorKind::SemanticError(
268                                "Cannot use more than one precision qualifier per declaration"
269                                    .into(),
270                            ),
271                            meta: token.meta,
272                        })
273                    }
274
275                    qualifiers.precision = Some((p, token.meta));
276                }
277                TokenValue::MemoryQualifier(access) => {
278                    let load_store = crate::StorageAccess::LOAD | crate::StorageAccess::STORE;
279                    let storage_access = qualifiers
280                        .storage_access
281                        .get_or_insert((load_store, Span::default()));
282
283                    if !storage_access.0.contains(!access & load_store) {
284                        frontend.errors.push(Error {
285                            kind: ErrorKind::SemanticError(
286                                "The same memory qualifier can only be used once".into(),
287                            ),
288                            meta: token.meta,
289                        })
290                    }
291
292                    storage_access.0 &= access;
293                    storage_access.1.subsume(token.meta);
294                }
295                TokenValue::Restrict => continue,
296                _ => unreachable!(),
297            };
298        }
299
300        Ok(qualifiers)
301    }
302
303    pub fn parse_layout_qualifier_id_list(
304        &mut self,
305        frontend: &mut Frontend,
306        ctx: &mut Context,
307        qualifiers: &mut TypeQualifiers,
308    ) -> Result<()> {
309        self.expect(frontend, TokenValue::LeftParen)?;
310        loop {
311            self.parse_layout_qualifier_id(frontend, ctx, &mut qualifiers.layout_qualifiers)?;
312
313            if self.bump_if(frontend, TokenValue::Comma).is_some() {
314                continue;
315            }
316
317            break;
318        }
319        let token = self.expect(frontend, TokenValue::RightParen)?;
320        qualifiers.span.subsume(token.meta);
321
322        Ok(())
323    }
324
325    pub fn parse_layout_qualifier_id(
326        &mut self,
327        frontend: &mut Frontend,
328        ctx: &mut Context,
329        qualifiers: &mut crate::FastHashMap<QualifierKey, (QualifierValue, Span)>,
330    ) -> Result<()> {
331        // layout_qualifier_id:
332        //     IDENTIFIER
333        //     IDENTIFIER EQUAL constant_expression
334        //     SHARED
335        let mut token = self.bump(frontend)?;
336        match token.value {
337            TokenValue::Identifier(name) => {
338                let (key, value) = match name.as_str() {
339                    "std140" => (
340                        QualifierKey::Layout,
341                        QualifierValue::Layout(StructLayout::Std140),
342                    ),
343                    "std430" => (
344                        QualifierKey::Layout,
345                        QualifierValue::Layout(StructLayout::Std430),
346                    ),
347                    "index" => {
348                        self.expect(frontend, TokenValue::Assign)?;
349                        let (value, end_meta) = self.parse_uint_constant(frontend, ctx)?;
350                        token.meta.subsume(end_meta);
351
352                        (QualifierKey::Index, QualifierValue::Uint(value))
353                    }
354                    word => {
355                        if let Some(format) = map_image_format(word) {
356                            (QualifierKey::Format, QualifierValue::Format(format))
357                        } else {
358                            let key = QualifierKey::String(name.into());
359                            let value = if self.bump_if(frontend, TokenValue::Assign).is_some() {
360                                let (value, end_meta) =
361                                    match self.parse_uint_constant(frontend, ctx) {
362                                        Ok(v) => v,
363                                        Err(e) => {
364                                            frontend.errors.push(e);
365                                            (0, Span::default())
366                                        }
367                                    };
368                                token.meta.subsume(end_meta);
369
370                                QualifierValue::Uint(value)
371                            } else {
372                                QualifierValue::None
373                            };
374
375                            (key, value)
376                        }
377                    }
378                };
379
380                qualifiers.insert(key, (value, token.meta));
381            }
382            _ => frontend.errors.push(Error {
383                kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]),
384                meta: token.meta,
385            }),
386        }
387
388        Ok(())
389    }
390
391    pub fn peek_type_name(&mut self, frontend: &mut Frontend) -> bool {
392        self.peek(frontend).is_some_and(|t| match t.value {
393            TokenValue::TypeName(_) | TokenValue::Void => true,
394            TokenValue::Struct => true,
395            TokenValue::Identifier(ref ident) => frontend.lookup_type.contains_key(ident),
396            _ => false,
397        })
398    }
399}
400
401fn map_image_format(word: &str) -> Option<crate::StorageFormat> {
402    use crate::StorageFormat as Sf;
403
404    let format = match word {
405        // float-image-format-qualifier:
406        "rgba32f" => Sf::Rgba32Float,
407        "rgba16f" => Sf::Rgba16Float,
408        "rg32f" => Sf::Rg32Float,
409        "rg16f" => Sf::Rg16Float,
410        "r11f_g11f_b10f" => Sf::Rg11b10Ufloat,
411        "r32f" => Sf::R32Float,
412        "r16f" => Sf::R16Float,
413        "rgba16" => Sf::Rgba16Unorm,
414        "rgb10_a2ui" => Sf::Rgb10a2Uint,
415        "rgb10_a2" => Sf::Rgb10a2Unorm,
416        "rgba8" => Sf::Rgba8Unorm,
417        "rg16" => Sf::Rg16Unorm,
418        "rg8" => Sf::Rg8Unorm,
419        "r16" => Sf::R16Unorm,
420        "r8" => Sf::R8Unorm,
421        "rgba16_snorm" => Sf::Rgba16Snorm,
422        "rgba8_snorm" => Sf::Rgba8Snorm,
423        "rg16_snorm" => Sf::Rg16Snorm,
424        "rg8_snorm" => Sf::Rg8Snorm,
425        "r16_snorm" => Sf::R16Snorm,
426        "r8_snorm" => Sf::R8Snorm,
427        // int-image-format-qualifier:
428        "rgba32i" => Sf::Rgba32Sint,
429        "rgba16i" => Sf::Rgba16Sint,
430        "rgba8i" => Sf::Rgba8Sint,
431        "rg32i" => Sf::Rg32Sint,
432        "rg16i" => Sf::Rg16Sint,
433        "rg8i" => Sf::Rg8Sint,
434        "r32i" => Sf::R32Sint,
435        "r16i" => Sf::R16Sint,
436        "r8i" => Sf::R8Sint,
437        // uint-image-format-qualifier:
438        "rgba32ui" => Sf::Rgba32Uint,
439        "rgba16ui" => Sf::Rgba16Uint,
440        "rgba8ui" => Sf::Rgba8Uint,
441        "r64ui" => Sf::R64Uint,
442        "rg32ui" => Sf::Rg32Uint,
443        "rg16ui" => Sf::Rg16Uint,
444        "rg8ui" => Sf::Rg8Uint,
445        "r32ui" => Sf::R32Uint,
446        "r16ui" => Sf::R16Uint,
447        "r8ui" => Sf::R8Uint,
448        // TODO: These next ones seem incorrect to me
449        // "rgb10_a2ui" => Sf::Rgb10a2Unorm,
450        _ => return None,
451    };
452
453    Some(format)
454}