Skip to content

Commit

Permalink
feat: implement requires directives
Browse files Browse the repository at this point in the history
  • Loading branch information
ErichDonGubler committed Oct 22, 2024
1 parent e28ac60 commit 499bddd
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 20 deletions.
28 changes: 28 additions & 0 deletions naga/src/front/wgsl/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use crate::front::wgsl::parse::directive::language_extension::{
LanguageExtension, UnimplementedLanguageExtension,
};
use crate::front::wgsl::parse::directive::{DirectiveKind, UnimplementedDirectiveKind};
use crate::front::wgsl::parse::lexer::Token;
use crate::front::wgsl::Scalar;
Expand Down Expand Up @@ -186,6 +189,7 @@ pub(crate) enum Error<'a> {
UnknownType(Span),
UnknownStorageFormat(Span),
UnknownConservativeDepth(Span),
UnknownLanguageExtension(Span, &'a str),
SizeAttributeTooLow(Span, u32),
AlignAttributeTooLow(Span, Alignment),
NonPowerOfTwoAlignAttribute(Span),
Expand Down Expand Up @@ -275,6 +279,10 @@ pub(crate) enum Error<'a> {
DirectiveAfterFirstGlobalDecl {
directive_span: Span,
},
LanguageExtensionNotYetImplemented {
kind: UnimplementedLanguageExtension,
span: Span,
},
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -525,6 +533,11 @@ impl<'a> Error<'a> {
labels: vec![(bad_span, "unknown type".into())],
notes: vec![],
},
Error::UnknownLanguageExtension(span, name) => ParseError {
message: format!("unknown language extension `{name}`"),
labels: vec![(span, "".into())],
notes: vec![],
},
Error::SizeAttributeTooLow(bad_span, min_size) => ParseError {
message: format!("struct member size must be at least {min_size}"),
labels: vec![(bad_span, format!("must be at least {min_size}").into())],
Expand Down Expand Up @@ -907,6 +920,21 @@ impl<'a> Error<'a> {
)
.into()],
},
Error::LanguageExtensionNotYetImplemented { kind, span } => ParseError {
message: format!(
"`{}` is not yet implemented",
LanguageExtension::Unimplemented(kind).to_ident()
),
labels: vec![(span, "".into())],
notes: vec![format!(
concat!(
"Let Naga maintainers know that you ran into this at ",
"<https://github.com/gfx-rs/wgpu/issues/{}>, ",
"so they can prioritize it!"
),
kind.tracking_issue_num()
)],
},
}
}
}
Expand Down
24 changes: 6 additions & 18 deletions naga/src/front/wgsl/parse/directive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
//!
//! See also <https://www.w3.org/TR/WGSL/#directives>.

pub(crate) mod language_extension;

/// A parsed sentinel word indicating the type of directive to be parsed next.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum DirectiveKind {
Requires,
Unimplemented(UnimplementedDirectiveKind),
}

Expand All @@ -18,18 +21,18 @@ impl DirectiveKind {
Some(match s {
Self::DIAGNOSTIC => Self::Unimplemented(UnimplementedDirectiveKind::Diagnostic),
Self::ENABLE => Self::Unimplemented(UnimplementedDirectiveKind::Enable),
Self::REQUIRES => Self::Unimplemented(UnimplementedDirectiveKind::Requires),
Self::REQUIRES => Self::Requires,
_ => return None,
})
}

