diff --git a/src/front/glsl/ast.rs b/src/front/glsl/ast.rs index 2594a76e80..4c2054bfe6 100644 --- a/src/front/glsl/ast.rs +++ b/src/front/glsl/ast.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{borrow::Cow, fmt}; use super::{builtins::MacroCall, context::ExprPos, Span}; use crate::{ @@ -134,19 +134,118 @@ pub enum HirExprKind { }, } +#[derive(Debug, Hash, PartialEq, Eq)] +pub enum QualifierKey<'a> { + String(Cow<'a, str>), + /// Used for `std140` and `std430` layout qualifiers + Layout, +} + #[derive(Debug)] -pub enum TypeQualifier { - StorageQualifier(StorageQualifier), - Interpolation(Interpolation), - Set(u32), - Binding(u32), - Location(u32), - WorkGroupSize(usize, u32), - Sampling(Sampling), +pub enum QualifierValue { + None, + Uint(u32), Layout(StructLayout), - Precision(Precision), - EarlyFragmentTests, - StorageAccess(StorageAccess), +} + +#[derive(Debug, Default)] +pub struct TypeQualifiers<'a> { + pub span: Span, + pub storage: (StorageQualifier, Span), + pub interpolation: Option<(Interpolation, Span)>, + pub precision: Option<(Precision, Span)>, + pub sampling: Option<(Sampling, Span)>, + pub storage_acess: Option<(StorageAccess, Span)>, + pub layout_qualifiers: crate::FastHashMap, (QualifierValue, Span)>, +} + +impl<'a> TypeQualifiers<'a> { + /// Appends `errors` with errors for all unused qualifiers + pub fn unused_errors(&self, errors: &mut Vec) { + if let Some((_, meta)) = self.interpolation { + errors.push(super::Error { + kind: super::ErrorKind::SemanticError( + "Interpolation qualifiers can only be used in in/out variables".into(), + ), + meta, + }); + } + + if let Some((_, meta)) = self.sampling { + errors.push(super::Error { + kind: super::ErrorKind::SemanticError( + "Sampling qualifiers can only be used in in/out variables".into(), + ), + meta, + }); + } + + if let Some((_, meta)) = self.storage_acess { + errors.push(super::Error { + kind: super::ErrorKind::SemanticError( + "Memory qualifiers can only be used in storage variables".into(), + ), + meta, + }); + } + + for &(_, meta) in self.layout_qualifiers.values() { + errors.push(super::Error { + kind: super::ErrorKind::SemanticError("Unexpected qualifier".into()), + meta, + }); + } + } + + /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't + /// a [`QualifierValue::Uint`] + pub fn uint_layout_qualifier( + &mut self, + name: &'a str, + errors: &mut Vec, + ) -> Option { + match self + .layout_qualifiers + .remove(&QualifierKey::String(name.into())) + { + Some((QualifierValue::Uint(v), _)) => Some(v), + Some((_, meta)) => { + errors.push(super::Error { + kind: super::ErrorKind::SemanticError("Qualifier expects a uint value".into()), + meta, + }); + // Return a dummy value instead of `None` to differentiate from + // the qualifier not existing, since some parts might require the + // qualifier to exist and throwing another error that it doesn't + // exist would be unhelpful + Some(0) + } + _ => None, + } + } + + /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't + /// a [`QualifierValue::None`] + pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec) -> bool { + match self + .layout_qualifiers + .remove(&QualifierKey::String(name.into())) + { + Some((QualifierValue::None, _)) => true, + Some((_, meta)) => { + errors.push(super::Error { + kind: super::ErrorKind::SemanticError( + "Qualifier doesn't expect a value".into(), + ), + meta, + }); + // Return a `true` to since the qualifier is defined and adding + // another error for it not being defined would be unhelpful + true + } + _ => false, + } + } } #[derive(Debug, Clone)] @@ -169,6 +268,12 @@ pub enum StorageQualifier { Const, } +impl Default for StorageQualifier { + fn default() -> Self { + StorageQualifier::AddressSpace(AddressSpace::Function) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StructLayout { Std140, diff --git a/src/front/glsl/parser.rs b/src/front/glsl/parser.rs index 2180b9b6ec..8527887930 100644 --- a/src/front/glsl/parser.rs +++ b/src/front/glsl/parser.rs @@ -1,5 +1,5 @@ use super::{ - ast::{FunctionKind, Profile, TypeQualifier}, + ast::{FunctionKind, Profile, TypeQualifiers}, context::{Context, ExprPos}, error::ExpectedToken, error::{Error, ErrorKind}, @@ -366,15 +366,15 @@ impl Parser { } } -pub struct DeclarationContext<'ctx> { - qualifiers: Vec<(TypeQualifier, Span)>, +pub struct DeclarationContext<'ctx, 'qualifiers> { + qualifiers: TypeQualifiers<'qualifiers>, external: bool, ctx: &'ctx mut Context, body: &'ctx mut Block, } -impl<'ctx> DeclarationContext<'ctx> { +impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> { fn add_var( &mut self, parser: &mut Parser, @@ -384,7 +384,7 @@ impl<'ctx> DeclarationContext<'ctx> { meta: Span, ) -> Result> { let decl = VarDeclaration { - qualifiers: &self.qualifiers, + qualifiers: &mut self.qualifiers, ty, name: Some(name), init, diff --git a/src/front/glsl/parser/declarations.rs b/src/front/glsl/parser/declarations.rs index e53d3b10d3..8171a11b74 100644 --- a/src/front/glsl/parser/declarations.rs +++ b/src/front/glsl/parser/declarations.rs @@ -1,8 +1,8 @@ use crate::{ front::glsl::{ ast::{ - GlobalLookup, GlobalLookupKind, Precision, StorageQualifier, StructLayout, - TypeQualifier, + GlobalLookup, GlobalLookupKind, Precision, QualifierKey, QualifierValue, + StorageQualifier, StructLayout, TypeQualifiers, }, context::{Context, ExprPos}, error::ExpectedToken, @@ -249,7 +249,7 @@ impl<'source> ParsingContext<'source> { // type_qualifier IDENTIFIER identifier_list SEMICOLON if self.peek_type_qualifier(parser) || self.peek_type_name(parser) { - let qualifiers = self.parse_type_qualifiers(parser)?; + let mut qualifiers = self.parse_type_qualifiers(parser)?; if self.peek_type_name(parser) { // This branch handles variables and function prototypes and if @@ -361,7 +361,7 @@ impl<'source> ParsingContext<'source> { parser, ctx, body, - &qualifiers, + &mut qualifiers, ty_name, token.meta, ) @@ -377,33 +377,28 @@ impl<'source> ParsingContext<'source> { } } TokenValue::Semicolon => { - let mut meta_all = token.meta; - for &(ref qualifier, meta) in qualifiers.iter() { - meta_all.subsume(meta); - match *qualifier { - TypeQualifier::WorkGroupSize(i, value) => { - parser.meta.workgroup_size[i] = value - } - TypeQualifier::EarlyFragmentTests => { - parser.meta.early_fragment_tests = true; - } - TypeQualifier::StorageQualifier(_) => { - // TODO: Maybe add some checks here - // This is needed because of cases like - // layout(early_fragment_tests) in; - } - _ => { - parser.errors.push(Error { - kind: ErrorKind::SemanticError( - "Qualifier not supported as standalone".into(), - ), - meta, - }); - } - } + if let Some(value) = + qualifiers.uint_layout_qualifier("local_size_x", &mut parser.errors) + { + parser.meta.workgroup_size[0] = value; + } + if let Some(value) = + qualifiers.uint_layout_qualifier("local_size_y", &mut parser.errors) + { + parser.meta.workgroup_size[1] = value; + } + if let Some(value) = + qualifiers.uint_layout_qualifier("local_size_z", &mut parser.errors) + { + parser.meta.workgroup_size[2] = value; } - Ok(Some(meta_all)) + parser.meta.early_fragment_tests |= qualifiers + .none_layout_qualifier("early_fragment_tests", &mut parser.errors); + + qualifiers.unused_errors(&mut parser.errors); + + Ok(Some(qualifiers.span)) } _ => Err(Error { kind: ErrorKind::InvalidToken( @@ -471,27 +466,22 @@ impl<'source> ParsingContext<'source> { parser: &mut Parser, ctx: &mut Context, body: &mut Block, - qualifiers: &[(TypeQualifier, Span)], + qualifiers: &mut TypeQualifiers, ty_name: String, meta: Span, ) -> Result { - let mut storage = None; - let mut layout = None; - - for &(ref qualifier, _) in qualifiers { - match *qualifier { - TypeQualifier::StorageQualifier(StorageQualifier::AddressSpace(c)) => { - storage = Some(c) + let layout = match qualifiers.layout_qualifiers.remove(&QualifierKey::Layout) { + Some((QualifierValue::Layout(l), _)) => l, + None => { + if let StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) = + qualifiers.storage.0 + { + StructLayout::Std430 + } else { + StructLayout::Std140 } - TypeQualifier::Layout(l) => layout = Some(l), - _ => continue, } - } - - let layout = match (layout, storage) { - (Some(layout), _) => layout, - (None, Some(AddressSpace::Storage { .. })) => StructLayout::Std430, - _ => StructLayout::Std140, + _ => unreachable!(), }; let mut members = Vec::new(); diff --git a/src/front/glsl/parser/functions.rs b/src/front/glsl/parser/functions.rs index f684ada6c7..35ecfa4833 100644 --- a/src/front/glsl/parser/functions.rs +++ b/src/front/glsl/parser/functions.rs @@ -396,7 +396,7 @@ impl<'source> ParsingContext<'source> { if self.bump_if(parser, TokenValue::Semicolon).is_none() { let (expr, expr_meta) = if self.peek_type_name(parser) || self.peek_type_qualifier(parser) { - let qualifiers = self.parse_type_qualifiers(parser)?; + let mut qualifiers = self.parse_type_qualifiers(parser)?; let (ty, mut meta) = self.parse_type_non_void(parser)?; let name = self.expect_ident(parser)?.0; @@ -407,7 +407,7 @@ impl<'source> ParsingContext<'source> { meta.subsume(end_meta); let decl = VarDeclaration { - qualifiers: &qualifiers, + qualifiers: &mut qualifiers, ty, name: Some(name), init: None, diff --git a/src/front/glsl/parser/types.rs b/src/front/glsl/parser/types.rs index 13cec58605..3fb1f355c4 100644 --- a/src/front/glsl/parser/types.rs +++ b/src/front/glsl/parser/types.rs @@ -1,6 +1,6 @@ use crate::{ front::glsl::{ - ast::{StorageQualifier, StructLayout, TypeQualifier}, + ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers}, error::ExpectedToken, parser::ParsingContext, token::{Token, TokenValue}, @@ -128,11 +128,8 @@ impl<'source> ParsingContext<'source> { }) } - pub fn parse_type_qualifiers( - &mut self, - parser: &mut Parser, - ) -> Result> { - let mut qualifiers = Vec::new(); + pub fn parse_type_qualifiers<'a>(&mut self, parser: &mut Parser) -> Result> { + let mut qualifiers = TypeQualifiers::default(); while self.peek_type_qualifier(parser) { let token = self.bump(parser)?; @@ -143,31 +140,104 @@ impl<'source> ParsingContext<'source> { continue; } - qualifiers.push(( - match token.value { - TokenValue::Interpolation(i) => TypeQualifier::Interpolation(i), - TokenValue::Const => TypeQualifier::StorageQualifier(StorageQualifier::Const), - TokenValue::In => TypeQualifier::StorageQualifier(StorageQualifier::Input), - TokenValue::Out => TypeQualifier::StorageQualifier(StorageQualifier::Output), - TokenValue::Uniform => TypeQualifier::StorageQualifier( - StorageQualifier::AddressSpace(AddressSpace::Uniform), - ), - TokenValue::Shared => TypeQualifier::StorageQualifier( - StorageQualifier::AddressSpace(AddressSpace::WorkGroup), - ), - TokenValue::Buffer => TypeQualifier::StorageQualifier( - StorageQualifier::AddressSpace(AddressSpace::Storage { - access: crate::StorageAccess::default(), - }), - ), - TokenValue::Sampling(s) => TypeQualifier::Sampling(s), - TokenValue::PrecisionQualifier(p) => TypeQualifier::Precision(p), - TokenValue::StorageAccess(access) => TypeQualifier::StorageAccess(access), - TokenValue::Restrict => continue, - _ => unreachable!(), - }, - token.meta, - )) + qualifiers.span.subsume(token.meta); + + match token.value { + TokenValue::Interpolation(i) => { + if qualifiers.interpolation.is_some() { + parser.errors.push(Error { + kind: ErrorKind::SemanticError( + "Cannot use more than one interpolation qualifier per declaration" + .into(), + ), + meta: token.meta, + }) + } + + qualifiers.interpolation = Some((i, token.meta)); + } + TokenValue::Const + | TokenValue::In + | TokenValue::Out + | TokenValue::Uniform + | TokenValue::Shared + | TokenValue::Buffer => { + let storage = match token.value { + TokenValue::Const => StorageQualifier::Const, + TokenValue::In => StorageQualifier::Input, + TokenValue::Out => StorageQualifier::Output, + TokenValue::Uniform => { + StorageQualifier::AddressSpace(AddressSpace::Uniform) + } + TokenValue::Shared => { + StorageQualifier::AddressSpace(AddressSpace::WorkGroup) + } + TokenValue::Buffer => { + StorageQualifier::AddressSpace(AddressSpace::Storage { + access: crate::StorageAccess::all(), + }) + } + _ => unreachable!(), + }; + + if StorageQualifier::AddressSpace(AddressSpace::Function) + != qualifiers.storage.0 + { + parser.errors.push(Error { + kind: ErrorKind::SemanticError( + "Cannot use more than one storage qualifier per declaration".into(), + ), + meta: token.meta, + }); + } + + qualifiers.storage = (storage, token.meta); + } + TokenValue::Sampling(s) => { + if qualifiers.sampling.is_some() { + parser.errors.push(Error { + kind: ErrorKind::SemanticError( + "Cannot use more than one sampling qualifier per declaration" + .into(), + ), + meta: token.meta, + }) + } + + qualifiers.sampling = Some((s, token.meta)); + } + TokenValue::PrecisionQualifier(p) => { + if qualifiers.interpolation.is_some() { + parser.errors.push(Error { + kind: ErrorKind::SemanticError( + "Cannot use more than one precision qualifier per declaration" + .into(), + ), + meta: token.meta, + }) + } + + qualifiers.precision = Some((p, token.meta)); + } + TokenValue::StorageAccess(access) => { + let storage_access = qualifiers + .storage_acess + .get_or_insert((crate::StorageAccess::empty(), Span::default())); + if storage_access.0.contains(access) { + parser.errors.push(Error { + kind: ErrorKind::SemanticError( + "The same memory qualifier can only be used once".into(), + ), + meta: token.meta, + }) + } + + storage_access.0 |= access; + storage_access.1.subsume(token.meta); + } + TokenValue::Restrict => continue, + _ => unreachable!(), + }; } Ok(qualifiers) @@ -176,11 +246,11 @@ impl<'source> ParsingContext<'source> { pub fn parse_layout_qualifier_id_list( &mut self, parser: &mut Parser, - qualifiers: &mut Vec<(TypeQualifier, Span)>, + qualifiers: &mut TypeQualifiers, ) -> Result<()> { self.expect(parser, TokenValue::LeftParen)?; loop { - self.parse_layout_qualifier_id(parser, qualifiers)?; + self.parse_layout_qualifier_id(parser, &mut qualifiers.layout_qualifiers)?; if self.bump_if(parser, TokenValue::Comma).is_some() { continue; @@ -188,7 +258,8 @@ impl<'source> ParsingContext<'source> { break; } - self.expect(parser, TokenValue::RightParen)?; + let token = self.expect(parser, TokenValue::RightParen)?; + qualifiers.span.subsume(token.meta); Ok(()) } @@ -196,7 +267,7 @@ impl<'source> ParsingContext<'source> { pub fn parse_layout_qualifier_id( &mut self, parser: &mut Parser, - qualifiers: &mut Vec<(TypeQualifier, Span)>, + qualifiers: &mut crate::FastHashMap, ) -> Result<()> { // layout_qualifier_id: // IDENTIFIER @@ -205,60 +276,38 @@ impl<'source> ParsingContext<'source> { let mut token = self.bump(parser)?; match token.value { TokenValue::Identifier(name) => { - if self.bump_if(parser, TokenValue::Assign).is_some() { - let (value, end_meta) = self.parse_uint_constant(parser)?; - token.meta.subsume(end_meta); + let (key, value) = match name.as_str() { + "std140" => ( + QualifierKey::Layout, + QualifierValue::Layout(StructLayout::Std140), + ), + "std430" => ( + QualifierKey::Layout, + QualifierValue::Layout(StructLayout::Std430), + ), + _ => { + let key = QualifierKey::String(name.into()); + let value = if self.bump_if(parser, TokenValue::Assign).is_some() { + let (value, end_meta) = match self.parse_uint_constant(parser) { + Ok(v) => v, + Err(e) => { + parser.errors.push(e); + (0, Span::default()) + } + }; + token.meta.subsume(end_meta); - qualifiers.push(( - match name.as_str() { - "location" => TypeQualifier::Location(value), - "set" => TypeQualifier::Set(value), - "binding" => TypeQualifier::Binding(value), - "local_size_x" => TypeQualifier::WorkGroupSize(0, value), - "local_size_y" => TypeQualifier::WorkGroupSize(1, value), - "local_size_z" => TypeQualifier::WorkGroupSize(2, value), - _ => { - parser.errors.push(Error { - kind: ErrorKind::UnknownLayoutQualifier(name), - meta: token.meta, - }); - return Ok(()); - } - }, - token.meta, - )) - } else { - qualifiers.push(( - match name.as_str() { - "push_constant" => { - qualifiers.push(( - TypeQualifier::Layout(StructLayout::Std430), - token.meta, - )); - qualifiers.push(( - TypeQualifier::StorageQualifier( - StorageQualifier::AddressSpace(AddressSpace::PushConstant), - ), - token.meta, - )); - return Ok(()); - } - "std140" => TypeQualifier::Layout(StructLayout::Std140), - "std430" => TypeQualifier::Layout(StructLayout::Std430), - "early_fragment_tests" => TypeQualifier::EarlyFragmentTests, - _ => { - parser.errors.push(Error { - kind: ErrorKind::UnknownLayoutQualifier(name), - meta: token.meta, - }); - return Ok(()); - } - }, - token.meta, - )); + QualifierValue::Uint(value) + } else { + QualifierValue::None + }; + + (key, value) + } }; + + qualifiers.insert(key, (value, token.meta)); } - // TODO: handle Shared? _ => parser.errors.push(Error { kind: ErrorKind::InvalidToken(token.value, vec![ExpectedToken::Identifier]), meta: token.meta, diff --git a/src/front/glsl/variables.rs b/src/front/glsl/variables.rs index 0cb11bcef6..072b9f4740 100644 --- a/src/front/glsl/variables.rs +++ b/src/front/glsl/variables.rs @@ -6,25 +6,12 @@ use super::{ }; use crate::{ AddressSpace, Binding, Block, BuiltIn, Constant, Expression, GlobalVariable, Handle, - Interpolation, LocalVariable, ResourceBinding, ScalarKind, ShaderStage, StorageAccess, - SwizzleComponent, Type, TypeInner, VectorSize, + Interpolation, LocalVariable, ResourceBinding, ScalarKind, ShaderStage, SwizzleComponent, Type, + TypeInner, VectorSize, }; -macro_rules! qualifier_arm { - ($src:expr, $tgt:expr, $meta:expr, $msg:literal, $errors:expr $(,)?) => {{ - if $tgt.is_some() { - $errors.push(Error { - kind: ErrorKind::SemanticError($msg.into()), - meta: $meta, - }) - } - - $tgt = Some($src); - }}; -} - -pub struct VarDeclaration<'a> { - pub qualifiers: &'a [(TypeQualifier, Span)], +pub struct VarDeclaration<'a, 'key> { + pub qualifiers: &'a mut TypeQualifiers<'key>, pub ty: Handle, pub name: Option, pub init: Option>, @@ -412,242 +399,141 @@ impl Parser { meta, }: VarDeclaration, ) -> Result { - let mut storage = StorageQualifier::AddressSpace(AddressSpace::Private); - let mut interpolation = None; - let mut set = None; - let mut binding = None; - let mut location = None; - let mut sampling = None; - let mut layout = None; - let mut precision = None; - let mut access = StorageAccess::all(); - - for &(ref qualifier, meta) in qualifiers { - match *qualifier { - TypeQualifier::StorageQualifier(s) => { - if StorageQualifier::AddressSpace(AddressSpace::PushConstant) == storage - && s == StorageQualifier::AddressSpace(AddressSpace::Uniform) - { - // Ignore the Uniform qualifier if the space was already set to PushConstant - continue; - } else if StorageQualifier::AddressSpace(AddressSpace::Private) != storage { - self.errors.push(Error { - kind: ErrorKind::SemanticError( - "Cannot use more than one storage qualifier per declaration".into(), - ), - meta, - }); - } - - storage = s; - } - TypeQualifier::Interpolation(i) => qualifier_arm!( - i, - interpolation, - meta, - "Cannot use more than one interpolation qualifier per declaration", - self.errors - ), - TypeQualifier::Binding(r) => qualifier_arm!( - r, - binding, - meta, - "Cannot use more than one binding per declaration", - self.errors - ), - TypeQualifier::Set(s) => qualifier_arm!( - s, - set, - meta, - "Cannot use more than one binding per declaration", - self.errors - ), - TypeQualifier::Location(l) => qualifier_arm!( - l, - location, - meta, - "Cannot use more than one binding per declaration", - self.errors - ), - TypeQualifier::Sampling(s) => qualifier_arm!( - s, - sampling, - meta, - "Cannot use more than one sampling qualifier per declaration", - self.errors - ), - TypeQualifier::Layout(ref l) => qualifier_arm!( - l, - layout, - meta, - "Cannot use more than one layout qualifier per declaration", - self.errors - ), - TypeQualifier::Precision(ref p) => qualifier_arm!( - p, - precision, - meta, - "Cannot use more than one precision qualifier per declaration", - self.errors - ), - TypeQualifier::StorageAccess(a) => access &= a, - _ => { - self.errors.push(Error { - kind: ErrorKind::SemanticError("Qualifier not supported in globals".into()), - meta, - }); - } - } - } - - match storage { - StorageQualifier::AddressSpace(AddressSpace::PushConstant) => { - if set.is_some() { - self.errors.push(Error { - kind: ErrorKind::SemanticError( - "set cannot be used to decorate push constant".into(), - ), - meta, + let storage = qualifiers.storage.0; + let (ret, lookup) = match storage { + StorageQualifier::Input | StorageQualifier::Output => { + let input = storage == StorageQualifier::Input; + // TODO: glslang seems to use a counter for variables without + // explicit location (even if that causes collisions) + let location = qualifiers + .uint_layout_qualifier("location", &mut self.errors) + .unwrap_or(0); + let interpolation = qualifiers.interpolation.take().map(|(i, _)| i).or_else(|| { + let kind = self.module.types[ty].inner.scalar_kind()?; + Some(match kind { + ScalarKind::Float => Interpolation::Perspective, + _ => Interpolation::Flat, }) - } - } - StorageQualifier::AddressSpace(AddressSpace::Uniform) - | StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) => { - if binding.is_none() { - self.errors.push(Error { - kind: ErrorKind::SemanticError( - "uniform/buffer blocks require layout(binding=X)".into(), - ), - meta, - }) - } - } - _ => { - if set.is_some() || binding.is_some() { - self.errors.push(Error { - kind: ErrorKind::SemanticError( - "set/binding can only be applied to uniform/buffer blocks".into(), - ), - meta, - }) - } - } - } + }); + let sampling = qualifiers.sampling.take().map(|(s, _)| s); - if (sampling.is_some() || interpolation.is_some()) && location.is_none() { - return Err(Error { - kind: ErrorKind::SemanticError( - "Sampling and interpolation qualifiers can only be used in in/out variables" - .into(), - ), - meta, - }); - } + let handle = self.module.global_variables.append( + GlobalVariable { + name: name.clone(), + space: AddressSpace::Private, + binding: None, + ty, + init, + }, + meta, + ); - if let Some(location) = location { - let input = storage == StorageQualifier::Input; - let interpolation = interpolation.or_else(|| { - let kind = self.module.types[ty].inner.scalar_kind()?; - Some(match kind { - ScalarKind::Float => Interpolation::Perspective, - _ => Interpolation::Flat, - }) - }); - - let handle = self.module.global_variables.append( - GlobalVariable { + let idx = self.entry_args.len(); + self.entry_args.push(EntryArg { name: name.clone(), - space: AddressSpace::Private, - binding: None, - ty, - init, - }, - meta, - ); + binding: Binding::Location { + location, + interpolation, + sampling, + }, + handle, + storage, + }); - let idx = self.entry_args.len(); - self.entry_args.push(EntryArg { - name: name.clone(), - binding: Binding::Location { - location, - interpolation, - sampling, - }, - handle, - storage, - }); - - if let Some(name) = name { let lookup = GlobalLookup { kind: GlobalLookupKind::Variable(handle), entry_arg: Some(idx), mutable: !input, }; - ctx.add_global(self, &name, lookup, body); - self.global_variables.push((name, lookup)); + (GlobalOrConstant::Global(handle), lookup) } + StorageQualifier::Const => { + let init = init.ok_or_else(|| Error { + kind: ErrorKind::SemanticError("const values must have an initializer".into()), + meta, + })?; - return Ok(GlobalOrConstant::Global(handle)); - } else if let StorageQualifier::Const = storage { - let init = init.ok_or_else(|| Error { - kind: ErrorKind::SemanticError("const values must have an initializer".into()), - meta, - })?; - if let Some(name) = name { let lookup = GlobalLookup { kind: GlobalLookupKind::Constant(init, ty), entry_arg: None, mutable: false, }; - ctx.add_global(self, &name, lookup, body); - self.global_variables.push((name, lookup)); + (GlobalOrConstant::Constant(init), lookup) } - return Ok(GlobalOrConstant::Constant(init)); - } - - let space = match self.module.types[ty].inner { - TypeInner::Image { .. } => AddressSpace::Handle, - TypeInner::Sampler { .. } => AddressSpace::Handle, - _ => { - if let StorageQualifier::AddressSpace(AddressSpace::Storage { .. }) = storage { - AddressSpace::Storage { access } - } else { - match storage { - StorageQualifier::AddressSpace(space) => space, - _ => AddressSpace::Private, + StorageQualifier::AddressSpace(mut space) => { + match space { + AddressSpace::Storage { ref mut access } => { + if let Some((restricted_access, _)) = qualifiers.storage_acess.take() { + access.remove(restricted_access); + } } - } + AddressSpace::Uniform => { + if let TypeInner::Image { .. } | TypeInner::Sampler { .. } = + self.module.types[ty].inner + { + space = AddressSpace::Handle + } else if qualifiers + .none_layout_qualifier("push_constant", &mut self.errors) + { + space = AddressSpace::PushConstant + } + } + AddressSpace::Function => space = AddressSpace::Private, + _ => {} + }; + + let binding = match space { + AddressSpace::Uniform | AddressSpace::Storage { .. } | AddressSpace::Handle => { + let binding = qualifiers.uint_layout_qualifier("binding", &mut self.errors); + if binding.is_none() { + self.errors.push(Error { + kind: ErrorKind::SemanticError( + "uniform/buffer blocks require layout(binding=X)".into(), + ), + meta, + }); + } + let set = qualifiers.uint_layout_qualifier("set", &mut self.errors); + binding.map(|binding| ResourceBinding { + group: set.unwrap_or(0), + binding, + }) + } + _ => None, + }; + + let handle = self.module.global_variables.append( + GlobalVariable { + name: name.clone(), + space, + binding, + ty, + init, + }, + meta, + ); + + let lookup = GlobalLookup { + kind: GlobalLookupKind::Variable(handle), + entry_arg: None, + mutable: true, + }; + + (GlobalOrConstant::Global(handle), lookup) } }; - let handle = self.module.global_variables.append( - GlobalVariable { - name: name.clone(), - space, - binding: binding.map(|binding| ResourceBinding { - group: set.unwrap_or(0), - binding, - }), - ty, - init, - }, - meta, - ); - if let Some(name) = name { - let lookup = GlobalLookup { - kind: GlobalLookupKind::Variable(handle), - entry_arg: None, - mutable: true, - }; ctx.add_global(self, &name, lookup, body); self.global_variables.push((name, lookup)); } - Ok(GlobalOrConstant::Global(handle)) + qualifiers.unused_errors(&mut self.errors); + + Ok(ret) } pub(crate) fn add_local_var( @@ -666,37 +552,18 @@ impl Parser { } } - let mut mutable = true; - let mut precision = None; - - for &(ref qualifier, meta) in decl.qualifiers { - match *qualifier { - TypeQualifier::StorageQualifier(StorageQualifier::Const) => { - if !mutable { - self.errors.push(Error { - kind: ErrorKind::SemanticError( - "Cannot use more than one constant qualifier per declaration" - .into(), - ), - meta, - }) - } - - mutable = false; - } - TypeQualifier::Precision(ref p) => qualifier_arm!( - p, - precision, - meta, - "Cannot use more than one precision qualifier per declaration", - self.errors - ), - _ => self.errors.push(Error { - kind: ErrorKind::SemanticError("Qualifier not supported in locals".into()), - meta, - }), + let storage = decl.qualifiers.storage; + let mutable = match storage.0 { + StorageQualifier::AddressSpace(AddressSpace::Function) => true, + StorageQualifier::Const => false, + _ => { + self.errors.push(Error { + kind: ErrorKind::SemanticError("Locals cannot have a storage qualifier".into()), + meta: storage.1, + }); + true } - } + }; let handle = ctx.locals.append( LocalVariable { @@ -712,6 +579,8 @@ impl Parser { ctx.add_local_var(name, expr, mutable); } + decl.qualifiers.unused_errors(&mut self.errors); + Ok(expr) } } diff --git a/tests/out/wgsl/expressions-frag.wgsl b/tests/out/wgsl/expressions-frag.wgsl index 86589b1af1..0b3f7ad46e 100644 --- a/tests/out/wgsl/expressions-frag.wgsl +++ b/tests/out/wgsl/expressions-frag.wgsl @@ -2,6 +2,10 @@ struct BST { data: i32; }; +struct FragmentOutput { + @location(0) o_color: vec4; +}; + var global: f32; var o_color: vec4; @@ -212,7 +216,8 @@ fn main_1() { } @stage(fragment) -fn main() { +fn main() -> FragmentOutput { main_1(); - return; + let _e5 = o_color; + return FragmentOutput(_e5); } diff --git a/tests/out/wgsl/fma-frag.wgsl b/tests/out/wgsl/fma-frag.wgsl index 543311de9a..8bd6686745 100644 --- a/tests/out/wgsl/fma-frag.wgsl +++ b/tests/out/wgsl/fma-frag.wgsl @@ -4,6 +4,10 @@ struct Mat4x3_ { mz: vec4; }; +struct FragmentOutput { + @location(0) o_color: vec4; +}; + var o_color: vec4; fn Fma(d: ptr, m: Mat4x3_, s: f32) { @@ -38,7 +42,8 @@ fn main_1() { } @stage(fragment) -fn main() { +fn main() -> FragmentOutput { main_1(); - return; + let _e3 = o_color; + return FragmentOutput(_e3); }