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