Skip to content

Commit b3f665b

Browse files
feat(wgsl-in): create skeleton for parsing directives
1 parent cb31465 commit b3f665b

File tree

8 files changed

+262
-1
lines changed

8 files changed

+262
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
8585
- Support local `const` declarations in WGSL. By @sagudev in [#6156](https://github.com/gfx-rs/wgpu/pull/6156).
8686
- Implemented `const_assert` in WGSL. By @sagudev in [#6198](https://github.com/gfx-rs/wgpu/pull/6198).
8787
- Support polyfilling `inverse` in WGSL. By @chyyran in [#6385](https://github.com/gfx-rs/wgpu/pull/6385).
88+
- 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).
8889

8990
#### General
9091

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

naga/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,4 @@ ron = "0.8.0"
101101
rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" }
102102
serde = { workspace = true, features = ["derive"] }
103103
spirv = { version = "0.3", features = ["deserialize"] }
104+
strum.workspace = true

naga/src/front/wgsl/error.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::front::wgsl::parse::directive::{DirectiveKind, UnimplementedDirectiveKind};
12
use crate::front::wgsl::parse::lexer::Token;
23
use crate::front::wgsl::Scalar;
34
use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError};
@@ -265,6 +266,13 @@ pub(crate) enum Error<'a> {
265266
PipelineConstantIDValue(Span),
266267
NotBool(Span),
267268
ConstAssertFailed(Span),
269+
DirectiveNotYetImplemented {
270+
kind: UnimplementedDirectiveKind,
271+
span: Span,
272+
},
273+
DirectiveAfterFirstGlobalDecl {
274+
directive_span: Span,
275+
},
268276
}
269277

270278
#[derive(Clone, Debug)]
@@ -861,6 +869,36 @@ impl<'a> Error<'a> {
861869
labels: vec![(span, "evaluates to false".into())],
862870
notes: vec![],
863871
},
872+
Error::DirectiveNotYetImplemented { kind, span } => ParseError {
873+
message: format!(
874+
"`{}` is not yet implemented",
875+
DirectiveKind::Unimplemented(kind).to_ident()
876+
),
877+
labels: vec![(
878+
span,
879+
"this global directive is standard, but not yet implemented".into(),
880+
)],
881+
notes: vec![format!(
882+
concat!(
883+
"Let Naga maintainers know that you ran into this at ",
884+
"<https://github.com/gfx-rs/wgpu/issues/{}>, ",
885+
"so they can prioritize it!"
886+
),
887+
kind.tracking_issue_num()
888+
)],
889+
},
890+
Error::DirectiveAfterFirstGlobalDecl { directive_span } => ParseError {
891+
message: "expected global declaration, but found a global directive".into(),
892+
labels: vec![(
893+
directive_span,
894+
"written after first global declaration".into(),
895+
)],
896+
notes: vec![concat!(
897+
"global directives are only allowed before global declarations; ",
898+
"maybe hoist this closer to the top of the shader module?"
899+
)
900+
.into()],
901+
},
864902
}
865903
}
866904
}

