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
32 changes: 26 additions & 6 deletions design/mvp/Binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,11 @@ Notes:
be [strongly-unique]; attributes are ignored.
* The `externname`s of all exports in a given component, instance, component-
type or instance-type must be [strongly-unique]; attributes are ignored.
* 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
* Validation requires that `plainname`s annotated with `[constructor]`,
`[method]`, `[static]`, 📡 `[get]`, or 📡 `[set]` only occur on `func` imports
or exports.
* Validation requires that, for `plainname`s annotated with `[constructor]`,
`[method]`, or `[static]`, the first `label` 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`-annotated imports or exports are
Expand All @@ -433,10 +435,28 @@ Notes:
`vec(<attribute>)`, this list is entirely ignored when validating the types
of components and instances.
* 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`.
is either `(own $R)` or `(result (own $R) (error $E)?)`, where `$R` is the
named resource type.
* Validation of `[method]` names requires the first parameter of the function
to be `(param "self" (borrow $R))`, where `$R` is the resource labeled `r`.
to be `(param "self" (borrow $R))`, where `$R` is the named resource type.
* 📡 Validation of `[get]` names requires that the function have no parameters,
unless the name is also annotated with `[method]`, in which case `self` must
be the only parameter.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
be the only parameter.
be the only parameter (subject to the `[method]` validation rules above).

* 📡 Validation of `[get]` names requires that the function have a result type.
* 📡 Validation of `[set]` names requires that the function have exactly one
parameter, unless the name is also annotated with `[method]`, in which case
there must be two parameters, the first of which is `self`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
there must be two parameters, the first of which is `self`.
there must be two parameters, the first of which is `self` (subject to the
`[method]` validation rules above).

* 📡 Validation of `[set]` names requires that the function have either no
result type or a result type of `(result (error $E)?)`.
* 📡 If a name with `[set]` is defined as an import or export within a
particular scope, the equivalent name with `[get]` must have already been
defined as an import or export respectively in that same scope—that is, all
labels must be equal (before canonicalization), and all annotations must be
the same except that `[set]` is replaced with `[get]`, and the `[get]`
import/export must precede the `[set]` import/export. For example,
`[set]prop` requires `[get]prop`, and `[method][set]foo.bar` requires
`[method][get]foo.bar`.
* 🔀/📡 Functions with `[get]` or `[set]` names must not be `async`.
* 🔗 Validation requires that `versionsuffix` is preceded by an `interfaceversion`
matching `canonversion` and that the concatenation of the `canonversion` and
the `versionsuffix` results in a `valid semver` as defined by
Expand Down
102 changes: 75 additions & 27 deletions design/mvp/Explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ shipped as part of a future WASI Developer Preview release:
* 📝: the `error-context` type
* 🔗: canonical interface names
* 🐘: [memory64]
* 📡: getters and setters


## Grammar
Expand Down Expand Up @@ -2708,9 +2709,11 @@ externnamelit ::= '"' <externname> '"'
externname ::= <plainname>
| <interfacename>
plainname ::= <label>
| '[get]' <label> 📡
| '[set]' <label> 📡
Comment on lines +2712 to +2713

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these 2 cases still intended and, if so, do they have a 3rd meaning distinct from [static] and [method]?

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.

Yes, these are the interface-level getters and setters we discussed for things like CSS.highlights. They are not associated with any resource type, so they have only a single label in their name. As I say that, though, I'm not entirely sure what our web bindings are expected to do in that case, so I should run this by Ryan before merging.

| '[constructor]' <label>
| '[method]' <label> '.' <label>
| '[static]' <label> '.' <label>
| '[method]' (📡 '[get]' | '[set]')? <label> '.' <label>
| '[static]' (📡 '[get]' | '[set]')? <label> '.' <label>
labellit ::= '"' <label> '"'
label ::= <first-fragment> ( '-' <fragment> )*
first-fragment ::= <first-word>
Expand Down Expand Up @@ -2745,11 +2748,12 @@ The names of component imports and exports provide two options:

