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