Skip to content

Commit d371bf7

Browse files
committed
Add 'optional' imports/exports
1 parent b11fa46 commit d371bf7

4 files changed

Lines changed: 74 additions & 12 deletions

File tree

design/mvp/Binary.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,9 @@ instancedecl ::= 0x00 t:<core:type> => t
232232
| 0x04 ed:<exportdecl> => ed
233233
importdecl ::= na:<nameattributes> et:<externtype> => (import na et)
234234
exportdecl ::= na:<nameattributes> et:<externtype> => (export na et)
235-
externtype ::= 0x00 0x11 i:<core:typeidx> => (core module (type i))
235+
externtype ::= 0x10 t:<someexterntype> => (optional t)
236+
| t:<someexterntype> => t
237+
someexterntype ::= 0x00 0x11 i:<core:typeidx> => (core module (type i))
236238
| 0x01 i:<typeidx> => (func (type i))
237239
| 0x02 b:<valuebound> => (value b) 🪙
238240
| 0x03 b:<typebound> => (type b)
@@ -287,6 +289,8 @@ Notes:
287289
`none` case of an optional immediate.)
288290
* 🔧 for fixed-sized lists the length of the list must be larger than 0 to pass
289291
validation.
292+
* ❓ validation of `optional` rejects everything except `func`, `instance` and
293+
`resource` types (this may be relaxed later).
290294

291295

292296
## Canonical Definitions
@@ -541,6 +545,7 @@ named once.
541545
subset of `canonopt`s.
542546
* Add optional `shared` immediate to all canonical definitions (explicitly or
543547
via `<canonopt>`) when shared-everything-threads (🧵②) is added.
548+
* Improve `externtype` opcodes for `optional`
544549

545550

546551
[`core:byte`]: https://webassembly.github.io/spec/core/binary/values.html#binary-byte