The `plainname` production captures several language-neutral syntactic hints
that allow bindings generators to produce more idiomatic bindings in their
target language. At the top-level, a `plainname` allows functions to be
annotated as being a constructor, method or static function of a preceding
resource.
target language. At the top level, a `plainname` allows functions to be
annotated as being a constructor, method, 📡 property getter, 📡 property setter,
or static function of a preceding resource, as well as a 📡 property getter or
📡 property setter not attached to a resource.

When a function is annotated with `constructor`, `method` or `static`, the
When a function is annotated with `constructor`, `method`, or `static`, the
first `label` is the name of the resource and the second `label` is the logical
field name of the function. This additional nesting information allows bindings
generators to insert the function into the nested scope of a class, abstract
Expand All @@ -2759,16 +2763,21 @@ member function `foo` in a class `C`. The JS API [below](#JS-API) describes how
the native JavaScript bindings could look.

To restrict the set of cases that bindings generators need to consider, these
annotations trigger additional type-validation rules (listed in
[Binary.md](Binary.md)) such as:
* An import or export named `[static]R.foo` must be a function and `R` must
be the name of an imported or exported resource type in the same `instance`
or `component` type.
* Similarly, an import or export named `[constructor]R` must be a function
whose return type must be `(own $R)` or `(result (own $R) (error <valtype>)?)`
where `$R` is the type-index of the resource type named `R`.
* Similarly, an import or export named `[method]R.foo` must be a function whose
first parameter must be `(param "self" (borrow $R))`.
annotations trigger additional type-validation rules (listed in detail in
[Binary.md](Binary.md)):
* A `[constructor]` import or export named `R` must be a function whose result
type is `(own $R)` or `(result (own $R) (error <valtype>)?)`, where `$R` is
the index of the resource type named `R`.
* A `[method]` import or export named `R.foo` must be a function whose first
parameter is `(param "self" (borrow $R))`, where `$R` is the index of the
resource type named `R`.
* A `[static]` import or export named `R.foo` must be a function, and `R` must
be the name of a resource type.
* 📡 A `[get]` import or export must have no parameters (besides the required
`self` parameter from `[method]`), and must have a result type.
* 📡 A `[set]` import or export must have exactly one parameter (besides the
required `self` parameter from `[method]`), and must have either no result
type or a result type of `(result (error <valtype>)?)`.

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 use of
Expand Down Expand Up @@ -2832,28 +2841,66 @@ within the same scope.

To determine whether two names (defined as sequences of [Unicode Scalar
Values]) are **strongly-unique**:

1. Canonicalize each name:
1. Lowercase all the `acronym`s (uppercase letters) in the name.
2. If the name is `[method]l.l` or `[static]l.l` for some `label` `l`, replace
the name with `l` (e.g. `[method]foo.foo` becomes `foo`).
3. If the name has any `[...]` annotation prefix other than `[constructor]`,
strip it from the name.
1. Lowercase all the `acronym`s (uppercase letters) in the name.
2. If the name is `[...]*l.l` for any annotations `[...]*` and some `label`
`l`, replace the name with `l` (e.g. `[method]foo.foo` becomes `foo`).
3. Strip all `[...]` annotations from the name besides `[constructor]`
and 📡 `[set]`.
2. The names are strongly-unique if the resulting canonicalized strings are
unequal.
unequal.

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):

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`, `[static]foo.baz`, `foo:bar/baz`
* `foo`, `foo-bar`, `[constructor]foo`, `[method]foo.bar`, `[static]foo.baz`,
`foo:bar/baz`, 📡 `[get]prop`, 📡 `[set]prop`, 📡 `[method][get]foo.prop`,
📡 `[method][set]foo.prop`, 📡 `[static][get]foo.prop-2`,
📡 `[static][set]foo.prop-2`, `[method]foo.get-prop`, `[method]foo.set-prop`

but attempting to add *any* of the following names would be a validation error:
* `foo`, `FOO`, `foo-BAR`, `[constructor]FOO`, `[method]foo.BAR`, `[static]foo.bar`, `[method]foo.baz`, `[method]foo.foo`, `[static]foo-BAR.FOO-bar`, `foo:bar/BAZ`

The purpose of steps 1.2 and 1.3 is to play nice with constructors: in many languages, constructors are the only item that can have the same name as the class.
* `foo`, `FOO`, `[method]foo.foo`, 📡 `[get]foo`, 📡 `[method][get]foo.foo`,
📡 `[static][set]foo.FOO` (conflicts with `foo`)
* `foo-BAR`, `[static]foo-BAR.FOO-bar` (conflicts with `foo-bar`)
* `[constructor]FOO` (conflicts with `[constructor]foo`)
* `[method]foo.BAR`, `[static]foo.bar` (conflicts with `[method]foo.bar`)
* `[method]foo.baz` (conflicts with `[static]foo.baz`)
* `foo:bar/BAZ` (conflicts with `foo:bar/baz`)
* 📡 `prop` (conflicts with `[get]prop`)
* 📡 `[set]PROP` (conflicts with `[set]prop`)
* 📡 `[method]foo.prop`, 📡 `[static]foo.PROP`, 📡 `[method][get]foo.PROP`,
📡 `[static][get]foo.prop` (conflicts with `[method][get]foo.prop`)
* 📡 `[method][set]foo.PROP`, 📡 `[static][set]foo.prop` (conflicts with
`[method][set]foo.prop`)

Note that additional validation rules involving types apply to names with
annotations. For example, the validation rules for `[constructor]foo` require
`foo` to be a resource type. See [Binary.md](Binary.md#import-and-export-definitions)
for details.

Note that these rules prevent having a method or static function (📡 or getter
or setter) with the same name as its resource type, leaving room for a
constructor. This is to satisfy the naming rules around constructors in many
languages.

📡 Also note that, although `[get]foo` and `foo` are not considered
strongly-unique, `[set]foo` and `foo` are. However, since `[set]foo` separately
requires `[get]foo` to be present, there is no practical concern of a bindings
conflict between `[set]foo` and `foo`. The same goes for other uses of `[get]`
and `[set]` annotations.

📡 Note also that `[method][get]foo.prop` and `[method]foo.get-prop` are
considered strongly-unique even though in practice they may conflict. Likewise,
`[method][set]foo.prop` and `[method]foo.set-prop` are considered
strongly-unique. If this results in a conflict for a bindings generator, the
bindings generator may choose one of the two and ignore the other. This is a
temporary measure: in a later release, it is expected that both of these
examples will not be considered strongly-unique, so API designers are
encouraged to avoid this case.


#### 🔗 Canonical Interface Name

Expand Down Expand Up @@ -3051,7 +3098,8 @@ object*] steps need to be expanded to cover them:
For type exports, each type definition would export a JS constructor function.
This function would be callable iff a `[constructor]`-annotated function was
also exported. All `[method]`- and `[static]`-annotated functions would be
dynamically installed on the constructor's prototype chain. In the case of
dynamically installed on the constructor's prototype chain, making sure to
register `[get]` and `[set]` functions as getters and setters. In the case of
re-exports and multiple exports of the same definition, the same constructor
function object would be exported (following the same rules as WebAssembly
Exported Functions today). In pathological cases (which, importantly, don't
Expand Down
62 changes: 55 additions & 7 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 🔧 and 🏷️.
See [Gated Features] for an explanation of emoji like 🔧 and 🏷️.

[IDL]: https://en.wikipedia.org/wiki/Interface_description_language
[components]: https://github.com/webassembly/component-model
Expand Down Expand Up @@ -1069,6 +1069,7 @@ keyword ::= 'as'
| 'from'
| 'func'
| 'future'
| 'get' 📡

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.

It's not clear to me that these actually need to be added to a global keyword list, given that it will likely cause breakage and they can only appear in a specific place.

| 'import'
| 'include'
| 'interface'
Expand All @@ -1084,6 +1085,7 @@ keyword ::= 'as'
| 's32'
| 's64'
| 's8'
| 'set' 📡
| 'static'
| 'stream'
| 'string'
Expand Down Expand Up @@ -1557,6 +1559,8 @@ typedef-item ::= resource-item
func-item ::= id ':' func-type ';'

func-type ::= 'async'? 'func' param-list result-list
| 'get' param-list result-list 📡
| 'set' param-list result-list 📡

param-list ::= '(' named-type-list ')'

Expand All @@ -1575,6 +1579,29 @@ source-language bindings (e.g., `async` functions in JS, Python, C# or Rust) if
concurrency execution is desired. For more details, see the [concurrency
explainer](Concurrency.md#summary).

📡 As syntactic sugar, functions can be declared as *getters* or *setters* by
replacing the `func` keyword with `get` or `set`. Such functions cannot be
async and have additional restrictions on their parameters and results. A `get`
function must have no parameters and must return a value. A `set` function
must have exactly one parameter and must not return a value unless that value
is of type `result<_, error?>`. Every setter must have a corresponding getter
with the same name. To simplify validation, the getter must be defined before
the setter. For example, the following definitions:

```wit
foo: func(x: u32);
bar: get() -> u64;
bar: set(v: u64);
```

desugar into:

```wit
foo: func(x: u32);
[get]bar: func() -> u64;
[set]bar: func(v: u64);
```

🏷️ As with `import`s and `export`s in worlds, and for the same use cases,
interface items can be annotated with `@external-id`:
```wit
Expand Down Expand Up @@ -1770,32 +1797,53 @@ functions*, which do not have an implicit `self` parameter but are meant to be
lexically nested in the scope of the resource type. Lastly, a resource
statement can contain at most one *constructor* function, which is syntactic
sugar for a function returning a handle of the containing resource type.

