-
Notifications
You must be signed in to change notification settings - Fork 92
Interface version canonicalization #536
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
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,7 @@ more user-focused explanation, take a look at the | |
* [Start definitions](#-start-definitions) | ||
* [Import and export definitions](#import-and-export-definitions) | ||
* [Name uniqueness](#name-uniqueness) | ||
* [Canonical interface name](#-canonical-interface-name) | ||
* [Component invariants](#component-invariants) | ||
* [JavaScript embedding](#JavaScript-embedding) | ||
* [JS API](#JS-API) | ||
|
@@ -55,6 +56,7 @@ implemented, considered stable and included in a future milestone: | |
* 🧵: threading built-ins | ||
* 🔧: fixed-length lists | ||
* 📝: the `error-context` type | ||
* 🔗: canonical interface names | ||
|
||
(Based on the previous [scoping and layering] proposal to the WebAssembly CG, | ||
this repo merges and supersedes the [module-linking] and [interface-types] | ||
|
@@ -294,7 +296,8 @@ sort ::= core <core:sort> | |
| type | ||
| component | ||
| instance | ||
inlineexport ::= (export <exportname> <sortidx>) | ||
inlineexport ::= (export "<exportname>" <sortidx>) | ||
| (export "<exportname>" <versionsuffix> <sortidx>) 🔗 | ||
``` | ||
Because component-level function, type and instance definitions are different | ||
than core-level function, type and instance definitions, they are put into | ||
|
@@ -574,8 +577,10 @@ instancedecl ::= core-prefix(<core:type>) | |
| <alias> | ||
| <exportdecl> | ||
| <value> 🪙 | ||
importdecl ::= (import <importname> bind-id(<externdesc>)) | ||
exportdecl ::= (export <exportname> bind-id(<externdesc>)) | ||
importdecl ::= (import "<importname>" bind-id(<externdesc>)) | ||
| (import "<importname>" <versionsuffix> bind-id(<externdesc>)) 🔗 | ||
exportdecl ::= (export "<exportname>" bind-id(<externdesc>)) | ||
| (export "<exportname>" <versionsuffix> bind-id(<externdesc>)) 🔗 | ||
externdesc ::= (<sort> (type <u32>) ) | ||
| core-prefix(<core:moduletype>) | ||
| <functype> | ||
|
@@ -988,6 +993,10 @@ and `$C1` is a subtype of `$C2`: | |
) | ||
``` | ||
|
||
🔗 Note that [canonical interface names](#-canonical-interface-name) may be | ||
annotated with a `versionsuffix` which is ignored for type checking except to | ||
improve diagnostic messages. | ||
|
||
When we next consider type imports and exports, there are two distinct | ||
subcases of `typebound` to consider: `eq` and `sub`. | ||
|
||
|
@@ -2242,8 +2251,11 @@ the identifier `$x`). In the case of exports, the `<id>?` right after the | |
preceding definition being exported (e.g., `(export $x "x" (func $f))` binds a | ||
new identifier `$x`). | ||
```ebnf | ||
import ::= (import "<importname>" bind-id(<externdesc>)) | ||
export ::= (export <id>? "<exportname>" <sortidx> <externdesc>?) | ||
import ::= (import "<importname>" bind-id(<externdesc>)) | ||
| (import "<importname>" <versionsuffix> bind-id(<externdesc>)) 🔗 | ||
export ::= (export <id>? "<exportname>" <sortidx> <externdesc>?) | ||
| (export <id>? "<exportname>" <versionsuffix> <sortidx> <externdesc>?) 🔗 | ||
versionsuffix ::= (versionsuffix "<semversuffix>") 🔗 | ||
``` | ||
All import names are required to be [strongly-unique]. Separately, all export | ||
names are also required to be [strongly-unique]. The rest of the grammar for | ||
|
@@ -2257,47 +2269,53 @@ interpreted as a *lexical* grammar defining a single token and thus whitespace | |
is not automatically inserted, all terminals are single-quoted, and everything | ||
unquoted is a meta-character. | ||
```ebnf | ||
exportname ::= <plainname> | ||
| <interfacename> | ||
importname ::= <exportname> | ||
| <depname> | ||
| <urlname> | ||
| <hashname> | ||
plainname ::= <label> | ||
| '[async]' <label> 🔀 | ||
| '[constructor]' <label> | ||
| '[method]' <label> '.' <label> | ||
| '[async method]' <label> '.' <label> 🔀 | ||
| '[static]' <label> '.' <label> | ||
| '[async static]' <label> '.' <label> 🔀 | ||
label ::= <fragment> | ||
| <label> '-' <fragment> | ||
fragment ::= <word> | ||
| <acronym> | ||
word ::= [a-z] [0-9a-z]* | ||
acronym ::= [A-Z] [0-9A-Z]* | ||
interfacename ::= <namespace> <label> <projection> <version>? | ||
| <namespace>+ <label> <projection>+ <version>? 🪺 | ||
namespace ::= <words> ':' | ||
words ::= <word> | ||
| <words> '-' <word> | ||
projection ::= '/' <label> | ||
version ::= '@' <valid semver> | ||
depname ::= 'unlocked-dep=<' <pkgnamequery> '>' | ||
| 'locked-dep=<' <pkgname> '>' ( ',' <hashname> )? | ||
pkgnamequery ::= <pkgpath> <verrange>? | ||
pkgname ::= <pkgpath> <version>? | ||
pkgpath ::= <namespace> <words> | ||
| <namespace>+ <words> <projection>* 🪺 | ||
verrange ::= '@*' | ||
| '@{' <verlower> '}' | ||
| '@{' <verupper> '}' | ||
| '@{' <verlower> ' ' <verupper> '}' | ||
verlower ::= '>=' <valid semver> | ||
verupper ::= '<' <valid semver> | ||
urlname ::= 'url=<' <nonbrackets> '>' (',' <hashname>)? | ||
nonbrackets ::= [^<>]* | ||
hashname ::= 'integrity=<' <integrity-metadata> '>' | ||
exportname ::= <plainname> | ||
| <interfacename> | ||
importname ::= <exportname> | ||
| <depname> | ||
| <urlname> | ||
| <hashname> | ||
plainname ::= <label> | ||
| '[async]' <label> 🔀 | ||
| '[constructor]' <label> | ||
| '[method]' <label> '.' <label> | ||
| '[async method]' <label> '.' <label> 🔀 | ||
| '[static]' <label> '.' <label> | ||
| '[async static]' <label> '.' <label> 🔀 | ||
label ::= <fragment> | ||
| <label> '-' <fragment> | ||
fragment ::= <word> | ||
| <acronym> | ||
word ::= [a-z] [0-9a-z]* | ||
acronym ::= [A-Z] [0-9A-Z]* | ||
interfacename ::= <namespace> <label> <projection> <interfaceversion>? | ||
| <namespace>+ <label> <projection>+ <interfaceversion>? 🪺 | ||
namespace ::= <words> ':' | ||
words ::= <word> | ||
| <words> '-' <word> | ||
projection ::= '/' <label> | ||
interfaceversion ::= '@' <valid semver> | ||
| '@' <canonversion> 🔗 | ||
canonversion ::= [1-9] [0-9]* 🔗 | ||
| '0.' [1-9] [0-9]* 🔗 | ||
| '0.0.' [1-9] [0-9]* 🔗 | ||
semversuffix ::= [0-9A-Za-z.+-]* 🔗 | ||
depname ::= 'unlocked-dep=<' <pkgnamequery> '>' | ||
| 'locked-dep=<' <pkgname> '>' ( ',' <hashname> )? | ||
pkgnamequery ::= <pkgpath> <verrange>? | ||
pkgname ::= <pkgpath> <pkgversion>? | ||
pkgversion ::= '@' <valid semver> | ||
pkgpath ::= <namespace> <words> | ||
| <namespace>+ <words> <projection>* 🪺 | ||
verrange ::= '@*' | ||
| '@{' <verlower> '}' | ||
| '@{' <verupper> '}' | ||
| '@{' <verlower> ' ' <verupper> '}' | ||
verlower ::= '>=' <valid semver> | ||
verupper ::= '<' <valid semver> | ||
urlname ::= 'url=<' <nonbrackets> '>' (',' <hashname>)? | ||
nonbrackets ::= [^<>]* | ||
hashname ::= 'integrity=<' <integrity-metadata> '>' | ||
``` | ||
Components provide six options for naming imports: | ||
* a **plain name** that leaves it up to the developer to "read the docs" | ||
|
@@ -2372,7 +2390,9 @@ tooling as "registries": | |
parameter of [`WebAssembly.instantiate()`]) | ||
|
||
The `valid semver` production is as defined by the [Semantic Versioning 2.0] | ||
spec and is meant to be interpreted according to that specification. The | ||
spec and is meant to be interpreted according to that specification. The use of | ||
`valid semver` in `interfaceversion` is temporary for backward compatibility; | ||
see [Canonical interface name](#-canonical-interface-name) below (🔗). The | ||
`verrange` production embeds a minimal subset of the syntax for version ranges | ||
found in common package managers like `npm` and `cargo` and is meant to be | ||
interpreted with the same [semantics][SemVerRange]. (Mostly this | ||
|
@@ -2539,6 +2559,53 @@ annotations. For example, the validation rules for `[constructor]foo` require | |
for details. | ||
|
||
|
||
### 🔗 Canonical Interface Name | ||
|
||
An `interfacename` (as defined above) is **canonical** iff it either: | ||
|
||
- has no `interfaceversion` | ||
- has an `interfaceversion` matching the `canonversion` production | ||
|
||
The purpose of `canonversion` is to simplify the matching of compatible import | ||
and export versions. For example, if a guest imports some interface from | ||
`wasi:http/[email protected]` and a host provides the (subtype-compatible) interface | ||
`wasi:http/[email protected]`, we'd like to make it easy for the host to link with the | ||
guest. The `canonversion` for both of these interfaces would be `0.2`, so this | ||
linking could be done by matching canonical interface names literally. | ||
lann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Symmetrically, if a host provides `wasi:http/[email protected]` and a guest imports | ||
`wasi:http/[email protected]`, so long as the guest only uses the subset of | ||
functions defined in `wasi:http/[email protected]` (which is checked by normal | ||
component type validation), linking succeeds. Thus, including only the | ||
canonicalized version in the name allows both backwards and (limited) | ||
forwards compatibility using only trivial string equality (as well as the | ||
type checking already required). | ||
|
||
Any `valid semver` (as used in WIT) can be canonicalized by splitting it into | ||
two parts - the `canonversion` prefix and the remaining `semversuffix`. Using | ||
the `<major>.<minor>.<patch>` syntax of [Semantic Versioning 2.0], the split | ||
point is chosen as follows: | ||
|
||
- if `major` > 0, split immediately after `major` | ||
- `1.2.3` → `1` / `.2.3` | ||
- otherwise if `minor` > 0, split immediately after `minor` | ||
- `0.2.6-rc.1` → `0.2` / `.6-rc.1` | ||
- otherwise, split immediately after `patch` | ||
- `0.0.1-alpha` → `0.0.1` / `-alpha` | ||
|
||
When a version is canonicalized, any `semversuffix` that was split off of the | ||
version should be preserved in the `versionsuffix` field of any resulting | ||
`import`s and `export`s. This gives component runtimes and other tools access to | ||
the original version for error messages, documentation, and other development | ||
purposes. Where a `versionsuffix` is present the preceding `interfacename` must | ||
have a `canonversion`, and the concatenation of the `canonversion` and | ||
`versionsuffix` must be a `valid semver`. | ||
|
||
For compatibility with older versions of this spec, non-canonical | ||
`interfacename`s (with `interfaceversion`s matching any `valid semver`) are | ||
temporarily permitted. These non-canonical names may trigger warnings and will | ||
start being rejected some time after after [WASI Preview 3] is released. | ||
|
||
|
||
## Component Invariants | ||
|
||
As a consequence of the shared-nothing design described above, all calls into | ||
|
@@ -2894,6 +2961,7 @@ For some use-case-focused, worked examples, see: | |
[`rectype`]: https://webassembly.github.io/gc/core/text/types.html#text-rectype | ||
[shared-everything-threads]: https://github.com/WebAssembly/shared-everything-threads | ||
[WASI Preview 2]: https://github.com/WebAssembly/WASI/tree/main/wasip2#readme | ||
[WASI Preview 3]: https://github.com/WebAssembly/WASI/tree/main/wasip2#looking-forward-to-preview-3 | ||
[reference types]: https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md | ||
|
||
[Strongly-unique]: #name-uniqueness | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
<valid semver>
wasn't referenced in this file.