mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 00:03:29 +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).
|
- 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).
|
- 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).
|
- 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
|
#### General
|
||||||
|
|
||||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1897,6 +1897,7 @@ dependencies = [
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"spirv 0.3.0+sdk-1.3.268.0",
|
"spirv 0.3.0+sdk-1.3.268.0",
|
||||||
|
"strum",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
|
@ -101,3 +101,4 @@ ron = "0.8.0"
|
|||||||
rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" }
|
rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
spirv = { version = "0.3", features = ["deserialize"] }
|
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::parse::lexer::Token;
|
||||||
use crate::front::wgsl::Scalar;
|
use crate::front::wgsl::Scalar;
|
||||||
use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
|
use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
|
||||||
@ -265,6 +266,13 @@ pub(crate) enum Error<'a> {
|
|||||||
PipelineConstantIDValue(Span),
|
PipelineConstantIDValue(Span),
|
||||||
NotBool(Span),
|
NotBool(Span),
|
||||||
ConstAssertFailed(Span),
|
ConstAssertFailed(Span),
|
||||||
|
DirectiveNotYetImplemented {
|
||||||
|
kind: UnimplementedDirectiveKind,
|
||||||
|
span: Span,
|
||||||
|
},
|
||||||
|
DirectiveAfterFirstGlobalDecl {
|
||||||
|
directive_span: Span,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -861,6 +869,36 @@ impl<'a> Error<'a> {
|
|||||||
labels: vec![(span, "evaluates to false".into())],
|
labels: vec![(span, "evaluates to false".into())],
|
||||||
notes: vec![],
|
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> {
|
pub fn parse_str(source: &str) -> Result<crate::Module, ParseError> {
|
||||||
Frontend::new().parse(source)
|
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(
|
pub(in crate::front::wgsl) fn peek_ident_with_span(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<(&'a str, Span), Error<'a>> {
|
) -> Result<(&'a str, Span), Error<'a>> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::front::wgsl::error::{Error, ExpectedToken};
|
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::lexer::{Lexer, Token};
|
||||||
use crate::front::wgsl::parse::number::Number;
|
use crate::front::wgsl::parse::number::Number;
|
||||||
use crate::front::wgsl::Scalar;
|
use crate::front::wgsl::Scalar;
|
||||||
@ -7,6 +8,7 @@ use crate::{Arena, FastIndexSet, Handle, ShaderStage, Span};
|
|||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod conv;
|
pub mod conv;
|
||||||
|
pub mod directive;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod number;
|
pub mod number;
|
||||||
|
|
||||||
@ -136,6 +138,7 @@ enum Rule {
|
|||||||
SingularExpr,
|
SingularExpr,
|
||||||
UnaryExpr,
|
UnaryExpr,
|
||||||
GeneralExpr,
|
GeneralExpr,
|
||||||
|
Directive,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParsedAttribute<T> {
|
struct ParsedAttribute<T> {
|
||||||
@ -2357,6 +2360,9 @@ impl Parser {
|
|||||||
let start = lexer.start_byte_offset();
|
let start = lexer.start_byte_offset();
|
||||||
let kind = match lexer.next() {
|
let kind = match lexer.next() {
|
||||||
(Token::Separator(';'), _) => None,
|
(Token::Separator(';'), _) => None,
|
||||||
|
(Token::Word(word), directive_span) if DirectiveKind::from_ident(word).is_some() => {
|
||||||
|
return Err(Error::DirectiveAfterFirstGlobalDecl { directive_span });
|
||||||
|
}
|
||||||
(Token::Word("struct"), _) => {
|
(Token::Word("struct"), _) => {
|
||||||
let name = lexer.next_ident()?;
|
let name = lexer.next_ident()?;
|
||||||
|
|
||||||
@ -2474,6 +2480,24 @@ impl Parser {
|
|||||||
|
|
||||||
let mut lexer = Lexer::new(source);
|
let mut lexer = Lexer::new(source);
|
||||||
let mut tu = ast::TranslationUnit::default();
|
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 {
|
loop {
|
||||||
match self.global_decl(&mut lexer, &mut tu) {
|
match self.global_decl(&mut lexer, &mut tu) {
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
|
Loading…
Reference in New Issue
Block a user