design/mvp/CanonicalABI.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ specified here.
6868
* [`canon thread.yield-then-resume`](#-canon-threadyield-then-resume) 🧵
6969
* [`canon thread.suspend-then-promote`](#-canon-threadsuspend-then-promote) 🧵
7070
* [`canon thread.yield-then-promote`](#-canon-threadyield-then-promote) 🧵
71+
* [`canon optional.test`](#-canon-optionaltest)
7172
* [`canon error-context.new`](#-canon-error-contextnew) 📝
7273
* [`canon error-context.debug-message`](#-canon-error-contextdebug-message) 📝
7374
* [`canon error-context.drop`](#-canon-error-contextdrop) 📝
@@ -2567,6 +2568,7 @@ possible, for example if the index was actually a `borrow` or if the `own`
25672568
handle is currently being lent out as borrows.
25682569
```python
25692570
def lift_own(cx, i, t):
2571+
;; TODO: assert(not isinstance(t, Optional))
25702572
h = cx.inst.handles.remove(i)
25712573
trap_if(not isinstance(h, ResourceHandle))
25722574
trap_if(h.rt is not t.rt)
@@ -2586,6 +2588,7 @@ from the source handle, leaving the source handle intact in the current
25862588
component instance's `handles` table:
25872589
```python
25882590
def lift_borrow(cx, i, t):
2591+
;; TODO: assert(not isinstance(t, Optional))
25892592
assert(isinstance(cx.borrow_scope, Subtask))
25902593
h = cx.inst.handles.get(i)
25912594
trap_if(not isinstance(h, ResourceHandle))
@@ -3025,10 +3028,12 @@ elements in the current component instance's `handles` table. The increment of
30253028
ensures that all borrowed handles are dropped before the end of the task.
30263029
```python
30273030
def lower_own(cx, rep, t):
3031+
;; TODO: assert(not isinstance(t, Optional))
30283032
h = ResourceHandle(t.rt, rep, own = True)
30293033
return cx.inst.handles.add(h)
30303034

30313035
def lower_borrow(cx, rep, t):
3036+
;; TODO: assert(not isinstance(t, Optional))
30323037
assert(isinstance(cx.borrow_scope, Task))
30333038
if cx.inst is t.rt.impl:
30343039
return rep
@@ -3556,14 +3561,16 @@ performed for a component. These are defined as:
35563561

35573562
For a canonical definition:
35583563
```wat
3559-
(canon lift $callee:<funcidx> $opts:<canonopt>* (func $f (type $ft)))
3564+
(canon lift $callee:<core:funcidx> $opts:<canonopt>* (func $f (type $ft)))
35603565
```
35613566

35623567
In addition to [general validation of `$opts`](#canonopt-validation) the additional
35633568
validation is performed:
35643569

3570+
TODO: can `$ft` be `optional`?
3571+
35653572
* `$callee` must have type `flatten_functype($opts, $ft, 'lift')`
3566-
* `$f` is given type `$ft`
3573+
* `$f` is given type `$ft` (TODO: must be non-`optional` `func` type)
35673574
* if a `post-return` is present, it has type `(func (param flatten_functype($opts, $ft, 'lift').results))`
35683575
* requires options based on [`lift(param)`](#canonopt-validation) for all parameters in `ft`
35693576
* requires options based on [`lower(result)`](#canonopt-validation) if `ft` has a result
@@ -3789,8 +3796,10 @@ For a canonical definition:
37893796
```
37903797

37913798
In addition to [general validation of `$opts`](#canonopt-validation), additional
3792-
validation is performed where `$callee` has type `$ft`:
3799+
validation is performed where `$callee` has type `$ft'`:
37933800

3801+
* `$ft'` must either be of the form `(func ...)` or `(optional (func ...))`
3802+
* let `$ft` be the `(func ...)` (sub)expression of `$ft'`
37943803
* `$f` is given type `flatten_functype($opts, $ft, 'lower')`
37953804
* requires options [based on `lower(param)`](#canonopt-validation) for all parameters in `ft`
37963805
* requires options [based on `lift(result)`](#canonopt-validation) if `ft` has a result
@@ -3814,9 +3823,11 @@ handles are not dropped before `Subtask.deliver_resolve` is called (below).
38143823
def canon_lower(callee, ft, opts, flat_args: list[CoreValType]) -> list[CoreValType]:
38153824
thread = current_thread()
38163825
trap_if(not thread.task.inst.may_leave)
3826+
trap_if(callee is None)
38173827
subtask = Subtask()
38183828
cx = LiftLowerContext(opts, thread.task.inst, subtask)
38193829
```
3830+
TODO: mention that `callee is None` can only happen if `$ft'` was `optional`.
38203831

38213832
The next chunk makes the call to `callee` using the `opts` immediates of the
38223833
`canon lower` definition to configure `lift_flat_values` and `lower_flat_values`
@@ -5097,6 +5108,19 @@ prepared to propagate cancellation, they can omit `cancellable` so that
50975108
cancellation is instead delivered at a later `cancellable` call.
50985109

50995110

5111+
### `canon optional.test`
5112+
5113+
For a canonical definition:
5114+
```wat
5115+
(canon optional.test $sortidx (core global $present))
5116+
```
5117+
validation specifies:
5118+
* `$sortidx` must ... and be `optional`
5119+
* `$optional.test` is given type `(global i32)`
5120+
5121+
TODO: describe
5122+
5123+
51005124
### 📝 `canon error-context.new`
51015125

51025126
For a canonical definition:

design/mvp/Explainer.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ more user-focused explanation, take a look at the
2121
* [Specialized value types](#specialized-value-types)
2222
* [Definition types](#definition-types)
2323
* [Declarators](#declarators)
24+
* [Optional types](#optional-types)
2425
* [Type checking](#type-checking)
2526
* [Canonical definitions](#canonical-definitions)
2627
* [Canonical ABI](#canonical-abi)
2728
* [Canonical built-ins](#canonical-built-ins)
2829
* [Resource built-ins](#resource-built-ins)
2930
* [Concurrency built-ins](#-concurrency-built-ins)
31+
* [Feature-test built-in](#-feature-test-built-in)
3032
* [Error Context built-ins](#-error-context-built-ins)
3133
* [Value definitions](#-value-definitions)
3234
* [Start definitions](#-start-definitions)
@@ -69,6 +71,7 @@ subsequent WASI Developer Preview minor releases:
6971
* 🐘: [memory64]
7072
* 🗺️: the `map` type
7173
* 🏷️: `implements` annotations for plain-named interface imports/exports
74+
* ❓: `optional` imports and exports
7275

7376

7477
## Grammar
@@ -607,9 +610,9 @@ valtype ::= <typeidx>
607610
keytype ::= bool | s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64 | char | string 🗺️
608611
resourcetype ::= (resource (rep i32) (dtor <core:funcidx>)?)
609612
| (resource (rep i64) (dtor <core:funcidx>)?) 🐘
610-
functype ::= (func async? (param <labellit> <valtype>)* (result <valtype>)?)
613+
functype ::= (func optional? async? (param <labellit> <valtype>)* (result <valtype>)?)
611614
componenttype ::= (component <componentdecl>*)
612-
instancetype ::= (instance <instancedecl>*)
615+
instancetype ::= (instance optional? <instancedecl>*)
613616
componentdecl ::= <importdecl>
614617
| <instancedecl>
615618
instancedecl ::= core-prefix(<core:type>)
@@ -624,9 +627,8 @@ externtype ::= (<sort> (type <u32>) )
624627
| <componenttype>
625628
| <instancetype>
626629
| (value <valuebound>) 🪙
627-
| (type <typebound>)
628-
typebound ::= (eq <typeidx>)
629-
| (sub resource)
630+
| (type (eq <typeidx>))
631+
| (type optional? (sub resource))
630632
valuebound ::= (eq <valueidx>) 🪙
631633
| <valtype> 🪙
632634
@@ -1009,6 +1011,14 @@ definitions:
10091011
Note that the inline use of `$G` and `$U` are syntactic sugar for `outer`
10101012
aliases.
10111013

1014+
#### Optional types
1015+
1016+
TODO:
1017+
* `alias export` produces `optional` unless `eq`
1018+
* `with` checks ...
1019+
* ascription checks ...
1020+
1021+
10121022
#### Type Checking
10131023

10141024
Like core modules, components have an up-front validation phase in which the
@@ -1091,6 +1101,9 @@ and `$C1` is a subtype of `$C2`:
10911101
checking and so any `external-id`, `implements` or `versionsuffix` attribute can
10921102
be added to any of the `func`s above without changing the result.
10931103

1104+
TODO: also mention `optional`
1105+
1106+
TODO: no `typebound`
10941107
When we next consider type imports and exports, there are two distinct
10951108
subcases of `typebound` to consider: `eq` and `sub`.
10961109

@@ -1298,6 +1311,9 @@ replaced by `$R` when validating the instantiations of `$c1` and `$c2`. These
12981311
type-checking rules for instantiating type imports mirror the *elimination*
12991312
rule of [universal types] (∀T).
13001313

1314+
TODO: if don't provide type, substitute incompatible type; prevent functions
1315+
from being supplied...
1316+
13011317
Importantly, this type substitution performed by the parent is not visible to
13021318
the child at validation- or run-time. In particular, there are no runtime
13031319
casts that can "see through" to the original type parameter, avoiding
@@ -1545,6 +1561,7 @@ canon ::= ...
15451561
| (canon thread.yield-then-resume cancellable? (core func <id>?)) 🧵
15461562
| (canon thread.suspend-then-promote cancellable? (core func <id>?)) 🧵
15471563
| (canon thread.yield-then-promote cancellable? (core func <id>?)) 🧵
1564+
| (canon optional.test <sortidx> (core global <id>?)) ❓
15481565
| (canon error-context.new <canonopt>* (core func <id>?)) 📝
15491566
| (canon error-context.debug-message <canonopt>* (core func <id>?)) 📝
15501567
| (canon error-context.drop (core func <id>?)) 📝
@@ -2331,6 +2348,11 @@ For details, see [`canon_thread_available_parallelism`] in the Canonical ABI
23312348
explainer.
23322349

23332350

2351+
##### ❓ Feature-test built-in
2352+
2353+
TODO: describe `canon optional.test`
2354+
2355+
23342356
##### 📝 Error Context built-ins
23352357

23362358
###### 📝 `error-context.new`

design/mvp/WIT.md

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

311+
TODO: add `optional` to examples
312+
311313
### De-duplication of interfaces
312314

313315
If two worlds share an imported or exported [interface name], then the union of
@@ -339,6 +341,8 @@ world union-my-world-b {
339341
}
340342
```
341343

344+
TODO: mention merging of `optional` and non-`optional`
345+
342346
### Name Conflicts and `with`
343347

344348
When two or more included Worlds have the same name for an import or export
@@ -438,6 +442,7 @@ world invalid-union-world {
438442

439443
### A Note on Subtyping
440444

445+
TODO: update this
441446
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.
442447

443448
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.
@@ -563,6 +568,8 @@ interface my-interface {
563568
}
564569
```
565570

571+
TODO: `use` from all-`optional`-imports/export --> `optional`
572+
566573
#### Top-level `use`
567574

568575
If a package being referred to has a version number, then using the above syntax
@@ -1448,9 +1455,11 @@ import-item ::= external-id? 'import' id ':' extern-type
14481455
14491456
external-id ::= '@external-id' '(' string-literal ')' 🏷️
14501457
1451-
extern-type ::= func-type ';'
1452-
| 'interface' '{' interface-items* '}'
1453-
| use-path ';' 🏷️
1458+
extern-type ::= 'optional' some-extern-type
1459+
| some-extern-type
1460+
some-extern-type ::= func-type ';'
1461+
| 'interface' '{' interface-items* '}'
1462+
| use-path ';' 🏷️
14541463
```
14551464

14561465
🏷️ While `id`s are restricted to kebab-case (to enable idiomatic bindings for
@@ -1508,6 +1517,8 @@ world my-world {
15081517
}
15091518
```
15101519

1520+
TODO: note `optional` and what it means
1521+
15111522
[`componenttype`]: Explainer.md#type-definitions
15121523

15131524
## Item: `include`

0 commit comments

Comments
 (0)