naga/front/
mod.rs

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