Skip to content

Add wildcard imports#183

Open
stefnotch wants to merge 20 commits into
mainfrom
glob-imports
Open

Add wildcard imports#183
stefnotch wants to merge 20 commits into
mainfrom
glob-imports

Conversation

@stefnotch
Copy link
Copy Markdown
Collaborator

@stefnotch stefnotch commented Apr 19, 2026

Fix #130
This adds a simple wildcard imports mechanism with safeguards to reduce the risk of libraries accidentally publishing breaking changes.

See also https://hackmd.io/@stefnotch/SyKtKk8uWx for some prior thoughts on this

Comment thread Imports.md Outdated
When the library module is called `prelude`, it is a module designed for wildcards. Adding a new item to it is a semver breaking change.
Wildcard importing from it is always allowed. (When we add exports, we will make this controllable instead of only giving special treatment to the `prelude`.)

Other library modules are not designed for wildcards. If another wildcard import exists, then an `wildcard_import` [diagnostic](https://www.w3.org/TR/WGSL/#diagnostics) at the error level will be emitted.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is not a bulletproof mechanism. One can still construct cases such as

// Allowed:
// This isn't a module designed for wildcard importing, 
// but I don't have any other wildcard imports.
import bevy::math::*;

const a: f32 = 3;

Then, if bevy::math adds struct f32 { a: u32 }, it would be a breaking change.
Wildcards do shadow builtins after all. The builtin shadowing is a necessity, because we don't want future WGSL evolutions to randomly break existing WESL code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting point. Anything we should do now? I imagine the WGSL folks won't want to be limited in the future use of their reserved words.

@stefnotch stefnotch changed the title Add glob imports Add wildcard imports Apr 19, 2026
@stefnotch stefnotch force-pushed the glob-imports branch 2 times, most recently from 6232981 to 0156aa9 Compare April 19, 2026 13:04
@Arne-Berner
Copy link
Copy Markdown

This is very confusing to me. There was an ongoing discussion in issues. The hackmd was neither mentioned on discord nor in the issue. So it seems to me, that there is an internal design team consisting of two people that decides on this. This pr has not been merged yet, but it is weird to me, that the discussion switched to the hackmd. (Bevy uses hackmd too, but it often gets referenced in issues, discussions or on discord.)

It states that library authors don't expect adding new items to be a breaking change. But the issue discusses this. This premise does not seem as clear as it is stated here. Most people find this in rust to be a non issue in that discussion.

The whole "private vs public by default" discussion seems decided here.

It mostly took the original proposal in the discussion from the author and renamed ambient to prelude. This still seems to me like a "magic keyword" that would confuse me a lot as a library author and user. Especially since bevy uses it subtly different.

I couldn't find anything about this, but this PR raised the following question for me:
How is wesl organized and what is the workflow for decisions?

The PRs here might just be there for lose discussion, but it assumes some things on such a fundamental level that I'm not sure it is that. (Like private by default)

@stefnotch
Copy link
Copy Markdown
Collaborator Author

stefnotch commented Apr 19, 2026

@Arne-Berner Most of the discussions happened on the WESL Discord servers or internally, since that is a fast way to get feedback on ideas. The hackmd note are thoughts that I wrote down after that. With that, this proposal here is one of the few designs that I'm reasonably confident in.

That said, this PR here is for discussion before I push through something unreasonable.

We are leaning towards having private by default. Could you point out the place where this proposal assumes that? If it does, then that is likely an oversight on my part.

Regarding

It states that library authors don't expect adding new items to be a breaking change.

The cargo documentation explicitly calls out this corner case, since it is unexpected. One of our goals for WESL is to allow for safe library evolution beyond the Rust ecosystem. This means taking into account what npm users expect of semver, and also designing a safe path for future languages that we'll add. Semver breaking changes depend on how and when the package manager will do patch upgrades.

It mostly took the original proposal in the discussion from the author and renamed ambient to prelude. This still seems to me like a "magic keyword" that would confuse me a lot as a library author and user. Especially since bevy uses it subtly different.

This really wasn't my intent. My intent was to surface the corner cases via diagnostics. The prelude case was purely added for convenience. I am happy to remove it or replace it with an opt-in for prelude-like modules.

Though, how does Bevy's different usage look like?

@Arne-Berner
Copy link
Copy Markdown

We are leaning towards having private by default. Could you point out the place where this proposal assumes that? If it does, then that is likely an oversight on my part.

I'm sorry for not being clear there. When I said "public by default vs private by default" I was talking about using "export" on a module, to make it available for users.

Though, how does Bevy's different usage look like?

Bevy uses prelude as a marker for utilities that are most likely needed. It is a helper for the "most important functions". Naming a crate prelude does not have any function, besides being a convenient function.
If I tried to write a wesl library myself and most people would use the import package::prelude::* syntax I would assume that wildcard imports are possible everywhere. (At least if I didn't read this proposal) There would be no indicator in the code to let me assume that "prelude" is a keyword in the language that helps me export a module. Especially if I'm coming from rust or bevy. Since I would assume that "prelude" was chosen in a way that it is used in Rust. (an implicit marker, not a functional part of the language)

I must have misunderstood

See also https://hackmd.io/@stefnotch/SyKtKk8uWx for some prior thoughts on this

English is not my first language. If you wrote "See also ... for my prior thoughts on this" I wouldn't have assumed that the hackmd was the official discussion threat that lead to this decision. Getting all subtleties in english sometimes is hard for me. I've been lurking in the discord and must have missed further discussion about this topic.
I asked about the workflow for decisions because in the issue thread it was stated that Internally, I think we found a new favorite scheme but we need to write it up.. I didn't understand that this meant the discord server in general.

@mighdoll
Copy link
Copy Markdown
Contributor

Hi @Arne-Berner our project is younger and smaller than Bevy and we haven't polished a procedure about where we discuss. We're still small enough that we sometimes save issues for live meetings, rather than doing things async. Undoubtedly we'll grow more standard over time, especially if more folks want to engage! Here Stefan is trying to recall/refresh our earlier discussions, I'm sure we'll discuss/modify further before the final form lands in the spec. We aim for unanimity among the three core committers before we commit spec changes.

@k2d222
Copy link
Copy Markdown
Contributor

k2d222 commented Apr 19, 2026

@Arne-Berner First to answer this:

How is wesl organized and what is the workflow for decisions?

We always submit ideas to the community in the shape of Discord discussions, GitHub issues and GitHub PRs (in that order) before merging. So far the "leadership" has been rather natural, since there are three main contributors (@stefnotch, @mighdoll and me) and reaching consensus has always been possible. Though, since you're raising a valid concern, I'll open a separate issue for further discussion and link it here. EDIT: #184


Back on topic

I was strongly in favor of unrestricted wildcards. As you said, they are usually not an issue. But the proposal written by @stefnotch may give you the wrong idea, at a first glance, because it goes deep into the edge cases. To clarify this PR:

  • you can always wildcard-import anything you like.
  • ...however, if you have two wildcard-import in the same file, AND they come from external dependencies, you will have to silence a diagnostic -- otherwise, the compiler will let you know why with a nice error message.
  • ...unless they are named prelude. In this case, changing the API of a prelude module must be a breaking change.

In other words, a large majority of users would just never meet these edge cases. And if they do, it's a compile-time diagnostic that can be silenced.

Comment thread Imports.md Outdated
### Wildcard imports from libraries

For libraries, we distinguish between modules that were designed for wildcard imports and modules that were not.
Library authors generally expect that adding a new item is not a breaking change. Wildcard imports are a surprising edge case.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add quite a few links here to back this up. Wildcard imports are a brittle feature in many programming languages.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think linking the decisions about wildcards in the most common webgpu languages should suffice. Rust allows it, javascript disallows it, c++ uses "using namespace std;" but discourages it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should also look through the discussion around the clippy lint for wildcard imports. While that lint is a lot more heavy handed, it might contain valuable information.

Comment thread Imports.md
TODO: Or should it immediately result in a warning, even if the variables are unused?

### Wildcard imports from libraries

Copy link
Copy Markdown
Collaborator Author

@stefnotch stefnotch Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably start by saying something like "The following section applies when the current module has two or more wildcard imports. We specifically deal with the potential name clashes and corner cases. Users are not expected to run into this edge case. It exists to give us certain guarantees in the language itself. This will be useful, for example, to build semver checking tools."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any mention of this "two wilcards rule" any more. Is this comment outdated?

Comment thread Imports.md Outdated

WESL detects the edge cases and will emit a diagnostic. This makes upholding semver guarantees significantly easier.

When the library module is called `prelude`, it is a module designed for wildcards. Adding a new item to it is a semver breaking change.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make the prelude special? Or should we find some other way of making modules as "safe for wildcard imports even if there are multiple of them"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue discusses this at length:

  • Library authors could think their library should be imported without restriction and everything gets put into prelude.
  • it's hard to understand why sometimes multiple imports work (using prelude) and sometimes it needs diagnostic opt in
  • it's changing the definition of semver as most people understand it

Copy link
Copy Markdown

@cart cart Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit: see my next comment first

I do not think prelude should be special. Implicit hard-coding of this feature to a specific module name is both implicit (hard for authors and consumers to understand and track), and overly restrictive (prevents library authors from defining the interfaces they want).

Given that the competition in the shader space generally looks like this:

import shader_material;

I suspect there will be significant appetite for "flat" scoped modules intended to be pulled in wholesale for a context:

import shader_material::*;

Forcing library authors to define a redundant (and longer) import_shader_material::prelude::*, then forcing library consumers to choose between import shader_material::Symbol and import shader_material::prelude::Symbol is "friction" / confusion on both sides.

In a world where wildcards aren't supported everywhere by default (which is not the world I think we should be living in), library authors should be able to opt-in their modules at the language level. This provides explicit, positive handoff (rather than implicit special casing), and flexibility / autonomy on both sides. Something like:

export wildcard shader_material;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm either I misread or I was reviewing an older version:

(When we add exports, we will make this controllable instead of only giving special treatment to the prelude.)

I'll take back my concerns here.

@Arne-Berner
Copy link
Copy Markdown

Back on topic

I was strongly in favor of unrestricted wildcards. As you said, they are usually not an issue. But the proposal written by @stefnotch may give you the wrong idea, at a first glance, because it goes deep into the edge cases. To clarify this PR:

  • you can always wildcard-import anything you like.
  • ...however, if you have two wildcard-import in the same file, AND they come from external dependencies, you will have to silence a diagnostic -- otherwise, the compiler will let you know why with a nice error message.
  • ...unless they are named prelude. In this case, changing the API of a prelude module must be a breaking change.

In other words, a large majority of users would just never meet these edge cases. And if they do, it's a compile-time diagnostic that can be silenced.

No, that was very clear! Also I personally really like the proposal (except for the prelude addition). Probably that prelude decision was throwing me off track the most since there seemed to be some sound arguments in the issue about it.

I also think public vs private by default would change this proposal a lot. But that might just be me.

Using opt in diagnostics as a user feels very clever to me :)

Comment thread Imports.md Outdated

Wildcard imports are used to import a whole set of items into the current module. They are lower priority than local names and explicit imports to reduce the chance of breaking changes if the wildcard-imported module changes. Wildcard imported modules are eagerly loaded, since they need to be checked before accessing any predeclared items.

When multiple wildcard imports are used, name clashes are possible.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall you discovered a particularly bad case if library zap wildcard imports foo and bar. A js package manager can follow semver and create a tough user problem if zap now has a wildcard-wildcard conflict. Disallow double external wildcards entirely for now? Or restrict to publisher designated wildcard-safe modules like prelude?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know most services freeze their dependencies and only update them in a safe environment. This would be an easy fix, since you just need to import the specific item you care about explicitly. But there seem to be two sides to this argument:
Some people from the rust side seem to say "it hasn't been an issue in rust"
And you rightfully say that "it might be an issue here"
We can't know that but I think it should cater to the needs of the people that are willing to use it now and not exclusively to a future audience.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We embed our WESL enhanced WebGPU libraries in both npm and cargo, cross platform shader libraries! But much of the npm ecosystem uses semver by default (it's the default if you npm install). Their expectation is that well behaved packages will not fail on patch/minor updates. We can't hope that npm users will change their stripes, it's on us to make sure that npm update basically never fails for well behaved WESL libraries.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not considered a major change because conventionally glob imports are a known forwards-compatibility hazard. Glob imports of items from external crates should be avoided.