naga/src/front/wgsl/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,21 @@ impl Frontend {
5858
pub fn parse_str(source: &str) -> Result<crate::Module, ParseError> {
5959
Frontend::new().parse(source)
6060
}
61+
62+
#[cfg(test)]
63+
#[track_caller]
64+
pub fn assert_parse_err(input: &str, snapshot: &str) {
65+
let output = parse_str(input)
66+
.expect_err("expected parser error")
67+
.emit_to_string(input);
68+
if output != snapshot {
69+
for diff in diff::lines(snapshot, &output) {
70+
match diff {
71+
diff::Result::Left(l) => println!("-{l}"),
72+
diff::Result::Both(l, _) => println!(" {l}"),
73+
diff::Result::Right(r) => println!("+{r}"),
74+
}
75+
}
76+
panic!("Error snapshot failed");
77+
}
78+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
//! WGSL directives. The focal point of this API is [`DirectiveKind`].
2+
//!
3+
//! See also <https://www.w3.org/TR/WGSL/#directives>.
4+
5+
/// A parsed sentinel word indicating the type of directive to be parsed next.
6+
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
7+
pub enum DirectiveKind {
8+
Unimplemented(UnimplementedDirectiveKind),
9+
}
10+
11+
impl DirectiveKind {
12+
const DIAGNOSTIC: &'static str = "diagnostic";
13+
const ENABLE: &'static str = "enable";
14+
const REQUIRES: &'static str = "requires";
15+
16+
/// Convert from a sentinel word in WGSL into its associated [`DirectiveKind`], if possible.
17+
pub fn from_ident(s: &str) -> Option<Self> {
18+
Some(match s {
19+
Self::DIAGNOSTIC => Self::Unimplemented(UnimplementedDirectiveKind::Diagnostic),
20+
Self::ENABLE => Self::Unimplemented(UnimplementedDirectiveKind::Enable),
21+
Self::REQUIRES => Self::Unimplemented(UnimplementedDirectiveKind::Requires),
22+
_ => return None,
23+
})
24+
}
25+
26+
/// Maps this [`DirectiveKind`] into the sentinel word associated with it in WGSL.
27+
pub const fn to_ident(self) -> &'static str {
28+
match self {
29+
Self::Unimplemented(kind) => match kind {
30+
UnimplementedDirectiveKind::Diagnostic => Self::DIAGNOSTIC,
31+
UnimplementedDirectiveKind::Enable => Self::ENABLE,
32+
UnimplementedDirectiveKind::Requires => Self::REQUIRES,
33+
},
34+
}
35+
}
36+
37+
#[cfg(test)]
38+
fn iter() -> impl Iterator<Item = Self> {
39+
use strum::IntoEnumIterator;
40+
41+
UnimplementedDirectiveKind::iter().map(Self::Unimplemented)
42+
}
43+
}
44+
45+
/// A [`DirectiveKind`] that is not yet implemented. See [`DirectiveKind::Unimplemented`].
46+
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
47+
#[cfg_attr(test, derive(strum::EnumIter))]
48+
pub enum UnimplementedDirectiveKind {
49+
Diagnostic,
50+
Enable,
51+
Requires,
52+
}
53+
54+
impl UnimplementedDirectiveKind {
55+
pub const fn tracking_issue_num(self) -> u16 {
56+
match self {
57+
Self::Diagnostic => 5320,
58+
Self::Requires => 6350,
59+
Self::Enable => 5476,
60+
}
61+
}
62+
}
63+
64+
#[cfg(test)]
65+
mod test {
66+
use strum::IntoEnumIterator;
67+
68+
use crate::front::wgsl::assert_parse_err;
69+
70+
use super::{DirectiveKind, UnimplementedDirectiveKind};
71+
72+
#[test]
73+
fn unimplemented_directives() {
74+
for unsupported_shader in UnimplementedDirectiveKind::iter() {
75+
let shader;
76+
let expected_msg;
77+
match unsupported_shader {
78+
UnimplementedDirectiveKind::Diagnostic => {
79+
shader = "diagnostic(off,derivative_uniformity);";
80+
expected_msg = "\
81+
error: `diagnostic` is not yet implemented
82+
┌─ wgsl:1:1
83+
84+
1 │ diagnostic(off,derivative_uniformity);
85+
│ ^^^^^^^^^^ this global directive is standard, but not yet implemented
86+
87+
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5320>, so they can prioritize it!
88+
89+
";
90+
}
91+
UnimplementedDirectiveKind::Enable => {
92+
shader = "enable f16;";
93+
expected_msg = "\
94+
error: `enable` is not yet implemented
95+
┌─ wgsl:1:1
96+
97+
1 │ enable f16;
98+
│ ^^^^^^ this global directive is standard, but not yet implemented
99+
100+
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/5476>, so they can prioritize it!
101+
102+
";
103+
}
104+
UnimplementedDirectiveKind::Requires => {
105+
shader = "requires readonly_and_readwrite_storage_textures";
106+
expected_msg = "\
107+
error: `requires` is not yet implemented
108+
┌─ wgsl:1:1
109+
110+
1 │ requires readonly_and_readwrite_storage_textures
111+
│ ^^^^^^^^ this global directive is standard, but not yet implemented
112+
113+
= note: Let Naga maintainers know that you ran into this at <https://github.com/gfx-rs/wgpu/issues/6350>, so they can prioritize it!
114+
115+
";
116+
}
117+
};
118+
119+
assert_parse_err(shader, expected_msg);
120+
}
121+
}
122+
123+
#[test]
124+
fn directive_after_global_decl() {
125+
for unsupported_shader in DirectiveKind::iter() {
126+
let directive;
127+
let expected_msg;
128+
match unsupported_shader {
129+
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Diagnostic) => {
130+
directive = "diagnostic(off,derivative_uniformity)";
131+
expected_msg = "\
132+
error: expected global declaration, but found a global directive
133+
┌─ wgsl:2:1
134+
135+
2 │ diagnostic(off,derivative_uniformity);
136+
│ ^^^^^^^^^^ written after first global declaration
137+
138+
= note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?
139+
140+
";
141+
}
142+
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Enable) => {
143+
directive = "enable f16";
144+
expected_msg = "\
145+
error: expected global declaration, but found a global directive
146+
┌─ wgsl:2:1
147+
148+
2 │ enable f16;
149+
│ ^^^^^^ written after first global declaration
150+
151+
= note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?
152+
153+
";
154+
}
155+
DirectiveKind::Unimplemented(UnimplementedDirectiveKind::Requires) => {
156+
directive = "requires readonly_and_readwrite_storage_textures";
157+
expected_msg = "\
158+
error: expected global declaration, but found a global directive
159+
┌─ wgsl:2:1
160+
161+
2 │ requires readonly_and_readwrite_storage_textures;
162+
│ ^^^^^^^^ written after first global declaration
163+
164+
= note: global directives are only allowed before global declarations; maybe hoist this closer to the top of the shader module?
165+
166+
";
167+
}
168+
}
169+
170+
let shader = format!(
171+
"\
172+
@group(0) @binding(0) var<storage> thing: i32;
173+
{directive};
174+
"
175+
);
176+
assert_parse_err(&shader, expected_msg);
177+
}
178+
}
179+
}

