Skip to content

Commit 2b60da3

Browse files
committed
Add 'optional' imports/exports (WIP)
1 parent fed10b0 commit 2b60da3

6 files changed

Lines changed: 249 additions & 97 deletions

File tree

‎design/mvp/Binary.md‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,10 @@ instancedecl ::= 0x00 t:<core:type> => t
235235
| 0x01 t:<type> => t
236236
| 0x02 a:<alias> => a
237237
| 0x04 ed:<exportdecl> => ed
238-
importdecl ::= na:<nameattributes> et:<externtype> => (import na et)
239-
exportdecl ::= na:<nameattributes> et:<externtype> => (export na et)
238+
importdecl ::= na:<nameattributes> oet:<optexterntype> => (import na oet)
239+
exportdecl ::= na:<nameattributes> oet:<optexterntype> => (export na oet)
240+
optexterntype ::= et:<externtype> => et
241+
| 0x10 et:<externtype> => optional et
240242
externtype ::= 0x00 0x11 i:<core:typeidx> => (core module (type i))
241243
| 0x01 i:<typeidx> => (func (type i))
242244
| 0x02 b:<valuebound> => (value b) 🪙
@@ -308,6 +310,7 @@ canon ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift
308310
| 0x02 rt:<typeidx> => (canon resource.new rt (core func))
309311
| 0x03 rt:<typeidx> => (canon resource.drop rt (core func))
310312
| 0x04 rt:<typeidx> => (canon resource.rep rt (core func))
313+
| 0x07 si:<sortidx> => (canon optional.present si (core global)) ❓
311314
| 0x24 => (canon backpressure.inc (core func)) 🔀
312315
| 0x25 => (canon backpressure.dec (core func)) 🔀
313316
| 0x09 rs:<resultlist> opts:<opts> => (canon task.return rs opts (core func)) 🔀
@@ -403,7 +406,7 @@ flags are set.
403406
(See [Import and Export Definitions](Explainer.md#import-and-export-definitions)
404407
in the explainer.)
405408
```ebnf
406-
import ::= na:<nameattributes> et:<externtype> => (import na et)
409+
import ::= na:<nameattributes> oet:<optexterntype> => (import na oet)
407410
export ::= na:<nameattributes> si:<sortidx> et?:<externtype>? => (export na si et?)
408411
nameattributes ::= 0x00 len:<u32> en:<externname> => "en" (if len = |en|)
409412
| 0x01 len:<u32> en:<externname> => "en" (if len = |en|)
@@ -573,6 +576,7 @@ named once.
573576
* The two `list` type codes should be merged into one with an optional immediate
574577
and similarly for `func`.
575578
* The redundant `0x00` and `0x01` opcodes of `nameattributes` will be merged.
579+
* The `0x10` for `optional` in `optexterntype` overlapping with `externtype`
576580
* The vestigial `0x00` in `thread.*` and `waitable-set.*` built-ins may be
577581
removed.
578582
* Most built-ins should have a `<canonopt>*` immediate instead of an ad hoc

‎design/mvp/CanonicalABI.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ specified here.
4040
* [`canon resource.new`](#canon-resourcenew)
4141
* [`canon resource.drop`](#canon-resourcedrop)
4242
* [`canon resource.rep`](#canon-resourcerep)
43+
* [`canon optional.present`](#-canon-optionalpresent) ❓
4344
* [`canon context.get`](#-canon-contextget) 🔀
4445
* [`canon context.set`](#-canon-contextset) 🔀
4546
* [`canon backpressure.{inc,dec}`](#-canon-backpressureincdec) 🔀
@@ -3910,6 +3911,21 @@ Note that the "locally-defined" requirement above ensures that only the
39103911
component instance defining a resource can access its representation.
39113912

39123913

3914+
### ❓ `canon optional.present`
3915+
3916+
For a canonical definition:
3917+
```wat
3918+
(canon optional.present $sortidx (core global $present))
3919+
```
3920+
validation specifies:
3921+
* `$sortidx` must refer to an `(optional <externtype'>)`
3922+
* `$present` is given type `(global i32)`
3923+
3924+
The new immutable global `$present` is appended to the core global index space
3925+
and is either `1` or `0` depending on whether the `$sortidx` is present or not,
3926+
resp.
3927+
3928+
39133929
### 🔀 `canon context.get`
39143930

39153931
For a canonical definition:

‎design/mvp/Explainer.md‎

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ more user-focused explanation, take a look at the
2626
* [Canonical ABI](#canonical-abi)
2727
* [Canonical built-ins](#canonical-built-ins)
2828
* [Resource built-ins](#resource-built-ins)
29+
* [Optionality built-ins](#-optionality-built-ins)
2930
* [Concurrency built-ins](#-concurrency-built-ins)
3031
* [Error Context built-ins](#-error-context-built-ins)
3132
* [Value definitions](#-value-definitions)
@@ -75,6 +76,7 @@ shipped as part of a future WASI Developer Preview release:
7576
* 🐘: [memory64]
7677
* 📡: getters and setters
7778
* ➡️: `stream.forward` and `future.forward` built-ins
79+
* ❓: `optional` imports and exports
7880

7981

8082
## Grammar
@@ -643,8 +645,9 @@ instancedecl ::= core-prefix(<core:type>)
643645
| <type>
644646
| <alias>
645647
| <exportdecl>
646-
importdecl ::= (import <externnamelit> <attributes> bind-id(<externtype>))
647-
exportdecl ::= (export <externnamelit> <attributes> bind-id(<externtype>))
648+
importdecl ::= (import <externnamelit> <attributes> <optexterntype>)
649+
exportdecl ::= (export <externnamelit> <attributes> <optexterntype>)
650+
optexterntype ::= optional? bind-id(<externtype>)
648651
externtype ::= (<sort> (type <idx>) )
649652
| core-prefix(<core:moduletype>)
650653
| <functype>
@@ -918,19 +921,20 @@ capabilities of the component model.
918921

919922
#### Declarators
920923

921-
The `importdecl` and `exportdecl` declarators correspond to component `import`
922-
and `export` definitions, respectively, allowing an identifier to be bound for
923-
use by subsequent declarators. The definitions of `labellit`, `externnamelit`
924-
and `attribute` are given in the [imports and exports](#import-and-export-definitions)
925-
section below. Following the precedent of [`core:typeuse`], the text format
924+
The `importdecl` and `exportdecl` rules describe component-level `import` and
925+
`export` definitions at the type level. Thus, *both* imports and exports have an
926+
`externtype`, not just imports. Both declarators allow an identifier to be bound
927+
for use by subsequent declarators, so that imported and exported types can
928+
be used to define imported or exported function types.
929+
930+
The definitions of `labellit`, `externnamelit` and `attribute` are given in the
931+
[imports and exports](#import-and-export-definitions) section below.
932+
933+
Following the precedent of [`core:typeuse`], the text format of `externtype`
926934
allows both references to out-of-line type definitions (via `(type <typeidx>)`)
927935
and inline type expressions that the text format desugars into out-of-line type
928936
definitions.
929937

930-
🪙 The `value` case of `externtype` describes a runtime value that is imported or
931-
exported at instantiation time as described in the
932-
[value definitions](#value-definitions) section below.
933-
934938
The `type` case of `externtype` describes an imported or exported type along
935939
with its "bound":
936940

@@ -955,6 +959,38 @@ to some preceding type definition. This allows:
955959
could generate in C++ a `typedef std::vector<uint64_t> bytes` or in JS an
956960
exported field named `bytes` that aliases `Uint64Array`.
957961

962+
🪙 The `value` case of `externtype` describes a runtime value that is imported or
963+
exported at instantiation time as described in the
964+
[value definitions](#value-definitions) section below.
965+
966+
❓ The `optional` immediate indicates that the containing import or export may
967+
or may not be "present" at instantiation time.
968+
export unconditionally adds an index to `T`'s index space so that index spaces
969+
have runtime-invariant contents. The meaning of "not present" depends on the
970+
`externtype'`:
971+
* For `func`, "not present" means "traps if called".
972+
* For `instance`, "not present" means "the contained exports are either
973+
all-present or all not-present". Consequently, `alias export $i "e"`
974+
produces an `optional` alias for `"e"` if `$i` is `optional`.
975+
* For `type`, "not present" means that any `func` or `instance` `externtype`
976+
that transitively refers to a "not present" `type` also becomes "not present",
977+
(even if that `func` or `instance` is not declared `optional`).
978+
* 🪙 For `value`, "not present" means "a `none` value will be passed into any
979+
`start` function that takes this value as a parameter" (as described below).
980+
Consequently, an import of `(optional (value T))` is given the value type
981+
`(option T)` when validating a `start` call.
982+
983+
To determine whether an `optional`-typed `sortidx` is present at runtime, Core
984+
WebAssembly code can import a `global i32` boolean for that `sortidx` using the
985+
ABI built-in `optional.present` (as described below).
986+
987+
TODO: For example...
988+
```wat
989+
... dependent explicitly optional
990+
... dependent implicitly optional
991+
... maybe show `optional.present` for a few and describe the relationship
992+
```
993+
958994
The `alias` declarator reuses the syntax for component-level `alias` definitions
959995
so that `export` and `outer` aliases can be used inside `component` and
960996
`instance` *types*. For example, `export` aliases are necessary to define the
@@ -1090,6 +1126,11 @@ in the future to better support API Evolution (being careful to understand how
10901126
subtyping manifests itself in the wide variety of source languages so that
10911127
subtype-compatible updates don't inadvertently break source-level clients).
10921128

1129+
❓ TODO: mention `optional` in subtyping:
1130+
* `(X ...) <: (X optional ...)`
1131+
* `(instance (X optional ...)) <: (instance optional (X ...))`
1132+
* dependently optional on all the parts: present iff all parts are
1133+
10931134
Module, component and instance subtyping allows a subtype to export more and
10941135
import less than is declared by the supertype, ignoring the exact order of
10951136
imports and exports and considering only names. For example, here, `$I1` is a
@@ -1335,6 +1376,15 @@ replaced by `$R` when validating the instantiations of `$c1` and `$c2`. These
13351376
type-checking rules for instantiating type imports mirror the *elimination*
13361377
rule of [universal types] (∀T).
13371378

1379+
❓ TODO:
1380+
* for `optional` import:
1381+
* if provide nothing: substitute bottom type (can't name: no valid functions)
1382+
* e.g., but can still lower (to trap) and call `optional.present`
1383+
* else: substitute
1384+
* `optional` is part of what's substituted, so it goes away if provide
1385+
non-optional def to `with`
1386+
* e.g., can export with non-optional type
1387+
13381388
Importantly, this type substitution performed by the parent is not visible to
13391389
the child at validation- or run-time. In particular, there are no runtime
13401390
casts that can "see through" to the original type parameter, avoiding
@@ -1558,6 +1608,7 @@ canon ::= ...
15581608
| (canon resource.new <typeidx> (core func <id>?))
15591609
| (canon resource.drop <typeidx> (core func <id>?))
15601610
| (canon resource.rep <typeidx> (core func <id>?))
1611+
| (canon optional.present <sortidx> (core global <id>?)) ❓
15611612
| (canon context.get <core:valtype> <u32> (core func <id>?)) 🔀
15621613
| (canon context.set <core:valtype> <u32> (core func <id>?)) 🔀
15631614
| (canon backpressure.inc (core func <id>?)) 🔀
@@ -1684,6 +1735,12 @@ transferring ownership of the newly-created resource to the export's caller.
16841735

16851736
For details, see [`canon_resource_rep`] in the Canonical ABI explainer.
16861737

1738+
##### ❓ Optionality built-ins
1739+
1740+
###### ❓ `optional.present`
1741+
1742+
TODO
1743+
16871744
##### 🔀🧵 Concurrency built-ins
16881745

16891746
See the [concurrency explainer] for background.
@@ -2658,27 +2715,29 @@ A third difference is that not only import definitions, but also export
26582715
definitions append a new element to the index space of the imported/exported
26592716
`sort` which can be optionally bound to an identifier in the text format. In the
26602717
case of imports, the identifier is bound just like Core WebAssembly, as part of
2661-
the `externtype` (e.g., `(import "x" (func $x))` binds the identifier `$x`). In
2662-
the case of exports, the `<id>?` right after the `export` is bound while the
2718+
the `externtype` (e.g., `(import "x" (func $x))` binds the identifier `$x`).
2719+
In the case of exports, the `<id>?` right after the `export` is bound while the
26632720
`<id>` inside the `<externidx>` is a reference to the preceding definition being
26642721
exported (e.g., `(export $x "x" (func $f))` binds a new identifier `$x`).
26652722

26662723
Given this, the syntax of imports and exports are defined as follows:
26672724
```ebnf
2668-
import ::= (import <externnamelit> <attributes> bind-id(<externtype>))
2669-
export ::= (export <id>? <externnamelit> <attributes> <externidx> <externtype>?)
2725+
import ::= (import <externnamelit> <attributes> <optexterntype>)
2726+
export ::= (export <id>? <externnamelit> <attributes> <externidx> <optexterntype>?)
26702727
attributes ::= <implements>? <versionsuffix>? <externid>?
26712728
implements ::= (implements <interfacenamelit>) 🏷️
26722729
versionsuffix ::= (version-suffix <semversuffixlit>) 🔗
26732730
externid ::= (external-id <name>) 🏷️
26742731
```
26752732
Imports and exports both have a *name* (`externnamelit`) and a list of
2676-
*attributes* (`attributes`). Imports always include a type (`externtype`).
2733+
*attributes* (`attributes`). Imports always include a type (`optexterntype`).
26772734
Exports always refer to a preceding definition (`externidx`) whose type is used
26782735
by default as the type of the export. However, this "inferred" type can be
2679-
overridden with an optional type ascription (`externtype`) which must be
2736+
overridden with an optional type ascription (`optexterntype`) which must be
26802737
compatible with the exported definition's type.
26812738

2739+
TODO: `optional`
2740+
26822741
Import and export names are used by hosts and [parent][Donut Wrapped] components
26832742
to *identify* imports and exports. Names are expected to be bound to source
26842743
language variable-, function- and type-names. Thus, names are required to be

‎design/mvp/WIT.md‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ world union-my-world {
308308
}
309309
```
310310

311+
TODO: add `optional` to some of the `include` examples above (unions *both* imports and exports)
312+
TODO: add a note that `include` does not produce subtype or supertype, but other ways to combine worlds (`meet` and `join`) could be added in the future
313+
311314
### De-duplication of interfaces
312315

313316
If two worlds share an imported or exported [interface name], then the union of
@@ -339,6 +342,8 @@ world union-my-world-b {
339342
}
340343
```
341344

345+
TODO: mention merging of `optional` and non-`optional` ~~> non-`optional`
346+
342347
### Name Conflicts and `with`
343348

344349
When two or more included Worlds have the same name for an import or export
@@ -436,12 +441,6 @@ world invalid-union-world {
436441
437442
```
438443

439-
### A Note on Subtyping
440-
441-
In the future, when `optional` export is supported, the world author may explicitly mark exports as optional to make a component targeting an included World a subtype of the union World.
442-
443-
For now, we are not following the subtyping rules for the `include` statement. That is, the `include` statement does not imply any subtyping relationship between the included worlds and the union world.
444-
445444
## WIT Packages and `use`
446445
[use]: #wit-packages-and-use
447446

@@ -563,6 +562,8 @@ interface my-interface {
563562
}
564563
```
565564

565+
TODO: `use` resolution when only used from `optional`y-imported interfaces --> `optional`
566+
566567
#### Top-level `use`
567568

568569
If a package being referred to has a version number, then using the above syntax
@@ -1075,6 +1076,7 @@ keyword ::= 'as'
10751076
| 'list'
10761077
| 'map'
10771078
| 'option'
1079+
| 'optional'
10781080
| 'own'
10791081
| 'package'
10801082
| 'record'
@@ -1441,10 +1443,10 @@ world-definition ::= export-item
14411443
| typedef-item
14421444
| include-item
14431445
1444-
export-item ::= external-id? 'export' id ':' extern-type
1445-
| 'export' use-path ';'
1446-
import-item ::= external-id? 'import' id ':' extern-type
1447-
| 'import' use-path ';'
1446+
export-item ::= external-id? 'export' id ':' 'optional'? extern-type
1447+
| 'export' 'optional'? use-path ';'
1448+
import-item ::= external-id? 'import' id ':' 'optional'? extern-type
1449+
| 'import' 'optional'? use-path ';'
14481450
14491451
external-id ::= '@external-id' '(' string-literal ')' 🏷️
14501452
@@ -1508,6 +1510,8 @@ world my-world {
15081510
}
15091511
```
15101512

1513+
TODO: note `optional` and what it means
1514+
15111515
[`componenttype`]: Explainer.md#type-definitions
15121516

15131517
## Item: `include`

0 commit comments

Comments
 (0)