Skip to content
Merged
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
39 changes: 35 additions & 4 deletions design/mvp/Async.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,40 @@ component-level function that has been [lifted] from Core WebAssembly with the
function that does not have the `async` option set (which is the default and
only option prior to Preview 3). Thus, the sync/async distinction appears
only independently in how a component-level function is *implemented* or
*called*.
*called*. This lack of distinction helps to avoid the classic ["What Color Is
Your Function?"][Color] problem.

Functions *may* however be annotated (in both WIT and component binaries) with
`async` as a *hint*. This hint is intended to inform the default language
bindings generation, indicating whether to use a source-level `async` function
or not in languages that have such a distinction (e.g., JS, Python, C# and
Rust). In the absence of such a hint, a bindings generator would be forced to
make a uniform decision for what to do by default for all functions or require
manual programmer directives. However, because `async` is just a hint, there is
no prohibition against non-`async`-exported functions calling imported `async`
functions. This does mean that non-`async` functions may end up blocking
their caller, but (1) any loss in performance is the callee's "fault", (2) the
caller can still lower `async` if they want to (overriding the default hint),
(3) any *transitive* caller an lower `async` to avoid blocking.

For example, given this interface:
```wit
interface filesystem {
resource file {
constructor();
is-closed: func() -> bool;
read: async func(num-bytes: u32) -> result<list<u8>>;
from-bytes: static func(bytes: list<u8>) -> file;
from-stream: static async func(bytes: stream<u8>) -> file;
}
}
```
a bindings generator in a language with `async` would only emit `async`
functions for `read` and `fetch`. Since in many languages `new` expressions
cannot be async, there is no `async constructor`. Use cases requiring
asynchronous construction can instead use `static async` functions, similar to
`from-stream` in this example.


### Task

Expand Down Expand Up @@ -709,11 +742,9 @@ time as applications are built with more components.
## TODO

Native async support is being proposed incrementally. The following features
will be added in future chunks roughly in the order list to complete the full
will be added in future chunks roughly in the order listed to complete the full
"async" story, with a TBD cutoff between what's in [WASI Preview 3] and what
comes after:
* `nonblocking` function type attribute: allow a function to declare in its
type that it will not transitively do anything blocking
* `subtask.cancel`: allow a supertask to signal to a subtask that its result is
no longer wanted and to please wrap it up promptly
* zero-copy forwarding/splicing
Expand Down
11 changes: 5 additions & 6 deletions design/mvp/Binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,7 @@ Notes:
* All parameter labels, result labels, record field labels, variant case
labels, flag labels, enum case labels, component import names, component
export names, instance import names and instance export names must be
unique in their containing scope, considering two labels that differ only in
case to be equal and thus rejected.
[strongly-unique] in their containing scope.
* Validation of `externdesc` requires the various `typeidx` type constructors
to match the preceding `sort`.
* (The `0x00` immediate of `case` may be reinterpreted in the future as the
Expand Down Expand Up @@ -383,8 +382,8 @@ Notes:
of the inferred `externdesc` of the `sortidx`.
* `<importname>` and `<exportname>` refer to the productions defined in the
[text format](Explainer.md#import-and-export-definitions).
* The `<importname>`s of a component must be unique and the `<exportname>`s of
a component must be unique as well (defined in terms of raw string equality).
* 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
Expand All @@ -394,8 +393,6 @@ Notes:
`(result (own $R))`, where `$R` is the resource labeled `r`.
* Validation of `[method]` names requires the first parameter of the function
to be `(param "self" (borrow $R))`, where `$R` is the resource labeled `r`.
* Validation of `[method]` and `[static]` names ensures that all field names
are disjoint.
* `<valid semver>` is as defined by [https://semver.org](https://semver.org/)
* `<integrity-metadata>` is as defined by the
[SRI](https://www.w3.org/TR/SRI/#dfn-integrity-metadata) spec.
Expand Down Expand Up @@ -514,6 +511,8 @@ named once.
[`core:functype`]: https://webassembly.github.io/spec/core/binary/types.html#binary-functype
[`core:rectype]: https://webassembly.github.io/gc/core/binary/types.html#recursive-types

[Strongly-unique]: Explainer.md#name-uniqueness

[type-imports]: https://github.com/WebAssembly/proposal-type-imports/blob/master/proposals/type-imports/Overview.md
[module-linking]: https://github.com/WebAssembly/module-linking/blob/main/proposals/module-linking/Explainer.md

Expand Down
98 changes: 70 additions & 28 deletions design/mvp/Explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ more user-focused explanation, take a look at the
* [Value definitions](#-value-definitions)
* [Start definitions](#-start-definitions)
* [Import and export definitions](#import-and-export-definitions)
* [Name uniqueness](#name-uniqueness)
* [Component invariants](#component-invariants)
* [JavaScript embedding](#JavaScript-embedding)
* [JS API](#JS-API)
Expand Down Expand Up @@ -782,8 +783,8 @@ The remaining 4 type constructors in `deftype` use `valtype` to describe
shared-nothing functions, resources, components, and component instances:

The `func` type constructor describes a component-level function definition
that takes a list of uniquely-named `valtype` parameters and optionally returns
a `valtype`.
that takes a list of `valtype` parameters with [strongly-unique] names and
optionally returns a `valtype`.

The `resource` type constructor creates a fresh type for each instance of the
containing component (with "freshness" and its interaction with general
Expand Down Expand Up @@ -2173,16 +2174,17 @@ new identifier `$x`).
import ::= (import "<importname>" bind-id(<externdesc>))
export ::= (export <id>? "<exportname>" <sortidx> <externdesc>?)
```
All import names are required to be unique and all export names are required to
be unique. The rest of the grammar for imports and exports defines a structured
syntax for the contents of import and export names. Syntactically, these names
appear inside quoted string literals. The grammar thus restricts the contents
of these string literals to provide more structured information that can be
mechanically interpreted by toolchains and runtimes to support idiomatic
developer workflows and source-language bindings. The rules defining this
structured name syntax below are to be 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.
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
imports and exports defines a structured syntax for the contents of import and
export names. Syntactically, these names appear inside quoted string literals.
The grammar thus restricts the contents of these string literals to provide
more structured information that can be mechanically interpreted by toolchains
and runtimes to support idiomatic developer workflows and source-language
bindings. The rules defining this structured name syntax below are to be
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>
Expand All @@ -2191,9 +2193,12 @@ importname ::= <exportname>
| <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>
Expand Down Expand Up @@ -2307,16 +2312,24 @@ 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. In each of these cases, 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 data type, object,
namespace, package, module or whatever resources get bound to. For example, a
function named `[method]C.foo` could be bound in C++ to a member function `foo`
in a class `C`. The JS API [below](#JS-API) describes how the native JavaScript
bindings could look. Validation described in [Binary.md](Binary.md) inspects
the contents of `plainname` and ensures that the function has a compatible
signature.
resource and/or being asynchronous.

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
data type, object, namespace, package, module or whatever resources get bound
to. For example, a function named `[method]C.foo` could be bound in C++ to a
member function `foo` in a class `C`. The JS API [below](#JS-API) describes how
the native JavaScript bindings could look. Validation described in
[Binary.md](Binary.md) inspects the contents of `plainname` and ensures that
the function has a compatible signature.

When a function is annotated with `async`, bindings generators are expected to
emit whatever asynchronous language construct is appropriate (such as an
`async` function in JS, Python or Rust). Note the absence of
`[async constructor]`. See the [async
explainer](Async.md#sync-and-async-functions) for more details.

The `label` production used inside `plainname` as well as the labels of
`record` and `variant` types are required to have [kebab case]. The reason for
Expand All @@ -2329,12 +2342,6 @@ mapped to `isXML`, `IsXml`, `is_XML` or `is_xml`, depending on the target
language/convention. The highly-restricted character set ensures that
capitalization is trivial and does not require consulting Unicode tables.

Because some casing schemes (such as all-lowercase) would lead to clashes if
two `label`s differed only in case, in all cases where "uniqueness" is required
between a set of names (viz., import/export names, record field labels, variant
case labels, and function parameter/result names), two `label`s that differ
only in case are considered equal and thus rejected.

Components provide two options for naming exports, symmetric to the first two
options for naming imports:
* a **plain name** that leaves it up to the developer to "read the docs"
Expand Down Expand Up @@ -2426,6 +2433,39 @@ The inferred type of this component is:

Note, that the `url` value definition is absent from the component type

### Name Uniqueness

The goal of the `label`, `exportname` and `importname` productions defined and
used above is to allow automated bindings generators to map these names into
something more idiomatic to the language. For example, the `plainname`
`[method]my-resource.my-method` might get mapped to a method named `myMethod`
nested inside a class `MyResource`. To unburden bindings generators from having
to consider pathological cases where two unique-in-the-component names get
mapped to the same source-language identifier, Component Model validation
imposes a stronger form of uniquness than simple string equality on all the
names that appear within the same scope.

To determine whether two names (defined as sequences of [Unicode Scalar
Values]) are **strongly-unique**:
* If one name is `l` and the other name is `[constructor]l` (for the same
`label` `l`), they are strongly-unique.
* Otherwise:
* Lowercase all the `acronym`s (uppercase letters) in both names.
* 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`

but attempting to add *any* of the following names would be a validation error:
* `foo`, `foo-BAR`, `[constructor]foo-BAR`, `[async]foo`, `[method]foo.BAR`

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.


## Component Invariants

As a consequence of the shared-nothing design described above, all calls into
Expand Down Expand Up @@ -2783,6 +2823,8 @@ For some use-case-focused, worked examples, see:
[WASI Preview 2]: https://github.com/WebAssembly/WASI/tree/main/wasip2#readme
[reference types]: https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md

[Strongly-unique]: #name-uniqueness

[Adapter Functions]: FutureFeatures.md#custom-abis-via-adapter-functions
[Canonical ABI explainer]: CanonicalABI.md
[`canon_context_get`]: CanonicalABI.md#-canon-contextget
Expand Down
19 changes: 17 additions & 2 deletions design/mvp/WIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ keywords is still in flux at this time but the current set is:

```ebnf
keyword ::= 'as'
| 'async'
| 'bool'
| 'borrow'
| 'char'
Expand Down Expand Up @@ -1299,7 +1300,7 @@ typedef-item ::= resource-item

func-item ::= id ':' func-type ';'

func-type ::= 'func' param-list result-list
Copy link
Member

Choose a reason for hiding this comment

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

Could you add some text to Wit.md briefly describing what non-blocking does, from a Wit user perspective? And maybe link back to the Explainer.md for the full details?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea, fixed, PTAL

func-type ::= 'async'? 'func' param-list result-list

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

Expand All @@ -1312,6 +1313,17 @@ named-type-list ::= ϵ
named-type ::= id ':' ty
```

The optional `async` hint in a WIT function type indicates that the callee
is expected to block and thus the caller should emit whatever asynchronous
language bindings are appropriate (e.g., in JS, Python, C# or Rust, an `async`
WIT function would emit an `async` JS/Python/C#/Rust function). Because `async`
is just a hint and not enforced by the runtime, it is technically possible for
a non-`async` callee to block. In that case, though, it is the *callee's* fault
for any resultant loss of concurrency, not the caller's. Thus, `async` is
primarily intended to document expectations in a way that can be taken
advantage of by bindings generators. (For more details, see the [async
explainer](Async.md#sync-and-async-functions).)


## Item: `use`

Expand Down Expand Up @@ -1528,10 +1540,13 @@ Specifically, the syntax for a `resource` definition is:
resource-item ::= 'resource' id ';'
| 'resource' id '{' resource-method* '}'
resource-method ::= func-item
| id ':' 'static' func-type ';'
| id ':' 'static' 'async'? func-type ';'
| 'constructor' param-list ';'
```

The optional `async` hint on `static` functions has the same meaning as
in a non-`static` `func-item`.

The syntax for handle types is presented [below](#handles).

## Types
Expand Down