/// Maps this [`DirectiveKind`] into the sentinel word associated with it in WGSL.
pub const fn to_ident(self) -> &'static str {
match self {
Self::Requires => Self::REQUIRES,
Self::Unimplemented(kind) => match kind {
UnimplementedDirectiveKind::Diagnostic => Self::DIAGNOSTIC,
UnimplementedDirectiveKind::Enable => Self::ENABLE,
UnimplementedDirectiveKind::Requires => Self::REQUIRES,
},
}
}
Expand All @@ -48,14 +51,12 @@ impl DirectiveKind {
pub enum UnimplementedDirectiveKind {
Diagnostic,
Enable,
Requires,
}

impl UnimplementedDirectiveKind {
pub const fn tracking_issue_num(self) -> u16 {
match self {
Self::Diagnostic => 5320,
Self::Requires => 6350,
Self::Enable => 5476,
}
}
Expand Down Expand Up @@ -99,19 +100,6 @@ error: `enable` is not yet implemented
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5476>, so they can prioritize it!
";
}
UnimplementedDirectiveKind::Requires => {
shader = "requires readonly_and_readwrite_storage_textures";
expected_msg = "\
error: `requires` is not yet implemented
┌─ wgsl:1:1
1 │ requires readonly_and_readwrite_storage_textures
│ ^^^^^^^^ this global directive is standard, but not yet implemented
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/6350>, so they can prioritize it!
";
}
};
Expand Down Expand Up @@ -152,7 +140,7 @@ error: expected global declaration, but found a global directive
";
}
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Requires) => {
DirectiveKind::Requires => {
directive = "requires readonly_and_readwrite_storage_textures";
expected_msg = "\
error: expected global declaration, but found a global directive
Expand Down
192 changes: 192 additions & 0 deletions naga/src/front/wgsl/parse/directive/language_extension.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#[cfg(test)]
use strum::IntoEnumIterator;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(crate) enum LanguageExtension {
#[allow(unused)]
Implemented(ImplementedLanguageExtension),
Unimplemented(UnimplementedLanguageExtension),
}

impl LanguageExtension {
#[cfg(test)]
const NAGA_REPLACE_ME_WITH_A_REAL_EXTENSION_PLZ: &'static str =
"naga_replace_me_with_a_real_extension_plz";
const READONLY_AND_READWRITE_STORAGE_TEXTURES: &'static str =
"readonly_and_readwrite_storage_textures";
const PACKED4X8_INTEGER_DOT_PRODUCT: &'static str = "packed_4x8_integer_dot_product";
const UNRESTRICTED_POINTER_PARAMETERS: &'static str = "unrestricted_pointer_parameters";
const POINTER_COMPOSITE_ACCESS: &'static str = "pointer_composite_access";

pub fn from_ident(s: &str) -> Option<Self> {
Some(match s {
#[cfg(test)]
Self::NAGA_REPLACE_ME_WITH_A_REAL_EXTENSION_PLZ => {
Self::Implemented(ImplementedLanguageExtension::NagaReplaceMeWithARealExtensionPlz)
}
Self::READONLY_AND_READWRITE_STORAGE_TEXTURES => Self::Unimplemented(
UnimplementedLanguageExtension::ReadOnlyAndReadWriteStorageTextures,
),
Self::PACKED4X8_INTEGER_DOT_PRODUCT => {
Self::Unimplemented(UnimplementedLanguageExtension::Packed4x8IntegerDotProduct)
}
Self::UNRESTRICTED_POINTER_PARAMETERS => {
Self::Unimplemented(UnimplementedLanguageExtension::UnrestrictedPointerParameters)
}
Self::POINTER_COMPOSITE_ACCESS => {
Self::Unimplemented(UnimplementedLanguageExtension::PointerCompositeAccess)
}
_ => return None,
})
}

pub fn to_ident(self) -> &'static str {
match self {
Self::Implemented(kind) => match kind {
#[cfg(test)]
ImplementedLanguageExtension::NagaReplaceMeWithARealExtensionPlz => {
Self::NAGA_REPLACE_ME_WITH_A_REAL_EXTENSION_PLZ
}
},
Self::Unimplemented(kind) => match kind {
UnimplementedLanguageExtension::ReadOnlyAndReadWriteStorageTextures => {
Self::READONLY_AND_READWRITE_STORAGE_TEXTURES
}
UnimplementedLanguageExtension::Packed4x8IntegerDotProduct => {
Self::PACKED4X8_INTEGER_DOT_PRODUCT
}
UnimplementedLanguageExtension::UnrestrictedPointerParameters => {
Self::UNRESTRICTED_POINTER_PARAMETERS
}
UnimplementedLanguageExtension::PointerCompositeAccess => {
Self::POINTER_COMPOSITE_ACCESS
}
},
}
}

#[cfg(test)]
fn iter() -> impl Iterator<Item = Self> {
let implemented = ImplementedLanguageExtension::iter().map(Self::Implemented);
let unimplemented = UnimplementedLanguageExtension::iter().map(Self::Unimplemented);
implemented.chain(unimplemented)
}
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(test, derive(strum::EnumIter))]
pub(crate) enum ImplementedLanguageExtension {
#[cfg(test)]
NagaReplaceMeWithARealExtensionPlz,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(test, derive(strum::EnumIter))]
pub(crate) enum UnimplementedLanguageExtension {
ReadOnlyAndReadWriteStorageTextures,
Packed4x8IntegerDotProduct,
UnrestrictedPointerParameters,
PointerCompositeAccess,
}

impl UnimplementedLanguageExtension {
pub(crate) fn tracking_issue_num(self) -> u16 {
match self {
Self::ReadOnlyAndReadWriteStorageTextures => todo!(),
Self::Packed4x8IntegerDotProduct => todo!(),
Self::UnrestrictedPointerParameters => todo!(),
Self::PointerCompositeAccess => todo!(),
}
}
}

#[cfg(test)]
mod test {
use itertools::Itertools;
use strum::IntoEnumIterator;

use crate::front::wgsl::assert_parse_err;

use super::{ImplementedLanguageExtension, LanguageExtension};

#[test]
fn implemented() {
#[derive(Clone, Debug, strum::EnumIter)]
enum Count {
ByItself,
WithOther,
}

#[derive(Clone, Debug, strum::EnumIter)]
enum Separation {
SameLineNoSpace,
SameLine,
MultiLine,
}

#[derive(Clone, Debug, strum::EnumIter)]
enum TrailingComma {
Yes,
No,
}

#[track_caller]
fn test_requires(before: &str, idents: &[&str], ident_sep: &str, after: &str) {
let ident_list = idents.join(ident_sep);
let shader = format!("requires{before}{ident_list}{after};");
let expected_msg = "".to_string();
assert_parse_err(&shader, &expected_msg);
}

let implemented_extensions =
ImplementedLanguageExtension::iter().map(LanguageExtension::Implemented);

let iter = implemented_extensions
.clone()
.cartesian_product(Count::iter())
.cartesian_product(Separation::iter())
.cartesian_product(TrailingComma::iter());
for (((extension, count), separation), trailing_comma) in iter {
let before;
let ident_sep;
match separation {
Separation::SameLine => {
before = " ";
ident_sep = ", ";
}
Separation::SameLineNoSpace => {
before = " ";
ident_sep = ",";
}
Separation::MultiLine => {
before = "\n ";
ident_sep = ",\n ";
}
}
let after = match trailing_comma {
TrailingComma::Yes => ident_sep,
TrailingComma::No => before,
};
match count {
Count::ByItself => test_requires(before, &[extension.to_ident()], ident_sep, after),
Count::WithOther => {
for other_extension in implemented_extensions.clone() {
for list in [[extension, other_extension], [other_extension, extension]] {
let list = list.map(|e| e.to_ident());
test_requires(before, &list, ident_sep, after);
}
}
}
}
}
}

#[test]
fn unimplemented() {}

#[test]
fn unknown() {}

#[test]
fn malformed() {}
}
20 changes: 18 additions & 2 deletions naga/src/front/wgsl/parse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::front::wgsl::error::{Error, ExpectedToken};
use crate::front::wgsl::parse::directive::language_extension::LanguageExtension;
use crate::front::wgsl::parse::directive::DirectiveKind;
use crate::front::wgsl::parse::lexer::{Lexer, Token};
use crate::front::wgsl::parse::number::Number;
Expand Down Expand Up @@ -2258,7 +2259,6 @@ impl Parser {
Ok(fun)
}

#[allow(unused)]
fn directive_ident_list<'a>(
&self,
lexer: &mut Lexer<'a>,
Expand Down Expand Up @@ -2512,12 +2512,28 @@ impl Parser {
let mut tu = ast::TranslationUnit::default();

// Parse directives.
#[allow(clippy::never_loop, unreachable_code)]
while let Ok((ident, span)) = lexer.peek_ident_with_span() {
if let Some(kind) = DirectiveKind::from_ident(ident) {
self.push_rule_span(Rule::Directive, &mut lexer);
let _ = lexer.next_ident_with_span().unwrap();
match kind {
DirectiveKind::Requires => {
self.directive_ident_list(&mut lexer, |ident, span| {
match LanguageExtension::from_ident(ident) {
Some(LanguageExtension::Implemented(_kind)) => {
// NOTE: No further validation is needed for an extension, so
// just throw parsed information away. If we ever want to apply
// what we've parsed to diagnostics, maybe we'll want to refer
// to enabled extensions later?
Ok(())
}
Some(LanguageExtension::Unimplemented(kind)) => {
Err(Error::LanguageExtensionNotYetImplemented { kind, span })
}
None => Err(Error::UnknownLanguageExtension(span, ident)),
}
})?;
}
DirectiveKind::Unimplemented(kind) => {
return Err(Error::DirectiveNotYetImplemented { kind, span })
}
Expand Down

0 comments on commit 499bddd

Please sign in to comment.