Constructors can be fallible or infallible. Fallible constructors have an
explicitly-written return type which must be of the form `result<r, ...>`
where `r` is the name of the containing `resource`. Infallible constructors
have no written return type and are given the implicit return type `r`.
explicitly-written return type which must be of the form `result<r, ...>` where
`r` is the name of the containing `resource`. Infallible constructors have no
written return type and are given the implicit return type `r`.

📡 A resource statement can also contain any number of *getters* and
*setters*, which may or may not be static. Non-static getters and setters also
implicitly take a `self` parameter. Getters take no parameters (besides the
implicit `self` parameter) and must return a value. Setters take exactly one
parameter (besides the implicit `self` parameter) and must not return a value
unless that value is of type `result<_, error?>`. Every setter must have a
corresponding getter with the same name and same static-ness.

For example, the following resource definitions:

For example, the following resource definition:
```wit
resource blob {
constructor(init: list<u8>);
write: func(bytes: list<u8>);
read: func(n: u32) -> list<u8>;
merge: static func(lhs: borrow<blob>, rhs: borrow<blob>) -> blob;
position: get() -> u64; // 📡
position: set(value: u64); // 📡
max-size: static get() -> u64; // 📡
max-size: static set(value: u64) -> result<_, string>; // 📡
}
resource blob2 {
constructor(init: list<u8>) -> result<blob2>;
}
```
desugars into:

desugar into:

```wit
resource blob;
%[constructor]blob: func(init: list<u8>) -> blob;
%[constructor]blob2: func(init: list<u8>) -> result<blob2>;
%[method]blob.write: func(self: borrow<blob>, bytes: list<u8>);
%[method]blob.read: func(self: borrow<blob>, n: u32) -> list<u8>;
%[static]blob.merge: func(lhs: borrow<blob>, rhs: borrow<blob>) -> blob;
%[method][get]blob.position: func(self: borrow<blob>) -> u64;
%[method][set]blob.position: func(self: borrow<blob>, value: u64);
%[static][get]blob.max-size: func() -> u64;
%[static][set]blob.max-size: func(value: u64) -> result<_, string>;
%[constructor]blob2: func(init: list<u8>) -> result<blob2>;
```

These `%`-prefixed strings embed the resource type name so that bindings
generators can generate idiomatic syntax for the target language or (for
languages like C) fall back to an appropriately-prefixed free function name.
Expand Down
Loading