The cargo documentation warns against glob imports. So even if it does not come up too often in Rust, doing glob imports is still a discouraged pattern according to the official guidelines.

Comment thread Imports.md Outdated
When the library module is called `prelude`, it is a module designed for wildcards. Adding a new item to it is a semver breaking change.
Wildcard importing from it is always allowed. (When we add exports, we will make this controllable instead of only giving special treatment to the `prelude`.)

Other library modules are not designed for wildcards. If another wildcard import exists, then an `wildcard_import` [diagnostic](https://www.w3.org/TR/WGSL/#diagnostics) at the error level will be emitted.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting point. Anything we should do now? I imagine the WGSL folks won't want to be limited in the future use of their reserved words.

Comment thread Imports.md Outdated
import bar::*; // exports clashing_name
```

The name clash is ignored until the item is used.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should also clarify that name clashes only happen when the imported items are different. It is totally possible to wildcard import the same thing.

@BenjaminBrienen
Copy link
Copy Markdown
Contributor

BenjaminBrienen commented Apr 20, 2026

I don't think stopping people from using glob imports makes sense beyond giving a configurable diagnostic. Triggering special behavior on using 2 glob imports also seems weird if the collision can already occur with only 1 (if a library adds a function that shadows a builtin)

Comment thread Imports.md Outdated
The name clash is ignored until the item is used.
TODO: Or should it immediately result in a warning, even if the variables are unused?

### Wildcard imports from libraries
Copy link
Copy Markdown

@cart cart Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this current proposal slightly more agreeable than the previous one:

  • It uses existing public/private semantics / keywords rather than introducing new ones (or at least, it doesn't explicitly define new things in this space, so I'm assuming this)
  • It prescribes prelude (has existing uses in the wider language ecosystem) for the "wildcard" module name instead of ambient (a brand new word for this space). Given that in Bevy we want to call our wildcard modules prelude, this is nice.
  • It gives users autonomy. They can wildcard import anything provided they suppress the diagnostic.

I still have concerns, but I'll create separate threads for them / respond to existing ones, so we can keep this organized:

Major concerns that I would personally block on:

  • I strongly disagree that prelude should be special-cased here / be the only module that supports this. Looks like I either misread or was reviewing an older version. The current wording states that this will ultimately be configurable.

Minor concerns:

  • I think wildcard imports on modules that aren't flagged as wildcard importable should emit warnings, not errors.
  • I still think this approach introduces language complexity and user-facing pain / friction for nearly intangible benefits. I won't create a thread for this as I've already said my piece in the issue, and we're still here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think this proposal only really makes sense in the context of "private by default". Otherwise a bunch of new concerns are introduced (which I discussed in the issue).

Comment thread Imports.md Outdated
When the library module is called `prelude`, it is a module designed for wildcards. Adding a new item to it is a semver breaking change.
Wildcard importing from it is always allowed. (When we add exports, we will make this controllable instead of only giving special treatment to the `prelude`.)

Other library modules are not designed for wildcards. If another wildcard import exists, then an `wildcard_import` [diagnostic](https://www.w3.org/TR/WGSL/#diagnostics) at the error level will be emitted.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that code can still compile with this, and it only protects against potential / theoretical future breaking changes, I think it should be a warning. This feels like a "best practices enforcement thing" (akin to using the wrong naming conventions). Why break peoples' development flow for it?

@mighdoll
Copy link
Copy Markdown
Contributor

Thanks @cart for clarifying which issues seem blocking for getting going with bevy usage/users. The language design issues tend to interrelate, and I would like to help make sure things are unblocked for when bevy is ready to use WESL.

On whether occasional library semver breakage is an acceptable cost for language simplicity wrt to wildcards, can you see the perspective of non-rust communities? Many language communities have chosen more restrictions rather than relying solely on community convention + occasional client cleanup to achieve safe wildcard use. I don't think it's fair to say that other communities reasoning is intangible or only theoretical. It's just a different choice on a spectrum of choices balancing stability vs user flexibility.

In practice for WebGPU/WESL, the js community expects semver to work and we embed in their library ecosystem. So we have to educate every new js user coming over to WESL and WebGPU libraries on rules for using wildcards safely. I want to use our linker/linter/ide tools to guide them in the right direction.

Personally, I managed engineering teams using unrestricted wildcards fruitfully for many years in Scala. I like using them just fine, even though I think one of these middle ground solutions is better for the WebGPU/WESL community.

If we can fully deliver cross platform WebGPU shader libraries, we'll have something very valuable for the community. That's a prize I like to keep an eye on. I don't like to compromise on simplicity goals, but I'm willing to do it here if it helps us get to compatible libraries.

@stefnotch
Copy link
Copy Markdown
Collaborator Author

I've pushed a new version of the proposal, but now that I've written out the longer diagnostics option, I'm not entirely convinced that it is the best solution.

The part about glob imports in libraries causing issues is preventable by making the globs explicit upon publishing. As in, if bevy has

import stefnotch_lib::*;
import evil_lib::*;

then when publishing bevy, we could expand the globs. (import stefnotch_lib::{foo, bar}; import evil_lib::{qux};)
In which case no matter what evil_lib does, it can't break bevy.

In user code, I don't think I care too much about a glob import that has just broken. It is an easy fix for me.

The part about accidentally shadowing a builtin seems impossible to fully prevent without #185

Copy link
Copy Markdown

@Arne-Berner Arne-Berner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using prelude still seems out of place in this context and very restricting to the naming scheme of library authors.

Comment thread Imports.md Outdated

2. Emit a warning [diagnostic](https://www.w3.org/TR/WGSL/#diagnostics) named `wildcard_import` if there are multiple wildcard imports from libraries and at least one is not designed for wildcards.

For this, we distinguish between modules that were designed for wildcard imports and modules that were not. When the library module is called `prelude`, it is a module designed for wildcards. Adding a new item to it is a semver breaking change. Therefore, wildcard importing from it is always allowed. (When we add exports, we will make this controllable instead of only giving special treatment to the `prelude`.)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like a step back into forcing people into this prelude pattern. If I had a small library that would only publish its most important functions, then I'd have to do something like

import utils::colors::prelude::*
import utils::noise::prelude::*

This makes it seem like there is a subgroup of functions not exposed in colors and noise. Even though the whole library could just be those two modules and be imported like this:

import utils::colors::*
import utils::noise::*

You could argue, that it is only a warning, but I would say that a warning is supposed to tell you the "right way to do things". Library authors would be incentivised not to use the 2nd option, even though it looks much cleaner. (in a world where glob imports are fine 😄 )

To be more precise here:
I think that this rule only applies to multiple dual imports makes this an edge case. But I would argue that especially when I import multiple modules from one library this behavior would be confusing and not necessary. A library probably won't break their own semver compatibilty.

Comment thread Imports.md Outdated

To reduce the odds of these cases, we recommend the following diagnostics.

1. Emit a warning [diagnostic](https://www.w3.org/TR/WGSL/#diagnostics) named `builtin_shadowing` if an top level item is declared that is known to conflict with a predeclared identifier. This also helps in the non-library case.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand this right, that this error only occurs when a library calls their published function "unstable_thing()"?
So does it only give out warnings for conflicts between naga features and library glob imports?

Comment thread Imports.md Outdated
// Requires opt-in
@diagnostic(off, wildcard_import)
import lygia::math::*;
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think examples that would show what is not allowed would be appropriate here, since the above text is about things that are not allowed.

@Arne-Berner
Copy link
Copy Markdown

I've pushed a new version of the proposal, but now that I've written out the longer diagnostics option, I'm not entirely convinced that it is the best solution.

The part about glob imports in libraries causing issues is preventable by making the globs explicit upon publishing. As in, if bevy has

import stefnotch_lib::*;
import evil_lib::*;

then when publishing bevy, we could expand the globs. (import stefnotch_lib::{foo, bar}; import evil_lib::{qux};) In which case no matter what evil_lib does, it can't break bevy.

In user code, I don't think I care too much about a glob import that has just broken. It is an easy fix for me.

The part about accidentally shadowing a builtin seems impossible to fully prevent without #185

I'm not sure I understand this correctly, so I hope it's fine to ask:
What exactly do you mean by publishing bevy? Is this the same as building the package via build.rs or wesl package?
That would mean, it is not user facing code, correct? I would write something like

import stefnotch_lib::*;
import evil_lib::*;

And the user of bevy would not get to see this:

import stefnotch_lib::{foo, bar}; 
import evil_lib::{qux};

Unless they are looking at the definition of a bevy function they use in their library?
If so I was assuming that the proposal worked like that already and only libraries that were directly used by me would be affected by adding functions. If any node in the dependency tree would always publish all the wildcards imported I do see your problem of it being risky. The probability for such a conflict would be much higher than I thought.

@stefnotch
Copy link
Copy Markdown
Collaborator Author

@Arne-Berner
It's the same as wesl package. Rust doesn't have a clear cut equivalent, because build scripts are executed on the machine of whoever uses the library.

And yes, that is a correct description. The name clashes caused by wildcards can happen at any place in the dependency tree.

@mighdoll
Copy link
Copy Markdown
Contributor

In user code, I don't think I care too much about a glob import that has just broken. It is an easy fix for me.

sure, for you it's easy :-).

But we can't expect that the user handling errors after an update is an aspiring WESL programmer sitting in their IDE who's read our blog post on how handle glob related errors.. In the js world, npm update, npm user expectations, npm dependency tools, etc. all treat patch/minor failures as upstream bugs. We can't hope that the npm ecosystem will change that expectation for WebGPU.

Imagine the user running npm (or cargo) update is not the shader programmer on their team. Or imagine a web dev team, updating html statement loading a shader library from a CDN (no package mgr, no IDE). Or imagine a bot trained to handle npm updates and classify upstream bugs. In all these cases, they're not well positioned (or expecting) to make shader fixes - they just want to get the security update or stay up to date or whatever.

AFAICT, we need patch/minor updates to succeed for default users. It's desirable anyway, but also a consequence of choosing to cross publish libraries to the npm ecosystem.

@Arne-Berner
Copy link
Copy Markdown

In user code, I don't think I care too much about a glob import that has just broken. It is an easy fix for me.

sure, for you it's easy :-).

But we can't expect that the user handling errors after an update is an aspiring WESL programmer sitting in their IDE who's read our blog post on how handle glob related errors.. In the js world, npm update, npm user expectations, npm dependency tools, etc. all treat patch/minor failures as upstream bugs. We can't hope that the npm ecosystem will change that expectation for WebGPU.

Isn't that in the context of using warn diagnostics when importing two libraries via wildcard? So someone would have to have used the "don't warn me about that" before it's relevant. And at that point it would mean that it's not an upstream library that has to change one wildcard import and you have to wait for them to fix your problem, but you can fix it yourself.

Imagine the user running npm (or cargo) update is not the shader programmer on their team. Or imagine a web dev team, updating html statement loading a shader library from a CDN (no package mgr, no IDE). Or imagine a bot trained to handle npm updates and classify upstream bugs. In all these cases, they're not well positioned (or expecting) to make shader fixes - they just want to get the security update or stay up to date or whatever.

I have only worked as a JS/TS dev for about halve a year, but if some package that is needed by the shader programmer breaks the whole library I think most people would pin that library and update the security issue in another library.
I do think that you are right and this is an issue that could be a problem for certain workflows. But I am not sure if it is a dealbreaker for most people instead of a compromise for incorporating a different programming language into js.
And wesl is another programming language than javascript and npm has not been optimized for wesl.

Lastly I would argue that people who get used to something like this:

[] + []        // ""
[] + {}        // "[object Object]"
{} + []        // 0   (in many consoles this is parsed as a block + unary +)
"5" - 1        // 4
"5" + 1        // "51"

Might tolerate a very rare breaking change on npm update for a pure wesl library that has a security patch, whilst the responsible developer is not there.

@mighdoll
Copy link
Copy Markdown
Contributor

mighdoll commented May 5, 2026

touch both poles of a car battery without any danger

What!? But I was always warned against that!

@laundmo
Copy link
Copy Markdown

laundmo commented May 5, 2026

Exactly! Now, to be sure, shorting them out would be dangerous but 12-13V aint enough to overcome the resistance of your skin. Its barely more than 9V and people lick those to see if theybhave power.

Anyways: With the requisite knowledge come risks you know you can safely take, and warnings you can bypass. Thats the nature of warnings: if you're knowledgable and careful, ignoring specific ones becomes safe. So, my point is, let the warnings exist for those who don't know they need them, those who know what they're doing won't bat an eye at having to bypass them.

(i... might be getting a bit too off-topic and oddly dramatic here. sorry. i feel like i've said my piece now)

@mighdoll
Copy link
Copy Markdown
Contributor

mighdoll commented May 5, 2026

Exactly! Now, to be sure, shorting them out would be dangerous but 12-13V aint enough to overcome the resistance of your skin. Its barely more than 9V and people lick those to see if theybhave power.

Yeah, future readers, please don't try this with something metal. Or on your electric car. Best really not to take any car advice at all from gpu tooling projects.

i think that may be stretching the definition of "happy path" a bit too far.

I'm tempted to debate further on the happy path philosophy - guiding principles are helpful to figure out. But let me fold here for now, and follow your suggestion for the next draft of this PR. And here's one more argument for making builtin_shadow a client side warning as you suggest: we can collect feedback and if we're wrong, we can loosen the rules later (but tightening the rules later is harder because it breaks existing code).

Copy link
Copy Markdown

@laundmo laundmo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never said to remove builtin_shadow from the author side...

Comment thread Imports.md Outdated
**Don't shadow WGSL builtins.** Names like `vec3`, `clamp`, `inverseSqrt` have
expected semantics that oughtn't be implicitly overridden with wildcards.
Similarly, avoid experimental Naga/Dawn/Safari builtins.
- WESL publishing tools should warn when a `@wildcardable` module exports an
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hold up, my point was never that it should only show on client side, but that it should also show on client side.

I don't like this being removed from author side either.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable to warn in the publishing tool too. Restored.

Comment thread Imports.md Outdated
| Wildcard import conflicts with wildcard import (when name is referenced) | Error |
| Wildcard import from a non-`@wildcardable` external module | Error (`wildcard_import`); suppressible |
| Local declaration or named import shadows a wildcard-imported name | Warning (`wildcard_shadow`); suppressible |
| Wildcard import shadows a WGSL builtin | Warning (`builtin_shadow`); suppressible |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be a bit clearer when it shows, namely, on imports.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed rev

Comment thread Imports.md Outdated

- **`builtin_shadow`** fires when a wildcard import shadows a WGSL builtin such
as `vec3` or `clamp`. Suppress at the import site if the override is
intentional; the suppression itself documents documents to readers that the
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
intentional; the suppression itself documents documents to readers that the
intentional; the suppression itself documents to readers that the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx fixed.

@mighdoll
Copy link
Copy Markdown
Contributor

mighdoll commented May 7, 2026

A few more syntax ideas from the discord discussion for the module metadata syntax, for wildcardable and beyond:

import wgsl_play::{play_version, range};

// module metadata syntax options:
module wildcardable, play_version(17);   // module never @ 
module @wildcardable, @play_version(17); // module always @
module wildcardable, @play_version(17);  // module mixed @
@play_version(17) module wildcardable;   // @ extensions then module
@wildcardable @play_version(17) module;  // @ all then module
@!wildcardable; @!play_version(17);      // @! instead of module or just @

// item metadata will look like this:
struct Uniforms {
  @range(3,5) num_balloons: u32;
}

They all work, we're syntax tuning.

In addition to the other syntax comments, some more nice to haves:

  • It's nice if the @phrase always attaches to the subsequent element.
  • It's nice if user/lib metadata is always @ prefixed for both item or module metadata.
  • It's nice if user/lib metadata is syntax similar to WESL metadata like wildcardable.
  • Plain module wildcardable; looks nice, and syntax analogous to e.g., enable f16;
  • Explicit syntax is nicer for the part time shader programmers WESL targets.
  • Nice not to limit the future use of the module keyword is nice.

We can't have all of those things of course.


At time of writing, I lean towards options where both @wildcardable and @play_version have an @ prefix, so that we have consistency for item library metadata, module library metadata, and module WESL metadata.

This idea (via @stefnotch):

@wildcardable @play_version(17) module;

has the additional advantage that the module keyword is still relatively usable for future features, and keeps with the current WGSL pattern that an @phrase is always an element annotation.

mighdoll added 2 commits May 10, 2026 22:04
- add `attribute*` to `import_statement` so `@diagnostic` is grammatical on imports
- `module` becomes a reserved keyword
- rationale for @wildcardable syntax
- rationale for @wildcardable design
@stefnotch
Copy link
Copy Markdown
Collaborator Author

Bikeshedding question: should module attributes come before or after import statements? For what it is worth, Rust does usually place them before import statements.

@BenjaminBrienen
Copy link
Copy Markdown
Contributor

I think the order doesn't matter.

@mighdoll
Copy link
Copy Markdown
Contributor

Bikeshedding question: should module attributes come before or after import statements? For what it is worth, Rust does usually place them before import statements.

For built-in attributes like @wildcardable, either order is reasonable:

@wildcardable module;

import foo::bar;

But if module is going to collect custom attributes from libs and apps, I think module probably wants to land after the imports so that the referent comes first:

import wgsl_play::play_version;

@play_version("0.9") module;

The reverse order would either make imported attribute names unavailable there, or break the expectation that imports come before use of imported names. Seems better to let the module declaration live after imports along with other WGSL global_directives like diagnostic.

Aside from attributes, it's nice stay flexible for future module syntax that might also want to reference imports. e.g. we could decide for @param const:

import super::util::Foo;

@param const x = Foo(3,4);

to instead use a syntax like:

import super::util::Foo;

module {
  const x = Foo(3,4);
}

Of course, users can use the fully qualified form too if they prefer: @wgsl_play::play_version("0.9") module;, super::util::Foo(3,4); .

@stefnotch
Copy link
Copy Markdown
Collaborator Author

or break the expectation that imports come before use of imported names

Just wanted to point out that WGSL top level items are unordered. So as a WGSL programmer, I don't actually have the expectation that imports must be specified before the names are used. I only expect that usual code will put the imports at the top. Which is why WESL has the current grammar restriction where the imports are before everything else.

But don't let this distract you from pushing forward with the proposal. I'm happy with module level attributes appearing after import statements.

@k2d222
Copy link
Copy Markdown
Contributor

k2d222 commented May 12, 2026

I'm happy with this: @wildcardable @play_version(17) module;.

This module { const x = Foo(3,4); } looks nice too, but is perhaps looking too much ahead. The above one is simpler IMO.

Copy link
Copy Markdown
Contributor

@k2d222 k2d222 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's in a good shape!

Comment thread Imports.md

A wildcard import imports all top-level declarations from a module. Submodule names and submodule contents are not imported.

WESL also extends WGSL's `global_directive` rule with a `module_declaration` form, used by `@wildcardable` (see [Wildcard imports](#wildcard-imports)) and reserved for future module-level metadata. `attribute` is the WGSL attribute rule.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • add: there can be at most one module declaration per file (?)

or would it make sense to allow multiple?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change to clarify just one for now. It's not really necessary to have more than one yet afaict, and we can relax to multiple later (or not if we introduce other future semantics on module that mean we don't want two).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just one, clarified in the PR

Comment thread Imports.md
Comment thread Imports.md
the user may see a conflict in library code they don't expect to modify.

WESL library publishing tools will address this by expanding wildcards to named
imports in the published version of the module:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I got confused / forgot about this issue. Yeah it sounds like it has to be a pre-publish process and that's currently not the case. I'd need to think about how to achieve this.

Comment thread Imports.md Outdated
the import statement to accept the upgrade risk that future versions of the
imported module may add conflicting names.

- **`wildcard_shadow`** fires when a local declaration or named import shadows a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • add like above: "suppress with ... (how?) on ... (where?)"

where: on the local declaration I suppose?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question. I think you're right.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clarified in the PR

Comment thread Imports.md Outdated
Comment on lines +405 to +409
- **`builtin_shadow`** fires on a wildcard import when a referenced name in the
module resolves to a wildcard-imported item that shadows a WGSL builtin such
as `vec3` or `clamp`. Suppress at the import site if the override is
intentional; the suppression itself documents to readers that the builtins
have changed semantics.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it slightly ackward that the consumer has to silence that warning, and not the wildcard module author. WGSL itself has no warning for shadowing built-ins, and I think it would make more sense if we added that warning in WESL, not just for wildcards, and it would be up to the wildcard module author to silence this.

I would not push against consumers also needing the warning, I just find it not necessary.

I would like defer this to a subsequent PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me, can be be a separate PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separated consumer side warning to PR #188 for us to consider at leisure.

Comment thread ImportsDesign.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

Support import package::*; to import all items in a file

7 participants