Skip to content

Commit 8d7a3df

Browse files
authored
Merge pull request #332 from WebAssembly/add-wit-gates
Add @feature and @SInCE gates to WIT
2 parents 72219d8 + 8d75db5 commit 8d7a3df

File tree

1 file changed

+140
-8
lines changed

1 file changed

+140
-8
lines changed

design/mvp/WIT.md

Lines changed: 140 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,83 @@ Concretely, the structure of a `wit` file is:
880880
wit-file ::= package-decl? (toplevel-use-item | interface-item | world-item)*
881881
```
882882

883+
### Feature Gates
884+
885+
Various WIT items can be "gated", to reflect the fact that the item is part of
886+
an unstable feature or that the item was added as part of a minor version
887+
update and shouldn't be used when targeting an earlier minor version.
888+
889+
For example, the following interface has 4 items, 3 of which are gated:
890+
```wit
891+
interface foo {
892+
a: func();
893+
894+
@since(version = 0.2.1)
895+
b: func();
896+
897+
@since(version = 0.2.2, feature = fancy-foo)
898+
c: func();
899+
900+
@unstable(feature = fancier-foo)
901+
d: func();
902+
}
903+
```
904+
The `@since` gate indicates that `b` and `c` were added as part of the `0.2.1`
905+
and `0.2.2` releases, resp. Thus, when building a component targeting, e.g.,
906+
`0.2.1`, `b` can be used, but `c` cannot. An important expectation set by the
907+
`@since` gate is that, once applied to an item, the item is not modified
908+
incompatibly going forward (according to general semantic versioning rules).
909+
910+
In contrast, the `@unstable` gate on `d` indicates that `d` is part of the
911+
`fancier-foo` feature that is still under active development and thus `d` may
912+
change type or be removed at any time. An important expectation set by the
913+
`@unstable` gate is that toolchains will not expose `@unstable` features by
914+
default unless explicitly opted-into by the developer.
915+
916+
Together, these gates support a development flow in which new features start
917+
with an `@unstable` gate while the details are still being hashed out. Then,
918+
once the feature is stable (and, in a WASI context, voted upon), the
919+
`@unstable` gate is switched to a `@since` gate. To enable a smooth transition
920+
(during which producer toolchains are targeting a version earlier than the
921+
`@since`-specified `version`), the `@since` gate contains an optional `feature`
922+
field that, when present, says to enable the feature when *either* the target
923+
version is greator-or-equal *or* the feature name is explicitly enabled by the
924+
developer. Thus, `c` is enabled if the version is `0.2.2` or newer or the
925+
`fancy-foo` feature is explicitly enabled by the developer. The `feature` field
926+
can be removed once producer toolchains have updated their default version to
927+
enable the feature by default.
928+
929+
Specifically, the syntax for feature gates is:
930+
```wit
931+
gate ::= unstable-gate
932+
| since-gate
933+
unstable-gate ::= '@unstable' '(' feature-field ')'
934+
feature-field ::= 'feature' '=' id
935+
since-gate ::= '@since' '(' 'version' '=' <valid semver> ( ',' feature-field )? ')'
936+
```
937+
938+
As part of WIT validation, any item that refers to another gated item must also
939+
be compatibly gated. For example, this is an error:
940+
```wit
941+
interface i {
942+
@since(version = 1.0.1)
943+
type t1 = u32;
944+
945+
type t2 = t1; // error
946+
}
947+
```
948+
Additionally, if an item is *contained* by a gated item, it must also be
949+
compatibly gated. For example, this is an error:
950+
```wit
951+
@since(version = 1.0.2)
952+
interface i {
953+
foo: func(); // error: no gate
954+
955+
@since(version = 1.0.1)
956+
bar: func(); // also error: weaker gate
957+
}
958+
```
959+
883960
## Package declaration
884961
[package declaration]: #package-declaration
885962

@@ -922,14 +999,21 @@ nesting both namespaces and packages, which would then generalize the syntax of
922999

9231000
## Item: `world`
9241001

925-
Worlds define a [componenttype](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md#type-definitions) as a collection of imports and exports.
1002+
Worlds define a [`componenttype`] as a collection of imports and exports, all
1003+
of which can be gated.
9261004

9271005
Concretely, the structure of a world is:
9281006

9291007
```ebnf
930-
world-item ::= 'world' id '{' world-items* '}'
1008+
world-item ::= gate 'world' id '{' world-items* '}'
9311009
932-
world-items ::= export-item | import-item | use-item | typedef-item | include-item
1010+
world-items ::= gate world-definition
1011+
1012+
world-definition ::= export-item
1013+
| import-item
1014+
| use-item
1015+
| typedef-item
1016+
| include-item
9331017
9341018
export-item ::= 'export' id ':' extern-type
9351019
| 'export' use-path ';'
@@ -944,6 +1028,8 @@ from the root of a component and used within functions imported and exported.
9441028
The `interface` item here additionally defines the grammar for IDs used to refer
9451029
to `interface` items.
9461030

