Skip to content
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

Add explicit scenarios for feature gate usage #387

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 164 additions & 9 deletions design/mvp/WIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ interface a {
resource r;
}
interface b {
use a.{r};
use a.{r};
foo: func() -> r;
}

Expand Down Expand Up @@ -946,33 +946,36 @@ required to always be paired up with either a `@since` or `@deprecated` gate.
Together, these gates support a development flow in which new features start
with an `@unstable` gate while the details are still being hashed out. Then,
once the feature is stable (and, in a WASI context, voted upon), the
`@unstable` gate is switched to a `@since` gate. To enable a smooth transition
(during which producer toolchains are targeting a version earlier than the
`@since`-specified `version`), the `@since` gate contains an optional `feature`
field that, when present, says to enable the feature when *either* the target
version is greator-or-equal *or* the feature name is explicitly enabled by the
developer. Thus, `c` is enabled if the version is `0.2.2` or newer or the
`@unstable` gate is switched to a `@since` gate.

Thus, `c` is enabled if the version is `0.2.2` or newer or the
`fancy-foo` feature is explicitly enabled by the developer. The `feature` field
can be removed once producer toolchains have updated their default version to
enable the feature by default.

Specifically, the syntax for feature gates is:
#### Feature gate syntax

The grammar that governs feature gate syntax is:

```wit
gate ::= gate-item*
gate-item ::= unstable-gate
| since-gate
| deprecated-gate

unstable-gate ::= '@unstable' '(' feature-field ')'
since-gate ::= '@since' '(' version-field ( ',' feature-field )? ')'
since-gate ::= '@since' '(' version-field ')'
deprecated-gate ::= '@deprecated' '(' version-field ')'

feature-field ::= 'feature' '=' id
version-field ::= 'version' '=' <valid semver>
```

#### Rules for feature gate usage

As part of WIT validation, any item that refers to another gated item must also
be compatibly gated. For example, this is an error:

```wit
interface i {
@since(version = 1.0.1)
Expand All @@ -981,6 +984,7 @@ interface i {
type t2 = t1; // error
}
```

Additionally, if an item is *contained* by a gated item, it must also be
compatibly gated. For example, this is an error:
```wit
Expand All @@ -993,6 +997,157 @@ interface i {
}
```

The following rules apply to the use of feature gates:

- Either `@since` *or* `@unstable` should be used, but not both (exclusive or).
- If a package contains a feature gate, it's version must be specified (i.e. `namespace:package@x.y.z`)

#### Scenario: Stabilization of a new feature

This section lays out the basic flow and expected usage of feature gate machinery
when stabilizing new features and deprecating old ones.

Assume the following WIT package as the initial interface:

```wit
package examples:fgates-calc@0.1.0;

@since(version = 0.1.0)
interface calc {
@since(version = 0.1.0)
variant calc-error {
integer-overflow,
integer-underflow,
unexpected,
}

@since(version = 0.1.0)
add: func(x: i32, y: i32) -> result<i32, calc-error>;
}
```

**First, add new items under an `@unstable` annotation with a `feature` specified:**

```wit
package examples:fgates-calc@0.1.1;

@since(version = 0.1.0)
interface calc {
@since(version = 0.1.0)
variant calc-error {
integer-overflow,
integer-underflow,
unexpected,
}

@since(version = 0.1.0)
add: func(x: i32, y: i32) -> result<i32, calc-error>;

/// By convention, feature flags should be prefixed with package name to reduce chance of collisions
///
/// see: https://github.com/WebAssembly/WASI/blob/main/Contributing.md#filing-changes-to-existing-phase-3-proposals
@unstable(feature = fgates-calc-minus)
sub: func(x: i32, y: i32) -> result<i32, calc-error>;
}
```

At this point, consumers of the WIT can enable feature `fgates-calc-minus` through their relevant tooling and get access to the `sub` function.

Note that, at least until subtyping is relaxed in the Component Model, if we had to *add* a new case to `calc-error`, this would be a *breaking change* and require either a new major version or adding a second, distinct `variant` definition used by new functions.

**Second, when the feature is ready to be stabilized, switch to a `@since` annotation:**

```wit
package examples:fgates-calc@0.1.2;

@since(version = 0.1.0)
interface calc {
@since(version = 0.1.0)
variant calc-error {
integer-overflow,
integer-underflow,
unexpected,
}

@since(version = 0.1.0)
add: func(x: i32, y: i32) -> result<i32, calc-error>;

@since(version = 0.1.2)
sub: func(x: i32, y: i32) -> result<i32, calc-error>;
}
```

#### Scenario: Deprecation of an existing stable feature

This section lays out the basic flow and expected usage of feature gate machinery when stabilizing a new feature.
vados-cosmonic marked this conversation as resolved.
Show resolved Hide resolved

Assume the following WIT package as the initial interface:

```wit
package examples:fgates-deprecation@0.1.1;

@since(version = 0.1.0)
interface calc {
@since(version = 0.1.0)
variant calc-error {
integer-overflow,
integer-underflow,
unexpected,
}

@since(version = 0.1.0)
add-one: func(x: i32) -> result<i32, calc-error>;
vados-cosmonic marked this conversation as resolved.
Show resolved Hide resolved

@since(version = 0.1.1)
add: func(x: i32, y: i32) -> result<i32, calc-error>;
}
```

**First: Add the `@deprecated` annotation to the relevant item in a new version**

```wit
package examples:fgates-deprecation@0.1.2;

@since(version = 0.1.0)
interface calc {
@since(version = 0.1.0)
variant calc-error {
integer-overflow,
integer-underflow,
unexpected,
}

@deprecated(version = 0.1.2)
add-one: func(x: i32) -> result<i32, calc-error>;

@since(version = 0.1.1)
add: func(x: i32, y: i32) -> result<i32, calc-error>;
}
```

At this point, tooling consuming this WIT will be able to appropriately alert users to the now-deprecated `add-one` function.

**Second: completely remove the deprecated item in some future SemVer-compliant major version**

```wit
package examples:fgates-deprecation@0.2.0;

@since(version = 0.1.0)
interface calc {
@since(version = 0.1.0)
variant calc-error {
integer-overflow,
integer-underflow,
unexpected,
}

@since(version = 0.1.1)
add: func(x: i32, y: i32) -> result<i32, calc-error>;
}
```

In this new "major" version (this is considered a major version under SemVer 0.X rules) -- the `add-one` function can be fully removed.

## Package declaration
[package declaration]: #package-declaration

Expand Down
Loading