mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-21 14:23:32 +00:00
feat(wgsl-in): create skeleton for parsing directives
This commit is contained in:
parent
cb31465811
commit
b3f665be7d
@ -85,6 +85,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
|
||||
- Support local `const` declarations in WGSL. By @sagudev in [#6156](https://github.com/gfx-rs/wgpu/pull/6156).
|
||||
- Implemented `const_assert` in WGSL. By @sagudev in [#6198](https://github.com/gfx-rs/wgpu/pull/6198).
|
||||
- Support polyfilling `inverse` in WGSL. By @chyyran in [#6385](https://github.com/gfx-rs/wgpu/pull/6385).
|
||||
- Add an internal skeleton for parsing `requires`, `enable`, and `diagnostic` directives that don't yet do anything besides emit nicer errors. By @ErichDonGubler in [#6352](https://github.com/gfx-rs/wgpu/pull/6352).
|
||||
|
||||
#### General
|
||||
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1897,6 +1897,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"spirv 0.3.0+sdk-1.3.268.0",
|
||||
"strum",
|
||||
"termcolor",
|
||||
"thiserror",
|
||||
"unicode-xid",
|
||||
|
@ -101,3 +101,4 @@ ron = "0.8.0"
|
||||
rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
spirv = { version = "0.3", features = ["deserialize"] }
|
||||
strum.workspace = true
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::front::wgsl::parse::directive::{DirectiveKind, UnimplementedDirectiveKind};
|
||||
use crate::front::wgsl::parse::lexer::Token;
|
||||
use crate::front::wgsl::Scalar;
|
||||
use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
|
||||
@ -265,6 +266,13 @@ pub(crate) enum Error<'a> {
|
||||
PipelineConstantIDValue(Span),
|
||||
NotBool(Span),
|
||||
ConstAssertFailed(Span),
|
||||
DirectiveNotYetImplemented {
|
||||
kind: UnimplementedDirectiveKind,
|
||||
span: Span,
|
||||
},
|
||||
DirectiveAfterFirstGlobalDecl {
|
||||
directive_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -861,6 +869,36 @@ impl<'a> Error<'a> {
|
||||
labels: vec![(span, "evaluates to false".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::DirectiveNotYetImplemented { kind, span } => ParseError {
|
||||
message: format!(
|
||||
"`{}` is not yet implemented",
|
||||
DirectiveKind::Unimplemented(kind).to_ident()
|
||||
),
|
||||
labels: vec![(
|
||||
span,
|
||||
"this global directive is standard, but not yet implemented".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()
|
||||
)],
|
||||
},
|
||||
Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
|
||||
message: "expected global declaration, but found a global directive".into(),
|
||||
labels: vec![(
|
||||
directive_span,
|
||||
"written after first global declaration".into(),
|
||||
)],
|
||||
notes: vec![concat!(
|
||||
"global directives are only allowed before global declarations; ",
|
||||
"maybe hoist this closer to the top of the shader module?"
|
||||
)
|
||||
.into()],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,3 +58,21 @@ impl Frontend {
|
||||
pub fn parse_str(source: &str) -> Result<crate::Module, ParseError> {
|
||||
Frontend::new().parse(source)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[track_caller]
|
||||
pub fn assert_parse_err(input: &str, snapshot: &str) {
|
||||
let output = parse_str(input)
|
||||
.expect_err("expected parser error")
|
||||
.emit_to_string(input);
|
||||
if output != snapshot {
|
||||
for diff in diff::lines(snapshot, &output) {
|
||||
match diff {
|
||||
diff::Result::Left(l) => println!("-{l}"),
|
||||
diff::Result::Both(l, _) => println!(" {l}"),
|
||||
diff::Result::Right(r) => println!("+{r}"),
|
||||
}
|
||||
}
|
||||
panic!("Error snapshot failed");
|
||||
}
|
||||
}
|
||||
|
179
naga/src/front/wgsl/parse/directive.rs
Normal file
179
naga/src/front/wgsl/parse/directive.rs
Normal file
@ -0,0 +1,179 @@
|
||||
//! WGSL directives. The focal point of this API is [`DirectiveKind`].
|
||||
//!
|
||||
//! See also <https://www.w3.org/TR/WGSL/#directives>.
|
||||
|
||||
/// A parsed sentinel word indicating the type of directive to be parsed next.
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum DirectiveKind {
|
||||
Unimplemented(UnimplementedDirectiveKind),
|
||||
}
|
||||
|
||||
impl DirectiveKind {
|
||||
const DIAGNOSTIC: &'static str = "diagnostic";
|
||||
const ENABLE: &'static str = "enable";
|
||||
const REQUIRES: &'static str = "requires";
|
||||
|
||||
/// Convert from a sentinel word in WGSL into its associated [`DirectiveKind`], if possible.
|
||||
pub fn from_ident(s: &str) -> Option<Self> {
|
||||
Some(match s {
|
||||
Self::DIAGNOSTIC => Self::Unimplemented(UnimplementedDirectiveKind::Diagnostic),
|
||||
Self::ENABLE => Self::Unimplemented(UnimplementedDirectiveKind::Enable),
|
||||
Self::REQUIRES => Self::Unimplemented(UnimplementedDirectiveKind::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::Unimplemented(kind) => match kind {
|
||||
UnimplementedDirectiveKind::Diagnostic => Self::DIAGNOSTIC,
|
||||
UnimplementedDirectiveKind::Enable => Self::ENABLE,
|
||||
UnimplementedDirectiveKind::Requires => Self::REQUIRES,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn iter() -> impl Iterator<Item = Self> {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
UnimplementedDirectiveKind::iter().map(Self::Unimplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`DirectiveKind`] that is not yet implemented. See [`DirectiveKind::Unimplemented`].
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(test, derive(strum::EnumIter))]
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::front::wgsl::assert_parse_err;
|
||||
|
||||
use super::{DirectiveKind, UnimplementedDirectiveKind};
|
||||
|
||||
#[test]
|
||||
fn unimplemented_directives() {
|
||||
for unsupported_shader in UnimplementedDirectiveKind::iter() {
|
||||
let shader;
|
||||
let expected_msg;
|
||||
match unsupported_shader {
|
||||
UnimplementedDirectiveKind::Diagnostic => {
|
||||
shader = "diagnostic(off,derivative_uniformity);";
|
||||
expected_msg = "\
|
||||
error: `diagnostic` is not yet implemented
|
||||
┌─ wgsl:1:1
|
||||
│
|
||||
1 │ diagnostic(off,derivative_uniformity);
|
||||
│ ^^^^^^^^^^ 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/5320>, so they can prioritize it!
|
||||
|
||||
";
|
||||
}
|
||||
UnimplementedDirectiveKind::Enable => {
|
||||
shader = "enable f16;";
|
||||
expected_msg = "\
|
||||
error: `enable` is not yet implemented
|
||||
┌─ wgsl:1:1
|
||||
│
|
||||
1 │ enable f16;
|
||||
│ ^^^^^^ 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/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!
|
||||
|
||||
";
|
||||
}
|
||||
};
|
||||
|
||||
assert_parse_err(shader, expected_msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn directive_after_global_decl() {
|
||||
for unsupported_shader in DirectiveKind::iter() {
|
||||
let directive;
|
||||
let expected_msg;
|
||||
match unsupported_shader {
|
||||
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Diagnostic) => {
|
||||
directive = "diagnostic(off,derivative_uniformity)";
|
||||
expected_msg = "\
|
||||
error: expected global declaration, but found a global directive
|
||||
┌─ wgsl:2:1
|
||||
│
|
||||
2 │ diagnostic(off,derivative_uniformity);
|
||||
│ ^^^^^^^^^^ written after first global declaration
|
||||
│
|
||||
= note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?
|
||||
|
||||
";
|
||||
}
|
||||
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Enable) => {
|
||||
directive = "enable f16";
|
||||
expected_msg = "\
|
||||
error: expected global declaration, but found a global directive
|
||||
┌─ wgsl:2:1
|
||||
│
|
||||
2 │ enable f16;
|
||||
│ ^^^^^^ written after first global declaration
|
||||
│
|
||||
= note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?
|
||||
|
||||
";
|
||||
}
|
||||
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Requires) => {
|
||||
directive = "requires readonly_and_readwrite_storage_textures";
|
||||
expected_msg = "\
|
||||
error: expected global declaration, but found a global directive
|
||||
┌─ wgsl:2:1
|
||||
│
|
||||
2 │ requires readonly_and_readwrite_storage_textures;
|
||||
│ ^^^^^^^^ written after first global declaration
|
||||
│
|
||||
= note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?
|
||||
|
||||
";
|
||||
}
|
||||
}
|
||||
|
||||
let shader = format!(
|
||||
"\
|
||||
@group(0) @binding(0) var<storage> thing: i32;
|
||||
{directive};
|
||||
"
|
||||
);
|
||||
assert_parse_err(&shader, expected_msg);
|
||||
}
|
||||
}
|
||||
}
|
@ -355,7 +355,6 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(in crate::front::wgsl) fn peek_ident_with_span(
|
||||
&mut self,
|
||||
) -> Result<(&'a str, Span), Error<'a>> {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::front::wgsl::error::{Error, ExpectedToken};
|
||||
use crate::front::wgsl::parse::directive::DirectiveKind;
|
||||
use crate::front::wgsl::parse::lexer::{Lexer, Token};
|
||||
use crate::front::wgsl::parse::number::Number;
|
||||
use crate::front::wgsl::Scalar;
|
||||
@ -7,6 +8,7 @@ use crate::{Arena, FastIndexSet, Handle, ShaderStage, Span};
|
||||
|
||||
pub mod ast;
|
||||
pub mod conv;
|
||||
pub mod directive;
|
||||
pub mod lexer;
|
||||
pub mod number;
|
||||
|
||||
@ -136,6 +138,7 @@ enum Rule {
|
||||
SingularExpr,
|
||||
UnaryExpr,
|
||||
GeneralExpr,
|
||||
Directive,
|
||||
}
|
||||
|
||||
struct ParsedAttribute<T> {
|
||||
@ -2357,6 +2360,9 @@ impl Parser {
|
||||
let start = lexer.start_byte_offset();
|
||||
let kind = match lexer.next() {
|
||||
(Token::Separator(';'), _) => None,
|
||||
(Token::Word(word), directive_span) if DirectiveKind::from_ident(word).is_some() => {
|
||||
return Err(Error::DirectiveAfterFirstGlobalDecl { directive_span });
|
||||
}
|
||||
(Token::Word("struct"), _) => {
|
||||
let name = lexer.next_ident()?;
|
||||
|
||||
@ -2474,6 +2480,24 @@ impl Parser {
|
||||
|
||||
let mut lexer = Lexer::new(source);
|
||||
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::Unimplemented(kind) => {
|
||||
return Err(Error::DirectiveNotYetImplemented { kind, span })
|
||||
}
|
||||
}
|
||||
self.pop_rule_span(&lexer);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.global_decl(&mut lexer, &mut tu) {
|
||||
Err(error) => return Err(error),
|
||||
|
Loading…
Reference in New Issue
Block a user