1031+
[`componenttype`]: Explainer.md#type-definitions
1032+
9471033
## Item: `include`
9481034

9491035
A `include` statement enables the union of the current world with another world. The structure of an `include` statement is:
@@ -966,18 +1052,20 @@ include-names-item ::= id 'as' id
9661052
## Item: `interface`
9671053

9681054
Interfaces can be defined in a `wit` file. Interfaces have a name and a
969-
sequence of items and functions.
1055+
sequence of items and functions, all of which can be gated.
9701056

9711057
Specifically interfaces have the structure:
9721058

9731059
> **Note**: The symbol `ε`, also known as Epsilon, denotes an empty string.
9741060
9751061
```ebnf
976-
interface-item ::= 'interface' id '{' interface-items* '}'
1062+
interface-item ::= gate 'interface' id '{' interface-items* '}'
9771063
978-
interface-items ::= typedef-item
979-
| use-item
980-
| func-item
1064+
interface-items ::= gate interface-definition
1065+
1066+
interface-definition ::= typedef-item
1067+
| use-item
1068+
| func-item
9811069
9821070
typedef-item ::= resource-item
9831071
| variant-items
@@ -1002,6 +1090,7 @@ named-type-list ::= ϵ
10021090
named-type ::= id ':' ty
10031091
```
10041092

1093+
10051094
## Item: `use`
10061095

10071096
A `use` statement enables importing type or resource definitions from other
@@ -1631,3 +1720,46 @@ standalone interface definitions (such `wasi:http/handler`) are no longer in a
16311720
`use`s are replaced by direct aliases to preceding type imports as determined
16321721
by the WIT resolution process.
16331722

1723+
Unlike most other WIT constructs, the `@since` and `@unstable` gates are not
1724+
represented in the component binary. Instead, they are considered "macro"
1725+
constructs that take the place of maintaining two copies of a single WIT
1726+
document. In particular, when encoding a collection of WIT documents into a
1727+
binary, the target version and set of explicitly-enabled feature names
1728+
determine whether individual gated features are included in the encoded type or
1729+
not.
1730+
1731+
For example, the following WIT document:
1732+
```wit
1733+
package ns:[email protected];
1734+
1735+
interface i {
1736+
f: func();
1737+
1738+
@since(version = 1.1.0)
1739+
g: func();
1740+
}
1741+
```
1742+
is encoded as the following component when the target version is `1.0.0`:
1743+
```wat
1744+
(component
1745+
(type (export "i") (component
1746+
(export "ns:p/[email protected]" (instance
1747+
(export "f" (func))
1748+
))
1749+
))
1750+
)
1751+
```
1752+
If the target version was instead `1.1.0`, the same WIT document would be
1753+
encoded as:
1754+
```wat
1755+
(component
1756+
(type (export "i") (component
1757+
(export "ns:p/[email protected]" (instance
1758+
(export "f" (func))
1759+
(export "g" (func))
1760+
))
1761+
))
1762+
)
1763+
```
1764+
Thus, `@since` and `@unstable` gates are not part of the runtime semantics of
1765+
components, just part of the source-level tooling for producing components.

0 commit comments

Comments
 (0)