Skip to content

Commit

Permalink
[wgsl-in] Implement module-level scoping.
Browse files Browse the repository at this point in the history
Fixes #1745: Support out-of-order module scope declarations in WGSL
Fixes #1044: Forbid local variable shadowing in WGSL
Fixes #2076: [wgsl-in] no error for duplicated type definition
Fixes #2071: Global item does not support 'const'
Fixes #2105: [wgsl-in] Type aliases for a vecN<T> doesn't work when constructing vec from a single argument
Fixes #1775: Referencing a function without a return type yields an unknown identifier error.
Fixes #2089: Error span reported on the declaration of a variable instead of its use
Fixes #1996: [wgsl-in] Confusing error: "expected unsigned/signed integer literal, found '1'"

Separate parsing from lowering by generating an AST, which desugars as
much as possible down to something like Naga IR. The AST is then used
to resolve identifiers while lowering to Naga IR.

Co-authored-by: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com>
Co-authored-by: Jim Blandy <jimb@red-bean.com>
  • Loading branch information
3 people committed Jan 12, 2023
1 parent 7effd12 commit 6035b07
Show file tree
Hide file tree
Showing 104 changed files with 9,846 additions and 8,030 deletions.
4 changes: 2 additions & 2 deletions src/back/wgsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,7 @@ impl<W: Write> Writer<W> {
} => {
let name = &self.names[&NameKey::Constant(handle)];
// First write only constant name
write!(self.out, "let {}: ", name)?;
write!(self.out, "const {}: ", name)?;
// Next write constant type and value
match *value {
crate::ScalarValue::Sint(value) => {
Expand All @@ -1874,7 +1874,7 @@ impl<W: Write> Writer<W> {
crate::ConstantInner::Composite { ty, ref components } => {
let name = &self.names[&NameKey::Constant(handle)];
// First write only constant name
write!(self.out, "let {}: ", name)?;
write!(self.out, "const {}: ", name)?;
// Next write constant type
self.write_type(module, ty)?;

Expand Down
2 changes: 1 addition & 1 deletion src/front/glsl/parser_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ fn expressions() {
# version 450
void main() {
uint index = 0;
--index;
++index;
}
Expand Down
15 changes: 1 addition & 14 deletions src/front/glsl/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,20 +300,7 @@ impl Parser {
meta: Span,
) -> Result<Handle<Type>> {
self.typifier_grow(ctx, expr, meta)?;
let resolution = &ctx.typifier[expr];
Ok(match *resolution {
// If the resolution is already a handle return early
crate::proc::TypeResolution::Handle(ty) => ty,
// If it's a value we need to clone it
crate::proc::TypeResolution::Value(_) => match resolution.clone() {
// This is unreachable
crate::proc::TypeResolution::Handle(ty) => ty,
// Add the value to the type arena and return the handle
crate::proc::TypeResolution::Value(inner) => {
self.module.types.insert(Type { name: None, inner }, meta)
}
},
})
Ok(ctx.typifier.register_type(expr, &mut self.module.types))
}

/// Invalidates the cached type resolution for `expr` forcing a recomputation
Expand Down
67 changes: 63 additions & 4 deletions src/front/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,35 @@ impl super::ConstantInner {
}
}

/// Helper processor that derives the types of all expressions.
/// A table of types for an `Arena<Expression>`.
///
/// A front end can use a `Typifier` to get types for an arena's expressions
/// while it is still contributing expressions to it. At any point, you can call
/// [`typifier.grow(expr, arena, ctx)`], where `expr` is a `Handle<Expression>`
/// referring to something in `arena`, and the `Typifier` will resolve the types
/// of all the expressions up to and including `expr`. Then you can write
/// `typifier[handle]` to get the type of any handle at or before `expr`.
///
/// Note that `Typifier` does *not* build an `Arena<Type>` as a part of its
/// usual operation. Ideally, a module's type arena should only contain types
/// actually needed by `Handle<Type>`s elsewhere in the module — functions,
/// variables, [`Compose`] expressions, other types, and so on — so we don't
/// want every little thing that occurs as the type of some intermediate
/// expression to show up there.
///
/// Instead, `Typifier` accumulates a [`TypeResolution`] for each expression,
/// which refers to the `Arena<Type>` in the [`ResolveContext`] passed to `grow`
/// as needed. [`TypeResolution`] is a lightweight representation for
/// intermediate types like this; see its documentation for details.
///
/// If you do need to register a `Typifier`'s conclusion in an `Arena<Type>`
/// (say, for a [`LocalVariable`] whose type you've inferred), you can use
/// [`register_type`] to do so.
///
/// [`typifier.grow(expr, arena)`]: Typifier::grow
/// [`register_type`]: Typifier::register_type
/// [`Compose`]: crate::Expression::Compose
/// [`LocalVariable`]: crate::LocalVariable
#[derive(Debug, Default)]
pub struct Typifier {
resolutions: Vec<TypeResolution>,
Expand All @@ -89,6 +117,34 @@ impl Typifier {
self.resolutions[expr_handle.index()].inner_with(types)
}

/// Add an expression's type to an `Arena<Type>`.
///
/// Add the type of `expr_handle` to `types`, and return a `Handle<Type>`
/// referring to it.
///
/// # Note
///
/// If you just need a [`TypeInner`] for `expr_handle`'s type, consider
/// using `typifier[expression].inner_with(types)` instead. Calling
/// [`TypeResolution::inner_with`] often lets us avoid adding anything to
/// the arena, which can significantly reduce the number of types that end
/// up in the final module.
///
/// [`TypeInner`]: crate::TypeInner
pub fn register_type(
&self,
expr_handle: Handle<crate::Expression>,
types: &mut UniqueArena<crate::Type>,
) -> Handle<crate::Type> {
match self[expr_handle].clone() {
TypeResolution::Handle(handle) => handle,
TypeResolution::Value(inner) => {
types.insert(crate::Type { name: None, inner }, crate::Span::UNDEFINED)
}
}
}

/// Grow this typifier until it contains a type for `expr_handle`.
pub fn grow(
&mut self,
expr_handle: Handle<crate::Expression>,
Expand All @@ -106,10 +162,13 @@ impl Typifier {
Ok(())
}

/// Invalidates the cached type resolution for `expr_handle` forcing a recomputation
/// Recompute the type resolution for `expr_handle`.
///
/// If the type of `expr_handle` hasn't yet been calculated, call
/// [`grow`](Self::grow) to ensure it is covered.
///
/// If the type of the expression hasn't yet been calculated a
/// [`grow`](Self::grow) is performed instead
/// In either case, when this returns, `self[expr_handle]` should be an
/// updated type resolution for `expr_handle`.
pub fn invalidate(
&mut self,
expr_handle: Handle<crate::Expression>,
Expand Down
Loading

0 comments on commit 6035b07

Please sign in to comment.