-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[wgsl-in] Implement module-level scoping #2075
Conversation
@jimblandy Would be great if you could a look at the AST definition! Also, you were talking about how 2 passes would be all that was necessary. The only way I can think of doing this in two passes is to maintain a set of the raw identifiers referenced by each declaration during parsing, and then using those to sort the declarations, but I'm not quite sure if this will actually be faster than resolving on the same IR, and then sorting. |
Here's how I was thinking it could work (I haven't actually tried this yet):
This amounts to a depth-first traversal of the DAG of definitions, where we "mark nodes as visited" by adding resolved definitions to the After we've processed the entry points, we may want to just walk the top-level unresolved definitions and finish off anything unvisited. Such definitions could be safely dropped from the module, since they're unreachable from any entry point (and we should probably produce warnings about this), but it's very useful for testing to have everything in the input appear in the Naturally, we will need to check for cyclic references at the point of recursion. As I say, I haven't tried this, so there may be aspects of the problem I've missed - but it seems like something along these lines ought to be possible somehow. |
You should use Otherwise, try to make the change as undisruptive and limited as you can while still preserving legibility. |
Recursion was something I thought of, but processing each declaration does use quite a large amount of stack, so we might end up stack overflowing quite quick, especially on Windows, where the default stack size is 2 MB. We can probably use stacker, which is what rustc uses. About the warnings, I do plan to add error recovery to the frontend so we can output more than one error, and will add warnings for unused items there (I did have this implemented in my other frontend, it isn't too big of a change). |
Using arenas instead of trees for expressions should reduce the stack depth, though, because you can just iterate over the arena directly. And with a little care we could avoid recursing on statements at all - I think only |
|
I think keeping a set of (textual) dependencies used by each declaration during parsing, and then using that to post-order depth-first sort would be the easiest solution, and it shouldn't really impact performance too much. The post-order sorting with cycle detection does require recursion, however it should require much less stack space than resolving the function. I have also implemented parsing and finalized the AST structures, would be great if you could take a look, and tell me if any changes are needed :) (I have removed parts of the code that aren't necessary for parsing, will modify and re-add them later) |
If I understand what you mean here, this seems reasonable, too: basically, just treat every identifier use as an edge in a DAG constructed during parsing. It is definitely preferable to avoid the recursion. I'll take a look soon. |
I did have to keep track of locals during the initial parse phase, to fix an issue with code like this: fn a() {
let a = b();
}
fn b() -> i32 {
let a = 20;
return a;
} If we just kept a set of textual dependencies, |
I'm curious if the ast generated here is publicly accessible. I do have a case where I need to look at the shader's ast to figure out how to setup bind groups |
It's not public at the moment, but I can make it so. However, can you not just use the |
@jimblandy This has reached feature parity with the existing frontend, and passes all the functionality-based tests. The diagnostics snapshots don't pass yet, but fixing those shouldn't change too much. You can start reviewing now. Surprisingly, performance has actually improved. The current frontend consistently takes around 3.6 ms for the The only difference in output is that I don't initialize |
Hey @SparkyPotato, thanks for this PR! There seem to be quite a lot of copy-paste "changes" (code being moved around) that make it difficult to know what actually changed. Would it be possible for you to undo as many code moves as possible to make it easier to review this? Right now, we are looking at having to review 11943 LOC (without test snapshots) which I don't consider particularly "reviewable". |
@teoxoy I've moved as much as I think possible back (parsing + errors).
This brings the overall diff of Do tell me if there's anything else I need to do to make reviewing easier! |
Thanks for reducing the diff, but it still looks like quite a lot of changes which will make browsing the history of the files challenging. Let's move the code in After this, the changes should be reviewable and after the review we can see how to proceed with new files/folders (and moving code around). |
Got it down to +4495 -2811, not sure if anything more is possible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reducing the diff!
I took a brief look around the code and left a few comments.
General comment
Only user defined structs should have a crate::Type.name
(some of the backends now generate extra type aliases for built-in types).
Wouldn't this lead to worse validation errors, where something like |
I think for errors we can have a method that gives us the proper name. Also, considering we have other front-ends that don't use the same syntax (i.e. |
Done! |
Thanks! There seem to be a few more places where we still set the name:
|
@teoxoy I have done a few things:
|
Feel free to change the GLSL frontend as well.
We can make use of
Can't think of doing it any other way right now since we match on those Thanks for the updates! |
from #2075 (comment) I also noticed this by looking at the test snapshots. I think it should be fine for now considering we'll have to deal with const expressions differently soon. |
All changes done. |
Hmm. This drops the name from the type:
|
Yeah, we discard type aliases as soon as they are resolved. Using type alias names when possible is quite hard, even rustc doesn't do it. Solutions to fixing that include:
|
56e1acd
to
e1a627f
Compare
LGTM, I think this should be ready to merge now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, let's go.
Fixes gfx-rs#1745: Support out-of-order module scope declarations in WGSL Fixes gfx-rs#1044: Forbid local variable shadowing in WGSL Fixes gfx-rs#2076: [wgsl-in] no error for duplicated type definition Fixes gfx-rs#2071: Global item does not support 'const' Fixes gfx-rs#2105: [wgsl-in] Type aliases for a vecN<T> doesn't work when constructing vec from a single argument Fixes gfx-rs#1775: Referencing a function without a return type yields an unknown identifier error. Fixes gfx-rs#2089: Error span reported on the declaration of a variable instead of its use Fixes gfx-rs#1996: [wgsl-in] Confusing error: "expected unsigned/signed integer literal, found '1'" Separate parsing from lowering by generating an AST, which desugars as much as possible down to something like Naga IR. The AST is then used to resolve identifiers while lowering to Naga IR. Co-authored-by: Teodor Tanasoaia <[email protected]> Co-authored-by: Jim Blandy <[email protected]>
5d1650e
to
1976586
Compare
Thanks for the hard work guys! When would be the next release date with this included? I really want to see this land in |
@cwfitzgerald is planning the next release. Connor? |
25th of this month! |
@cwfitzgerald That's exciting, thanks! |
Make changes suggested in #2075, but put off to a separate PR because they would interfere with reviewing the change: - Split the new WGSL front end into modules in a logical way. - Rename `Parser` to `Frontend`.
Try 2 :)
This PR separates parsing from lowering, by generating an AST, which desugars as much as possible down to something like Naga IR.
The AST is then used to resolve identifiers while lowering to Naga IR.