Skip to content
Open
Show file tree
Hide file tree
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
14 changes: 9 additions & 5 deletions design/mvp/Binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,15 @@ Notes:
[text format](Explainer.md#import-and-export-definitions).
* The `<importname>`s of a component must all be [strongly-unique]. Separately,
the `<exportname>`s of a component must also all be [strongly-unique].
* Validation requires that annotated `plainname`s only occur on `func` imports
or exports and that the first label of a `[constructor]`, `[method]` or
`[static]` matches the `plainname` of a preceding `resource` import or
export, respectively, in the same scope (component, component type or
instance type).
* Validation requires that `[constructor]`, `[method]` and `[static]` annotated
`plainname`s only occur on `func` imports or exports and that the first label
of a `[constructor]`, `[method]` or `[static]` matches the `plainname` of a
preceding `resource` import or export, respectively, in the same scope
(component, component type or instance type).
* 🏷️ Validation requires that `[implements=<I>]` annotated `plainname`s only
occur on `instance` imports or exports.
* 🏷️ Validation requires that `interfacename`-named imports or exports are
`instance`-typed.
* Validation of `[constructor]` names requires a `func` type whose result type
is either `(own $R)` or `(result (own $R) E?)` where `$R` is a resource type
labeled `r`.
Expand Down
33 changes: 30 additions & 3 deletions design/mvp/Explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ implemented, considered stable and included in a future milestone:
* 🔧: fixed-length lists
* 📝: the `error-context` type
* 🔗: canonical interface names
* 🏷️: `[implements]` annotations for plain-named interface imports/exports

(Based on the previous [scoping and layering] proposal to the WebAssembly CG,
this repo merges and supersedes the [module-linking] and [interface-types]
Expand Down Expand Up @@ -2538,6 +2539,7 @@ plainname ::= <label>
| '[constructor]' <label>
| '[method]' <label> '.' <label>
| '[static]' <label> '.' <label>
| '[implements=<' <interfacename> '>]' <label> 🏷️
label ::= <first-fragment> ( '-' <fragment> )*
first-fragment ::= [a-z] <word>
| [A-Z] <acronym>
Expand Down Expand Up @@ -2683,6 +2685,31 @@ annotations trigger additional type-validation rules (listed in
* Similarly, an import or export named `[method]R.foo` must be a function whose
first parameter must be `(param "self" (borrow $R))`.

🏷️ When an instance import or export is annotated with `[implements=<I>]L`, it
indicates that the instance implements interface `I` but is given the plain
name `L`. This enables a component to import or export the same interface
multiple times with different plain names. For example:

```wat
(component
(import "[implements=<wasi:keyvalue/store>]primary" (instance ...))
(import "[implements=<wasi:keyvalue/store>]secondary" (instance ...))
)
```

Here, both imports implement `wasi:keyvalue/store` but have distinct plain
names `primary` and `secondary`. Bindings generators can use the
`[implements=<I>]` annotation to know which interface the instance implements,
enabling them to share value type bindings across both imports. (Note that
resource types defined in the interface, such as `bucket`, are treated as
distinct for each import, since each may have a different implementation.)

The `interfacename` also helps hosts and clients of a component. A host that
sees `[implements=<wasi:keyvalue/store>]primary` knows to supply a
`wasi:keyvalue/store` implementation for that import, even though the import
name is just `primary`. Similarly, a client composing components can use the
annotation to match compatible imports and exports across components.

When a function's type is `async`, bindings generators are expected to
emit whatever asynchronous language construct is appropriate (such as an
`async` function in JS, Python or Rust). See the [concurrency explainer] for
Expand Down Expand Up @@ -2824,11 +2851,11 @@ Values]) are **strongly-unique**:
* Strip any `[...]` annotation prefix from both names.
* The names are strongly-unique if the resulting strings are unequal.

Thus, the following names are strongly-unique:
* `foo`, `foo-bar`, `[constructor]foo`, `[method]foo.bar`, `[method]foo.baz`
Thus, the following set of names are strongly-unique and can thus all be imports (or exports) of the same component (or component type or instance type):
* `foo`, `foo-bar`, `[constructor]foo`, `[method]foo.bar`, `[method]foo.baz`, `foo:bar/baz`, `[implements=<foo:bar/baz>]bar`, `[implements=<foo:bar/baz>]quux`

but attempting to add *any* of the following names would be a validation error:
* `foo`, `foo-BAR`, `[constructor]foo-BAR`, `[method]foo.foo`, `[method]foo.BAR`
* `foo`, `foo-BAR`, `[constructor]foo-BAR`, `[method]foo.foo`, `[method]foo.BAR`, `[implements=<a:b/c>]foo`, `foo:bar/baz`, `bar`, `[implements=<x:y/z>]bar`

Note that additional validation rules involving types apply to names with
annotations. For example, the validation rules for `[constructor]foo` require
Expand Down
120 changes: 118 additions & 2 deletions design/mvp/WIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ document, a pseudo-formal [grammar specification][lexical-structure], and
additionally a specification of the [package format][package-format] of a WIT
package suitable for distribution.

See [Gated Features] for an explanation of 🔧.
See [Gated Features] for an explanation of 🔧 and 🏷️.

[IDL]: https://en.wikipedia.org/wiki/Interface_description_language
[components]: https://github.com/webassembly/component-model
Expand Down Expand Up @@ -366,6 +366,55 @@ world union-my-world-b {
}
```

🏷️ When a world being included contains plain-named imports or exports that
reference a named interface (using the `id: use-path` syntax), the `with`
keyword renames the plain-name label while preserving the underlying
`[implements=<I>]` annotation in the encoding. For example:

```wit
package local:demo;

interface store {
get: func(key: string) -> option<string>;
}

world base {
import cache: store;
}

world extended {
include base with { cache as my-cache }
Copy link
Member

Choose a reason for hiding this comment

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

Thinking more about this example/rule in the context of duplicate-name-conflicts: could you expand this example to have two includes that contain a conflict for the plain-name cache and explicitly mention that this would be an error without the with (b/c we can't de-duplicate plain names like we can with pure interface names).

}
```

In this case, `extended` has a single import with the plain name `my-cache`
that implements `local:demo/store`, equivalent to writing
`import my-cache: store;` directly.

Unlike interface names (which are automatically de-duplicated when two
`include`s import the same interface), plain names cannot be de-duplicated
and will conflict. For example:

```wit
world base-a {
import cache: store;
}

world base-b {
import cache: store;
}

world conflict {
include base-a;
include base-b; // error: plain name 'cache' conflicts
}

world resolved {
include base-a;
include base-b with { cache as other-cache } // ok: renamed to avoid conflict
}
```

`with` cannot be used to rename interface names, however, so the following
world would be invalid:
```wit
Expand Down Expand Up @@ -1381,9 +1430,30 @@ export-item ::= 'export' id ':' extern-type
import-item ::= 'import' id ':' extern-type
| 'import' use-path ';'

extern-type ::= func-type ';' | 'interface' '{' interface-items* '}'
extern-type ::= func-type ';' | 'interface' '{' interface-items* '}' | use-path ';' 🏷️
```

🏷️ The third case of `extern-type` allows a named interface to be imported or
exported with a custom [plain name]. For example:

```wit
world my-world {
import primary: wasi:keyvalue/store;
import secondary: wasi:keyvalue/store;
export my-handler: wasi:http/handler;
}
```

Here, `primary` and `secondary` are two distinct imports that both have the
instance type of the `wasi:keyvalue/store` interface. The plain name of the
import is the `id` before the colon (e.g., `primary`), not the interface name.
This contrasts with `import wasi:keyvalue/store;` (without the `id :` prefix),
which would create a single import using the full interface name
`wasi:keyvalue/store`. Similarly, the export `my-handler` has the instance type
of `wasi:http/handler` but uses the plain name `my-handler` instead of the full
interface name, which is useful when a component wants to export the same
interface multiple times or simply use a more descriptive name.

Note that worlds can import types and define their own types to be exported
from the root of a component and used within functions imported and exported.
The `interface` item here additionally defines the grammar for IDs used to refer
Expand Down Expand Up @@ -2061,6 +2131,52 @@ This duplication is useful in the case of cross-package references or split
packages, allowing a compiled `world` definition to be fully self-contained and
able to be used to compile a component without additional type information.

🏷️ When a world imports or exports a named interface with a custom plain name
(using the `id: use-path` syntax), the encoding uses the `[implements=<I>]`
annotation defined in [Explainer.md](Explainer.md#import-and-export-definitions) to indicate which
interface the instance implements. For example, the following WIT:

```wit
package local:demo;

interface store {
get: func(key: string) -> option<string>;
}

world w {
import one: store;
import two: store;
}
```

is encoded as:

```wat
(component
(type (export "w") (component
(export "local:demo/w" (component
(import "[implements=<local:demo/store>]one" (instance
(export "get" (func (param "key" string) (result (option string))))
))
(import "[implements=<local:demo/store>]two" (instance
(export "get" (func (param "key" string) (result (option string))))
))
))
))
(type (export "store") (component
(export "local:demo/store" (instance
(export "get" (func (param "key" string) (result (option string))))
))
))
)
```

The `[implements=<local:demo/store>]` prefix tells bindings generators and
toolchains which interface each plain-named instance import implements, while
the labels `one` and `two` provide distinct plain names. This is a case of
the general `[implements=<interfacename>]label` pattern described in
[Explainer.md](Explainer.md#import-and-export-definitions).

Putting this all together, the following WIT definitions:

```wit
Expand Down
Loading