naga/front/
mod.rs

1/*!
2Frontend parsers that consume binary and text shaders and load them into [`Module`](super::Module)s.
3*/
4
5#[cfg(any(feature = "spv-in", feature = "wgsl-in"))]
6pub(crate) mod interpolator;
7mod type_gen;
8
9#[cfg(feature = "spv-in")]
10pub mod atomic_upgrade;
11#[cfg(feature = "glsl-in")]
12pub mod glsl;
13#[cfg(feature = "spv-in")]
14pub mod spv;
15#[cfg(feature = "wgsl-in")]
16pub mod wgsl;
17
18use alloc::{boxed::Box, vec, vec::Vec};
19use core::ops;
20
21use crate::{
22    arena::{Arena, Handle, HandleVec, UniqueArena},
23    proc::{ResolveContext, ResolveError, TypeResolution},
24    FastHashMap,
25};
26
27/// A table of types for an `Arena<Expression>`.
28///
29/// A front end can use a `Typifier` to get types for an arena's expressions
30/// while it is still contributing expressions to it. At any point, you can call
31/// [`typifier.grow(expr, arena, ctx)`], where `expr` is a `Handle<Expression>`
32/// referring to something in `arena`, and the `Typifier` will resolve the types
33/// of all the expressions up to and including `expr`. Then you can write
34/// `typifier[handle]` to get the type of any handle at or before `expr`.
35///
36/// Note that `Typifier` does *not* build an `Arena<Type>` as a part of its
37/// usual operation. Ideally, a module's type arena should only contain types
38/// actually needed by `Handle<Type>`s elsewhere in the module — functions,
39/// variables, [`Compose`] expressions, other types, and so on — so we don't
40/// want every little thing that occurs as the type of some intermediate
41/// expression to show up there.
42///
43/// Instead, `Typifier` accumulates a [`TypeResolution`] for each expression,
44/// which refers to the `Arena<Type>` in the [`ResolveContext`] passed to `grow`
45/// as needed. [`TypeResolution`] is a lightweight representation for
46/// intermediate types like this; see its documentation for details.
47///
48/// If you do need to register a `Typifier`'s conclusion in an `Arena<Type>`
49/// (say, for a [`LocalVariable`] whose type you've inferred), you can use
50/// [`register_type`] to do so.
51///
52/// [`typifier.grow(expr, arena)`]: Typifier::grow
53/// [`register_type`]: Typifier::register_type
54/// [`Compose`]: crate::Expression::Compose
55/// [`LocalVariable`]: crate::LocalVariable
56#[derive(Debug, Default)]
57pub struct Typifier {
58    resolutions: HandleVec<crate::Expression, TypeResolution>,
59}
60
61impl Typifier {
62    pub const fn new() -> Self {
63        Typifier {
64            resolutions: HandleVec::new(),
65        }
66    }
67
68    pub fn reset(&mut self) {
69        self.resolutions.clear()
70    }
71
72    pub fn get<'a>(
73        &'a self,
74        expr_handle: Handle<crate::Expression>,
75        types: &'a UniqueArena<crate::Type>,
76    ) -> &'a crate::TypeInner {
77        self.resolutions[expr_handle].inner_with(types)
78    }
79
80    /// Add an expression's type to an `Arena<Type>`.
81    ///
82    /// Add the type of `expr_handle` to `types`, and return a `Handle<Type>`
83    /// referring to it.
84    ///
85    /// # Note
86    ///
87    /// If you just need a [`TypeInner`] for `expr_handle`'s type, consider
88    /// using `typifier[expression].inner_with(types)` instead. Calling
89    /// [`TypeResolution::inner_with`] often lets us avoid adding anything to
90    /// the arena, which can significantly reduce the number of types that end
91    /// up in the final module.
92    ///
93    /// [`TypeInner`]: crate::TypeInner
94    pub fn register_type(
95        &self,
96        expr_handle: Handle<crate::Expression>,
97        types: &mut UniqueArena<crate::Type>,
98    ) -> Handle<crate::Type> {
99        match self[expr_handle].clone() {
100            TypeResolution::Handle(handle) => handle,
101            TypeResolution::Value(inner) => {
102                types.insert(crate::Type { name: None, inner }, crate::Span::UNDEFINED)
103            }
104        }
105    }
106
107    /// Grow this typifier until it contains a type for `expr_handle`.
108    pub fn grow(
109        &mut self,
110        expr_handle: Handle<crate::Expression>,
111        expressions: &Arena<crate::Expression>,
112        ctx: &ResolveContext,
113    ) -> Result<(), ResolveError> {
114        if self.resolutions.len() <= expr_handle.index() {
115            for (eh, expr) in expressions.iter().skip(self.resolutions.len()) {
116                //Note: the closure can't `Err` by construction
117                let resolution = ctx.resolve(expr, |h| Ok(&self.resolutions[h]))?;
118                log::debug!("Resolving {eh:?} = {expr:?} : {resolution:?}");
119                self.resolutions.insert(eh, resolution);
120            }
121        }
122        Ok(())
123    }
124
125    /// Recompute the type resolution for `expr_handle`.
126    ///
127    /// If the type of `expr_handle` hasn't yet been calculated, call
128    /// [`grow`](Self::grow) to ensure it is covered.
129    ///
130    /// In either case, when this returns, `self[expr_handle]` should be an
131    /// updated type resolution for `expr_handle`.
132    pub fn invalidate(
133        &mut self,
134        expr_handle: Handle<crate::Expression>,
135        expressions: &Arena<crate::Expression>,
136        ctx: &ResolveContext,
137    ) -> Result<(), ResolveError> {
138        if self.resolutions.len() <= expr_handle.index() {
139            self.grow(expr_handle, expressions, ctx)
140        } else {
141            let expr = &expressions[expr_handle];
142            //Note: the closure can't `Err` by construction
143            let resolution = ctx.resolve(expr, |h| Ok(&self.resolutions[h]))?;
144            self.resolutions[expr_handle] = resolution;
145            Ok(())
146        }
147    }
148}
149
150impl ops::Index<Handle<crate::Expression>> for Typifier {
151    type Output = TypeResolution;
152    fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
153        &self.resolutions[handle]
154    }
155}
156
157/// Type representing a lexical scope, associating a name to a single variable
158///
159/// The scope is generic over the variable representation and name representation
160/// in order to allow larger flexibility on the frontends on how they might
161/// represent them.
162type Scope<Name, Var> = FastHashMap<Name, Var>;
163
164/// Structure responsible for managing variable lookups and keeping track of
165/// lexical scopes
166///
167/// The symbol table is generic over the variable representation and its name
168/// to allow larger flexibility on the frontends on how they might represent them.
169///
170/// ```
171/// use naga::front::SymbolTable;
172///
173/// // Create a new symbol table with `u32`s representing the variable
174/// let mut symbol_table: SymbolTable<&str, u32> = SymbolTable::default();
175///
176/// // Add two variables named `var1` and `var2` with 0 and 2 respectively
177/// symbol_table.add("var1", 0);
178/// symbol_table.add("var2", 2);
179///
180/// // Check that `var1` exists and is `0`
181/// assert_eq!(symbol_table.lookup("var1"), Some(&0));
182///
183/// // Push a new scope and add a variable to it named `var1` shadowing the
184/// // variable of our previous scope
185/// symbol_table.push_scope();
186/// symbol_table.add("var1", 1);
187///
188/// // Check that `var1` now points to the new value of `1` and `var2` still
189/// // exists with its value of `2`
190/// assert_eq!(symbol_table.lookup("var1"), Some(&1));
191/// assert_eq!(symbol_table.lookup("var2"), Some(&2));
192///
193/// // Pop the scope
194/// symbol_table.pop_scope();
195///
196/// // Check that `var1` now refers to our initial variable with value `0`
197/// assert_eq!(symbol_table.lookup("var1"), Some(&0));
198/// ```
199///
200/// Scopes are ordered as a LIFO stack so a variable defined in a later scope
201/// with the same name as another variable defined in a earlier scope will take
202/// precedence in the lookup. Scopes can be added with [`push_scope`] and
203/// removed with [`pop_scope`].
204///
205/// A root scope is added when the symbol table is created and must always be
206/// present. Trying to pop it will result in a panic.
207///
208/// Variables can be added with [`add`] and looked up with [`lookup`]. Adding a
209/// variable will do so in the currently active scope and as mentioned
210/// previously a lookup will search from the current scope to the root scope.
211///
212/// [`push_scope`]: Self::push_scope
213/// [`pop_scope`]: Self::push_scope
214/// [`add`]: Self::add
215/// [`lookup`]: Self::lookup
216pub struct SymbolTable<Name, Var> {
217    /// Stack of lexical scopes. Not all scopes are active; see [`cursor`].
218    ///
219    /// [`cursor`]: Self::cursor
220    scopes: Vec<Scope<Name, Var>>,
221    /// Limit of the [`scopes`] stack (exclusive). By using a separate value for
222    /// the stack length instead of `Vec`'s own internal length, the scopes can
223    /// be reused to cache memory allocations.
224    ///
225    /// [`scopes`]: Self::scopes
226    cursor: usize,
227    lookup_cursor_is_one_behind: bool,
228}
229
230impl<Name, Var> SymbolTable<Name, Var> {
231    /// Adds a new lexical scope.
232    ///
233    /// All variables declared after this point will be added to this scope
234    /// until another scope is pushed or [`pop_scope`] is called, causing this
235    /// scope to be removed along with all variables added to it.
236    ///
237    /// # PANICS
238    /// - If the current lookup scope doesn't match the current scope
239    ///
240    /// [`pop_scope`]: Self::pop_scope
241    pub fn push_scope(&mut self) {
242        self.check_lookup_scope_matches_current_scope();
243
244        // If the cursor is equal to the scope's stack length then we need to
245        // push another empty scope. Otherwise we can reuse the already existing
246        // scope.
247        if self.scopes.len() == self.cursor {
248            self.scopes.push(FastHashMap::default())
249        } else {
250            self.scopes[self.cursor].clear();
251        }
252
253        self.cursor += 1;
254    }
255
256    /// Removes the current lexical scope and all its variables
257    ///
258    /// # PANICS
259    /// - If the current lexical scope is the root scope
260    /// - If the current lookup scope doesn't match the current scope
261    pub fn pop_scope(&mut self) {
262        // Despite the method title, the variables are only deleted when the
263        // scope is reused. This is because while a clear is inevitable if the
264        // scope needs to be reused, there are cases where the scope might be
265        // popped and not reused, i.e. if another scope with the same nesting
266        // level is never pushed again.
267        assert!(self.cursor != 1, "Tried to pop the root scope");
268        self.check_lookup_scope_matches_current_scope();
269
270        self.cursor -= 1;
271    }
272
273    /// Reduces the lookup scope by one level.
274    ///
275    /// # PANICS
276    /// - If the current lookup scope doesn't match the current scope
277    pub fn reduce_lookup_scope(&mut self) {
278        self.check_lookup_scope_matches_current_scope();
279        self.lookup_cursor_is_one_behind = true;
280    }
281
282    /// Resets the lookup scope to the current scope.
283    ///
284    /// # PANICS
285    /// - If the current lookup scope already matches the current scope
286    pub fn reset_lookup_scope(&mut self) {
287        assert!(
288            self.lookup_cursor_is_one_behind,
289            "current lookup scope already matches the current scope"
290        );
291        self.lookup_cursor_is_one_behind = false;
292    }
293
294    fn check_lookup_scope_matches_current_scope(&self) {
295        assert!(
296            !self.lookup_cursor_is_one_behind,
297            "current lookup scope doesn't match the current scope"
298        );
299    }
300}
301
302impl<Name, Var> SymbolTable<Name, Var>
303where
304    Name: core::hash::Hash + Eq,
305{
306    /// Perform a lookup for a variable named `name`.
307    ///
308    /// As stated in the struct level documentation the lookup will proceed from
309    /// the current scope to the root scope, returning `Some` when a variable is
310    /// found or `None` if there doesn't exist a variable with `name` in any
311    /// scope.
312    pub fn lookup<Q>(&self, name: &Q) -> Option<&Var>
313    where
314        Name: core::borrow::Borrow<Q>,
315        Q: core::hash::Hash + Eq + ?Sized,
316    {
317        let cursor = self
318            .cursor
319            .saturating_sub(self.lookup_cursor_is_one_behind.into());
320        // Iterate backwards through the scopes and try to find the variable
321        for scope in self.scopes[..cursor].iter().rev() {
322            if let Some(var) = scope.get(name) {
323                return Some(var);
324            }
325        }
326
327        None
328    }
329
330    /// Adds a new variable to the current scope.
331    ///
332    /// Returns the previous variable with the same name in this scope if it
333    /// exists, so that the frontend might handle it in case variable shadowing
334    /// is disallowed.
335    pub fn add(&mut self, name: Name, var: Var) -> Option<Var> {
336        self.scopes[self.cursor - 1].insert(name, var)
337    }
338
339    /// Adds a new variable to the root scope.
340    ///
341    /// This is used in GLSL for builtins which aren't known in advance and only
342    /// when used for the first time, so there must be a way to add those
343    /// declarations to the root unconditionally from the current scope.
344    ///
345    /// Returns the previous variable with the same name in the root scope if it
346    /// exists, so that the frontend might handle it in case variable shadowing
347    /// is disallowed.
348    pub fn add_root(&mut self, name: Name, var: Var) -> Option<Var> {
349        self.scopes[0].insert(name, var)
350    }
351}
352
353impl<Name, Var> Default for SymbolTable<Name, Var> {
354    /// Constructs a new symbol table with a root scope
355    fn default() -> Self {
356        Self {
357            scopes: vec![FastHashMap::default()],
358            cursor: 1,
359            lookup_cursor_is_one_behind: false,
360        }
361    }
362}
363
364use core::fmt;
365
366impl<Name: fmt::Debug, Var: fmt::Debug> fmt::Debug for SymbolTable<Name, Var> {
367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368        f.write_str("SymbolTable ")?;
369        f.debug_list()
370            .entries(self.scopes[..self.cursor].iter())
371            .finish()
372    }
373}
374
375impl crate::Module {
376    pub fn get_or_insert_default_doc_comments(&mut self) -> &mut Box<crate::DocComments> {
377        self.doc_comments
378            .get_or_insert_with(|| Box::new(crate::DocComments::default()))
379    }
380}