Skip to content

Commit 0a0d83c

Browse files
committed
Add 'optional' imports/exports (WIP)
1 parent d230073 commit 0a0d83c

6 files changed

Lines changed: 225 additions & 67 deletions

File tree

‎design/mvp/Binary.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,9 @@ instancedecl ::= 0x00 t:<core:type> => t
237237
| 0x04 ed:<exportdecl> => ed
238238
importdecl ::= na:<nameattributes> et:<externtype> => (import na et)
239239
exportdecl ::= na:<nameattributes> et:<externtype> => (export na et)
240-
externtype ::= 0x00 0x11 i:<core:typeidx> => (core module (type i))
240+
externtype ::= et:<externtype'> => et
241+
| 0x10 et:<externtype'> => optional et ❓
242+
externtype' ::= 0x00 0x11 i:<core:typeidx> => (core module (type i))
241243
| 0x01 i:<typeidx> => (func (type i))
242244
| 0x02 b:<valuebound> => (value b) 🪙
243245
| 0x03 b:<typebound> => (type 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)) 🔀
@@ -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 `externtype` 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) 🔀
@@ -3931,6 +3932,21 @@ Note that the "locally-defined" requirement above ensures that only the
39313932
component instance defining a resource can access its representation.
39323933

39333934

3935+
### ❓ `canon optional.present`
3936+
3937+
For a canonical definition:
3938+
```wat
3939+
(canon optional.present $sortidx (core global $present))
3940+
```
3941+
validation specifies:
3942+
* the target of `$sortidx` is declared with `optional`
3943+
* `$present` is given type `(global i32)`
3944+
3945+
The new immutable global `$present` is appended to the core global index space
3946+
and is either `1` or `0` depending on whether the target of `$sortidx` is
3947+
present or not, resp.
3948+
3949+
39343950
### 🔀 `canon context.get`
39353951

39363952
For a canonical definition:

‎design/mvp/Explainer.md‎

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ more user-focused explanation, take a look at the
2222
* [Definition types](#definition-types)
2323
* [Declarators](#declarators)
2424
* [Type checking](#type-checking)
25+
* [Optionality](#-optionality)
2526
* [Canonical definitions](#canonical-definitions)
2627
* [Canonical ABI](#canonical-abi)
2728
* [Canonical built-ins](#canonical-built-ins)
2829
* [Resource built-ins](#resource-built-ins)
30+
* [Optionality built-ins](#-optionality-built-ins)
2931
* [Concurrency built-ins](#-concurrency-built-ins)
3032
* [Error Context built-ins](#-error-context-built-ins)
3133
* [Value definitions](#-value-definitions)
@@ -75,6 +77,7 @@ shipped as part of a future WASI Developer Preview release:
7577
* 🐘: [memory64]
7678
* 📡: getters and setters
7779
* ➡️: `stream.forward` and `future.forward` built-ins
80+
* ❓: `optional` imports and exports
7881

7982

8083
## Grammar
@@ -643,9 +646,11 @@ instancedecl ::= core-prefix(<core:type>)
643646
| <type>
644647
| <alias>
645648
| <exportdecl>
646-
importdecl ::= (import <externnamelit> <attributes> bind-id(<externtype>))
647-
exportdecl ::= (export <externnamelit> <attributes> bind-id(<externtype>))
648-
externtype ::= (<sort> (type <idx>) )
649+
importdecl ::= (import <externnamelit> <attributes> <externtype>)
650+
exportdecl ::= (export <externnamelit> <attributes> <externtype>)
651+
externtype ::= bind-id(<externtype'>)
652+
| optional bind-id(<externtype'>) ❓
653+
externtype' ::= (<sort> (type <idx>) )
649654
| core-prefix(<core:moduletype>)
650655
| <functype>
651656
| <componenttype>
@@ -923,9 +928,10 @@ and `export` definitions, respectively, allowing an identifier to be bound for
923928
use by subsequent declarators. The definitions of `labellit`, `externnamelit`
924929
and `attribute` are given in the [imports and exports](#import-and-export-definitions)
925930
section below. Following the precedent of [`core:typeuse`], the text format
926-
allows both references to out-of-line type definitions (via `(type <typeidx>)`)
927-
and inline type expressions that the text format desugars into out-of-line type
928-
definitions.
931+
for `externtype` allows both references to out-of-line type definitions (via
932+
`(type <typeidx>)`) and inline type expressions that the text format desugars
933+
into out-of-line type definitions. The meaning of the `optional` immediate of
934+
`externtype` is given [below](#optionality).
929935

930936
🪙 The `value` case of `externtype` describes a runtime value that is imported or
931937
exported at instantiation time as described in the
@@ -1359,6 +1365,71 @@ distinct. Thus, in a sense, the generativity of resource types *generalizes*
13591365
traditional name-based nominal typing, providing a finer granularity of
13601366
isolation than otherwise achievable with a shared global namespace.
13611367

1368+
#### ❓ Optionality
1369+
1370+
If the type of an import or export (`externtype`) includes the `optional`
1371+
immediate, this indicates that the import or export may not be "present" at
1372+
instantiation time (on an instance-by-instance basis). Regardless of what
1373+
happens at instantiation time, at validation time, `optional` imports and
1374+
exports unconditionally append an index to the sort's index space and include
1375+
`optional` in the type of this index.
1376+
1377+
The meaning of "not present" depends on the `externtype'`:
1378+
* For `func`, "not present" means "traps if called".
1379+
* 🪙 For `value`, "not present" means "a `none` value will be passed into any
1380+
`start` function that takes this value as a parameter". Consequently, an
1381+
import of type `optional (value $T)` is given the value type `(option $T)`
1382+
when validating a `start` call.
1383+
* For `instance`, "not present" means "the contained exports are either
1384+
all-present or all-not-present". Consequently, `alias export $i "e"`
1385+
produces an `optional`-typed alias for `"e"` if `$i` is `optional` and,
1386+
conversely, `alias outer $C $d` introduces a *non*-`optional` alias for
1387+
`$d` even when `$d` is `optional`.
1388+
* For `type`, "not present" means that any dependent `func`, `value` or
1389+
`instance` also becomes "not present". Consequently, any `externtype`
1390+
containing a `func`, `value`, `instance` that transitively refers to an
1391+
`optional` type must itself be `optional`.
1392+
* For `component` and `core module`: these are temporarily rejected by
1393+
validation, but could be supported in the future by refining the rules for
1394+
`instantiate` to account for "not present".
1395+
1396+
For example, the following component exercises these rules and is valid:
1397+
```wat
1398+
(component $C
1399+
(import "t" optional (type $t (sub resource)))
1400+
(import "foo" optional (func (result (own $t))))
1401+
(import "i" optional (instance $i
1402+
(alias outer $C $t (type $t')) ;; non-optional
1403+
(export "bar" (func (result (own $t'))))
1404+
(export "u" (type $u (sub resource)))
1405+
(export "baz" (func (result (own $u))))
1406+
))
1407+
(alias export $i "u" (type $u')) ;; optional
1408+
(alias export $i "baz" (func $baz'))
1409+
(import "quux" optional (func (result (own $u'))))
1410+
)
1411+
```
1412+
The `alias outer` introduces `$t'` as non-`optional` and thus `bar` is not
1413+
required to be `optional`. The nested export of `u` is also non-`optional` and
1414+
thus `baz` is also not required to be `optional`. However, outside the scope of
1415+
`$i`, both `u` and `baz` become `optional` and thus `quux` must be declared
1416+
`optional`.
1417+
1418+
1419+
TODO: regarding subtyping:
1420+
* `(X ...) <: (X optional ...)`
1421+
* `(instance (X optional ...)) <: (instance optional (X ...))`
1422+
* dependently optional on all the parts: present iff all parts are
1423+
1424+
1425+
TODO: regarding substitution
1426+
* for `optional` import:
1427+
* if provide nothing: substitute bottom type (can't name: no valid functions)
1428+
* e.g., but can still lower (to trap) and call `optional.present`
1429+
* else: substitute
1430+
* `optional` is part of what's substituted, so it goes away if provide
1431+
non-optional def to `with`
1432+
* e.g., can export with non-optional type
13621433

13631434
### Canonical Definitions
13641435

@@ -1398,7 +1469,7 @@ two directions:
13981469
Canonical definitions specify one of these two wrapping directions, the function
13991470
to wrap and a list of configuration options:
14001471
```ebnf
1401-
canon ::= (canon lift core-prefix(<core:funcsortidx>) <canonopt>* bind-id(<externtype>))
1472+
canon ::= (canon lift core-prefix(<core:funcsortidx>) <canonopt>* bind-id(<functype>))
14021473
| (canon lower <funcsortidx> <canonopt>* (core func <id>?))
14031474
canonopt ::= string-encoding=utf8
14041475
| string-encoding=utf16
@@ -1409,9 +1480,6 @@ canonopt ::= string-encoding=utf8
14091480
| async 🔀
14101481
| (callback core-prefix(<core:funcidx>)) 🔀
14111482
```
1412-
While the production `externtype` accepts any `sort`, the validation rules
1413-
for `canon lift` would only allow the `func` sort. In the future, other sorts
1414-
may be added (viz., types), hence the explicit sort.
14151483

14161484
The `string-encoding` option specifies the encoding the Canonical ABI will use
14171485
for the `string` type. The `latin1+utf16` encoding captures a common string
@@ -1558,6 +1626,7 @@ canon ::= ...
15581626
| (canon resource.new <typeidx> (core func <id>?))
15591627
| (canon resource.drop <typeidx> (core func <id>?))
15601628
| (canon resource.rep <typeidx> (core func <id>?))
1629+
| (canon optional.present <sortidx> (core global <id>?)) ❓
15611630
| (canon context.get <core:valtype> <u32> (core func <id>?)) 🔀
15621631
| (canon context.set <core:valtype> <u32> (core func <id>?)) 🔀
15631632
| (canon backpressure.inc (core func <id>?)) 🔀
@@ -1684,6 +1753,12 @@ transferring ownership of the newly-created resource to the export's caller.
16841753

16851754
For details, see [`canon_resource_rep`] in the Canonical ABI explainer.
16861755

1756+
##### ❓ Optionality built-ins
1757+
1758+
###### ❓ `optional.present`
1759+
1760+
TODO
1761+
16871762
##### 🔀🧵 Concurrency built-ins
16881763

16891764
See the [concurrency explainer] for background.
@@ -2665,7 +2740,7 @@ exported (e.g., `(export $x "x" (func $f))` binds a new identifier `$x`).
26652740

26662741
Given this, the syntax of imports and exports are defined as follows:
26672742
```ebnf
2668-
import ::= (import <externnamelit> <attributes> bind-id(<externtype>))
2743+
import ::= (import <externnamelit> <attributes> <externtype>)
26692744
export ::= (export <id>? <externnamelit> <attributes> <externidx> <externtype>?)
26702745
attributes ::= <implements>? <versionsuffix>? <externid>?
26712746
implements ::= (implements <interfacenamelit>) 🏷️

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