naga/src/front/wgsl/parse/lexer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ impl<'a> Lexer<'a> {
355355
}
356356
}
357357

358-
#[allow(dead_code)]
359358
pub(in crate::front::wgsl) fn peek_ident_with_span(
360359
&mut self,
361360
) -> Result<(&'a str, Span), Error<'a>> {

naga/src/front/wgsl/parse/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::front::wgsl::error::{Error, ExpectedToken};
2+
use crate::front::wgsl::parse::directive::DirectiveKind;
23
use crate::front::wgsl::parse::lexer::{Lexer, Token};
34
use crate::front::wgsl::parse::number::Number;
45
use crate::front::wgsl::Scalar;
@@ -7,6 +8,7 @@ use crate::{Arena, FastIndexSet, Handle, ShaderStage, Span};
78

89
pub mod ast;
910
pub mod conv;
11+
pub mod directive;
1012
pub mod lexer;
1113
pub mod number;
1214

@@ -136,6 +138,7 @@ enum Rule {
136138
SingularExpr,
137139
UnaryExpr,
138140
GeneralExpr,
141+
Directive,
139142
}
140143

141144
struct ParsedAttribute<T> {
@@ -2357,6 +2360,9 @@ impl Parser {
23572360
let start = lexer.start_byte_offset();
23582361
let kind = match lexer.next() {
23592362
(Token::Separator(';'), _) => None,
2363+
(Token::Word(word), directive_span) if DirectiveKind::from_ident(word).is_some() => {
2364+
return Err(Error::DirectiveAfterFirstGlobalDecl { directive_span });
2365+
}
23602366
(Token::Word("struct"), _) => {
23612367
let name = lexer.next_ident()?;
23622368

@@ -2474,6 +2480,24 @@ impl Parser {
24742480

24752481
let mut lexer = Lexer::new(source);
24762482
let mut tu = ast::TranslationUnit::default();
2483+
2484+
// Parse directives.
2485+
#[allow(clippy::never_loop, unreachable_code)]
2486+
while let Ok((ident, span)) = lexer.peek_ident_with_span() {
2487+
if let Some(kind) = DirectiveKind::from_ident(ident) {
2488+
self.push_rule_span(Rule::Directive, &mut lexer);
2489+
let _ = lexer.next_ident_with_span().unwrap();
2490+
match kind {
2491+
DirectiveKind::Unimplemented(kind) => {
2492+
return Err(Error::DirectiveNotYetImplemented { kind, span })
2493+
}
2494+
}
2495+
self.pop_rule_span(&lexer);
2496+
} else {
2497+
break;
2498+
}
2499+
}
2500+
24772501
loop {
24782502
match self.global_decl(&mut lexer, &mut tu) {
24792503
Err(error) => return Err(error),

0 commit comments

Comments
 (0)