-
Notifications
You must be signed in to change notification settings - Fork 79
/
Explainer.md
2258 lines (2030 loc) · 103 KB
/
Explainer.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Component Model AST Explainer
This explainer walks through the grammar of a [component](../high-level) and
the proposed embedding of components into native JavaScript runtimes. For a
more user-focused explanation, take a look at the
**[Component Model Documentation]**.
* [Gated features](#gated-features)
* [Grammar](#grammar)
* [Component definitions](#component-definitions)
* [Index spaces](#index-spaces)
* [Instance definitions](#instance-definitions)
* [Alias definitions](#alias-definitions)
* [Type definitions](#type-definitions)
* [Fundamental value types](#fundamental-value-types)
* [Numeric types](#numeric-types)
* [Container types](#container-types)
* [Handle types](#handle-types)
* [Specialized value types](#specialized-value-types)
* [Definition types](#definition-types)
* [Declarators](#declarators)
* [Type checking](#type-checking)
* [Canonical definitions](#canonical-definitions)
* [Canonical ABI](#canonical-abi)
* [Canonical built-ins](#canonical-built-ins)
* [Resource built-ins](#resource-built-ins)
* [Async built-ins](#-async-built-ins)
* [Threading built-ins](#-threading-built-ins)
* [Value definitions](#-value-definitions)
* [Start definitions](#-start-definitions)
* [Import and export definitions](#import-and-export-definitions)
* [Component invariants](#component-invariants)
* [JavaScript embedding](#JavaScript-embedding)
* [JS API](#JS-API)
* [ESM-integration](#ESM-integration)
* [Examples](#examples)
## Gated Features
By default, the features described in this explainer (as well as the supporting
[Binary.md](Binary.md), [WIT.md](WIT.md) and [CanonicalABI.md](CanonicalABI.md))
have been implemented and are included in the [WASI Preview 2] stability
milestone. Features that are not part of Preview 2 are demarcated by one of the
emoji symbols listed below; these emojis will be removed once they are
implemented, considered stable and included in a future milestone:
* 🪙: value imports/exports and component-level start function
* 🪺: nested namespaces and packages in import/export names
* 🔀: async
* 🧵: threading built-ins
(Based on the previous [scoping and layering] proposal to the WebAssembly CG,
this repo merges and supersedes the [module-linking] and [interface-types]
proposals, pushing some of their original features into the post-MVP [future
feature](FutureFeatures.md) backlog.)
## Grammar
This section defines components using an EBNF grammar that parses something in
between a pure Abstract Syntax Tree (like the Core WebAssembly spec's
[Structure Section]) and a complete text format (like the Core WebAssembly
spec's [Text Format Section]). The goal is to balance completeness with
succinctness, with just enough detail to write examples and define a [binary
format](Binary.md) in the style of the [Binary Format Section], deferring full
precision to the [formal specification](../../spec/).
The main way the grammar hand-waves is regarding definition uses, where indices
referring to `X` definitions (written `<Xidx>`) should, in the real text
format, explicitly allow identifiers (`<id>`), checking at parse time that the
identifier resolves to an `X` definition and then embedding the resolved index
into the AST.
Additionally, standard [abbreviations] defined by the Core WebAssembly text
format (e.g., inline export definitions) are assumed but not explicitly defined
below.
### Component Definitions
At the top-level, a `component` is a sequence of definitions of various kinds:
```ebnf
component ::= (component <id>? <definition>*)
definition ::= core-prefix(<core:module>)
| core-prefix(<core:instance>)
| core-prefix(<core:type>)
| <component>
| <instance>
| <alias>
| <type>
| <canon>
| <start> 🪺
| <import>
| <export>
| <value> 🪙
where core-prefix(X) parses '(' 'core' Y ')' when X parses '(' Y ')'
```
Components are like Core WebAssembly modules in that their contained
definitions are acyclic: definitions can only refer to preceding definitions
(in the AST, text format and binary format). However, unlike modules,
components can arbitrarily interleave different kinds of definitions.
The `core-prefix` meta-function transforms a grammatical rule for parsing a
Core WebAssembly definition into a grammatical rule for parsing the same
definition, but with a `core` token added right after the leftmost paren.
For example, `core:module` accepts `(module (func))` so
`core-prefix(<core:module>)` accepts `(core module (func))`. Note that the
inner `func` doesn't need a `core` prefix; the `core` token is used to mark the
*transition* from parsing component definitions into core definitions.
The [`core:module`] production is unmodified by the Component Model and thus
components embed Core WebAssembly (text and binary format) modules as currently
standardized, allowing reuse of an unmodified Core WebAssembly implementation.
The next production, `core:instance`, is not currently included in Core
WebAssembly, but would be if Core WebAssembly adopted the [module-linking]
proposal. This new core definition is introduced below, alongside its
component-level counterpart. Finally, the existing [`core:type`] production is
extended below to add core module types as proposed for module-linking. Thus,
the overall idea is to represent core definitions (in the AST, binary and text
format) as-if they had already been added to Core WebAssembly so that, if they
eventually are, the implementation of decoding and validation can be shared in
a layered fashion.
The next kind of definition is, recursively, a component itself. Thus,
components form trees with all other kinds of definitions only appearing at the
leaves. For example, with what's defined so far, we can write the following
component:
```wasm
(component
(component
(core module (func (export "one") (result i32) (i32.const 1)))
(core module (func (export "two") (result f32) (f32.const 2)))
)
(core module (func (export "three") (result i64) (i64.const 3)))
(component
(component
(core module (func (export "four") (result f64) (f64.const 4)))
)
)
(component)
)
```
This top-level component roots a tree with 4 modules and 1 component as
leaves. However, in the absence of any `instance` definitions (introduced
next), nothing will be instantiated or executed at runtime; everything here is
dead code.
#### Index Spaces
[Like Core WebAssembly][Core Indices], the Component Model places each
`definition` into one of a fixed set of *index spaces*, allowing the
definition to be referred to by subsequent definitions (in the text and binary
format) via a nonnegative integral *index*. When defining, validating and
executing a component, there are 5 component-level index spaces:
* (component) functions
* (component) values
* (component) types
* component instances
* components
5 core index spaces that also exist in WebAssembly 1.0:
* (core) functions
* (core) tables
* (core) memories
* (core) globals
* (core) types
and 2 additional core index spaces that contain core definition introduced by
the Component Model that are not in WebAssembly 1.0 (yet: the [module-linking]
proposal would add them):
* module instances
* modules
for a total of 12 index spaces that need to be maintained by an implementation
when, e.g., validating a component. These 12 index spaces correspond 1:1 with
the terminals of the `sort` production defined below and thus "sort" and
"index space" can be used interchangeably.
Also [like Core WebAssembly][Core Identifiers], the Component Model text format
allows *identifiers* to be used in place of these indices, which are resolved
when parsing into indices in the AST (upon which validation and execution is
defined). Thus, the following two components are equivalent:
```wasm
(component
(core module (; empty ;))
(component (; empty ;))
(core module (; empty ;))
(export "C" (component 0))
(export "M1" (core module 0))
(export "M2" (core module 1))
)
```
```wasm
(component
(core module $M1 (; empty ;))
(component $C (; empty ;))
(core module $M2 (; empty ;))
(export "C" (component $C))
(export "M1" (core module $M1))
(export "M2" (core module $M2))
)
```
### Instance Definitions
Whereas modules and components represent immutable *code*, instances associate
code with potentially-mutable *state* (e.g., linear memory) and thus are
necessary to create before being able to *run* the code. Instance definitions
create module or component instances by selecting a module or component to
**instantiate** and then supplying a set of named *arguments* which satisfy all
the named *imports* of the selected module or component. This low-level
instantiation mechanism allows the Component Model to simultaneously support
multiple different styles of traditional [linking](Linking.md).
The syntax for defining a core module instance is:
```ebnf
core:instance ::= (instance <id>? <core:instancexpr>)
core:instanceexpr ::= (instantiate <core:moduleidx> <core:instantiatearg>*)
| <core:inlineexport>*
core:instantiatearg ::= (with <core:name> (instance <core:instanceidx>))
| (with <core:name> (instance <core:inlineexport>*))
core:sortidx ::= (<core:sort> <u32>)
core:sort ::= func
| table
| memory
| global
| type
| module
| instance
core:inlineexport ::= (export <core:name> <core:sortidx>)
```
When instantiating a module via `instantiate`, the two-level imports of the
core modules are resolved as follows:
1. The first `core:name` of the import is looked up in the named list of
`core:instantiatearg` to select a core module instance. (In the future,
other `core:sort`s could be allowed if core wasm adds single-level
imports.)
2. The second `core:name` of the import is looked up in the named list of
exports of the core module instance found by the first step to select the
imported core definition.
Each `core:sort` corresponds 1:1 with a distinct [index space] that contains
only core definitions of that *sort*. The `u32` field of `core:sortidx`
indexes into the sort's associated index space to select a definition.
Based on this, we can link two core modules `$A` and `$B` together with the
following component:
```wasm
(component
(core module $A
(func (export "one") (result i32) (i32.const 1))
)
(core module $B
(func (import "a" "one") (result i32))
)
(core instance $a (instantiate $A))
(core instance $b (instantiate $B (with "a" (instance $a))))
)
```
To see examples of other sorts, we'll need `alias` definitions, which are
introduced in the next section.
The `<core:inlineexport>*` form of `core:instanceexpr` allows module instances
to be created by directly tupling together preceding definitions, without the
need to `instantiate` a helper module. The `<core:inlineexport>*` form of
`core:instantiatearg` is syntactic sugar that is expanded during text format
parsing into an out-of-line instance definition referenced by `with`. To show
an example of these, we'll also need the `alias` definitions introduced in the
next section.
The syntax for defining component instances is symmetric to core module
instances, but with an expanded component-level definition of `sort`:
```ebnf
instance ::= (instance <id>? <instanceexpr>)
instanceexpr ::= (instantiate <componentidx> <instantiatearg>*)
| <inlineexport>*
instantiatearg ::= (with <name> <sortidx>)
| (with <name> (instance <inlineexport>*))
name ::= <core:name>
sortidx ::= (<sort> <u32>)
sort ::= core <core:sort>
| func
| value 🪙
| type
| component
| instance
inlineexport ::= (export <exportname> <sortidx>)
```
Because component-level function, type and instance definitions are different
than core-level function, type and instance definitions, they are put into
disjoint index spaces which are indexed separately. Components may import
and export various core definitions (when they are compatible with the
[shared-nothing] model, which currently means only `module`, but may in the
future include `data`). Thus, component-level `sort` injects the full set
of `core:sort`, so that they may be referenced (leaving it up to validation
rules to throw out the core sorts that aren't allowed in various contexts).
The `name` production reuses the `core:name` quoted-string-literal syntax of
Core WebAssembly (which appears in core module imports and exports and can
contain any valid UTF-8 string).
🪙 The `value` sort refers to a value that is provided and consumed during
instantiation. How this works is described in the
[value definitions](#value-definitions) section.
To see a non-trivial example of component instantiation, we'll first need to
introduce a few other definitions below that allow components to import, define
and export component functions.
### Alias Definitions
Alias definitions project definitions out of other components' index spaces and
into the current component's index spaces. As represented in the AST below,
there are three kinds of "targets" for an alias: the `export` of a component
instance, the `core export` of a core module instance and a definition of an
`outer` component (containing the current component):
```ebnf
alias ::= (alias <aliastarget> (<sort> <id>?))
aliastarget ::= export <instanceidx> <name>
| core export <core:instanceidx> <core:name>
| outer <u32> <u32>
```
If present, the `id` of the alias is bound to the new index added by the alias
and can be used anywhere a normal `id` can be used.
In the case of `export` aliases, validation ensures `name` is an export in the
target instance and has a matching sort.
In the case of `outer` aliases, the `u32` pair serves as a [de Bruijn
index], with first `u32` being the number of enclosing components/modules to
skip and the second `u32` being an index into the target's sort's index space.
In particular, the first `u32` can be `0`, in which case the outer alias refers
to the current component. To maintain the acyclicity of module instantiation,
outer aliases are only allowed to refer to *preceding* outer definitions.
Components containing outer aliases effectively produce a [closure] at
instantiation time, including a copy of the outer-aliased definitions. Because
of the prevalent assumption that components are immutable values, outer aliases
are restricted to only refer to immutable definitions: non-resource types,
modules and components. (In the future, outer aliases to all sorts of
definitions could be allowed by recording the statefulness of the resulting
component in its type via some kind of "`stateful`" type attribute.)
Both kinds of aliases come with syntactic sugar for implicitly declaring them
inline:
For `export` aliases, the inline sugar extends the definition of `sortidx`
and the various sort-specific indices:
```ebnf
sortidx ::= (<sort> <u32>) ;; as above
| <inlinealias>
Xidx ::= <u32> ;; as above
| <inlinealias>
inlinealias ::= (<sort> <u32> <name>+)
```
If `<sort>` refers to a `<core:sort>`, then the `<u32>` of `inlinealias` is a
`<core:instanceidx>`; otherwise it's an `<instanceidx>`. For example, the
following snippet uses two inline function aliases:
```wasm
(instance $j (instantiate $J (with "f" (func $i "f"))))
(export "x" (func $j "g" "h"))
```
which are desugared into:
```wasm
(alias export $i "f" (func $f_alias))
(instance $j (instantiate $J (with "f" (func $f_alias))))
(alias export $j "g" (instance $g_alias))
(alias export $g_alias "h" (func $h_alias))
(export "x" (func $h_alias))
```
For `outer` aliases, the inline sugar is simply the identifier of the outer
definition, resolved using normal lexical scoping rules. For example, the
following component:
```wasm
(component
(component $C ...)
(component
(instance (instantiate $C))
)
)
```
is desugared into:
```wasm
(component $Parent
(component $C ...)
(component
(alias outer $Parent $C (component $Parent_C))
(instance (instantiate $Parent_C))
)
)
```
Lastly, for symmetry with [imports][func-import-abbrev], aliases can be written
in an inverted form that puts the sort first:
```wasm
(func $f (import "i" "f") ...type...) ≡ (import "i" "f" (func $f ...type...)) (WebAssembly 1.0)
(func $f (alias export $i "f")) ≡ (alias export $i "f" (func $f))
(core module $m (alias export $i "m")) ≡ (alias export $i "m" (core module $m))
(core func $f (alias core export $i "f")) ≡ (alias core export $i "f" (core func $f))
```
With what's defined so far, we're able to link modules with arbitrary renamings:
```wasm
(component
(core module $A
(func (export "one") (result i32) (i32.const 1))
(func (export "two") (result i32) (i32.const 2))
(func (export "three") (result i32) (i32.const 3))
)
(core module $B
(func (import "a" "one") (result i32))
)
(core instance $a (instantiate $A))
(core instance $b1 (instantiate $B
(with "a" (instance $a)) ;; no renaming
))
(core func $a_two (alias core export $a "two")) ;; ≡ (alias core export $a "two" (core func $a_two))
(core instance $b2 (instantiate $B
(with "a" (instance
(export "one" (func $a_two)) ;; renaming, using out-of-line alias
))
))
(core instance $b3 (instantiate $B
(with "a" (instance
(export "one" (func $a "three")) ;; renaming, using <inlinealias>
))
))
)
```
To show analogous examples of linking components, we'll need component-level
type and function definitions which are introduced in the next two sections.
### Type Definitions
The syntax for defining core types extends the existing core type definition
syntax, adding a `module` type constructor:
```ebnf
core:rectype ::= ... from the Core WebAssembly spec
core:typedef ::= ... from the Core WebAssembly spec
core:subtype ::= ... from the Core WebAssembly spec
core:comptype ::= ... from the Core WebAssembly spec
| <core:moduletype>
core:moduletype ::= (module <core:moduledecl>*)
core:moduledecl ::= <core:importdecl>
| <core:type>
| <core:alias>
| <core:exportdecl>
core:alias ::= (alias <core:aliastarget> (<core:sort> <id>?))
core:aliastarget ::= outer <u32> <u32>
core:importdecl ::= (import <core:name> <core:name> <core:importdesc>)
core:exportdecl ::= (export <core:name> <core:exportdesc>)
core:exportdesc ::= strip-id(<core:importdesc>)
where strip-id(X) parses '(' sort Y ')' when X parses '(' sort <id>? Y ')'
```
Here, `core:comptype` (short for "composite type") as defined in the [GC]
proposal is extended with a `module` type constructor. The GC proposal also
adds recursion and explicit subtyping between core wasm types. Owing to
their different requirements and intended modes of usage, module types
support implicit subtyping and are not recursive. Thus, the existing core
validation rules would require the declared supertypes of module types to be
empty and disallow recursive use of module types.
In the MVP, validation will also reject `core:moduletype` defining or aliasing
other `core:moduletype`s, since, before module-linking, core modules cannot
themselves import or export other core modules.
The body of a module type contains an ordered list of "module declarators"
which describe, at a type level, the imports and exports of the module. In a
module-type context, import and export declarators can both reuse the existing
[`core:importdesc`] production defined in WebAssembly 1.0, with the only
difference being that, in the text format, `core:importdesc` can bind an
identifier for later reuse while `core:exportdesc` cannot.
With the Core WebAssembly [type-imports], module types will need the ability to
define the types of exports based on the types of imports. In preparation for
this, module types start with an empty type index space that is populated by
`type` declarators, so that, in the future, these `type` declarators can refer to
type imports local to the module type itself. For example, in the future, the
following module type would be expressible:
```wasm
(component $C
(core type $M (module
(import "" "T" (type $T))
(type $PairT (struct (field (ref $T)) (field (ref $T))))
(export "make_pair" (func (param (ref $T)) (result (ref $PairT))))
))
)
```
In this example, `$M` has a distinct type index space from `$C`, where element
0 is the imported type, element 1 is the `struct` type, and element 2 is an
implicitly-created `func` type referring to both.
Lastly, the `core:alias` module declarator allows a module type definition to
reuse (rather than redefine) type definitions in the enclosing component's core
type index space via `outer` `type` alias. In the MVP, validation restricts
`core:alias` module declarators to *only* allow `outer` `type` aliases (into an
enclosing component's or component-type's core type index space). In the
future, more kinds of aliases would be meaningful and allowed.
As an example, the following component defines two semantically-equivalent
module types, where the former defines the function type via `type` declarator
and the latter refers via `alias` declarator.
```wasm
(component $C
(core type $C1 (module
(type (func (param i32) (result i32)))
(import "a" "b" (func (type 0)))
(export "c" (func (type 0)))
))
(core type $F (func (param i32) (result i32)))
(core type $C2 (module
(alias outer $C $F (type))
(import "a" "b" (func (type 0)))
(export "c" (func (type 0)))
))
)
```
Component-level type definitions are symmetric to core-level type definitions,
but use a completely different set of value types. Unlike [`core:valtype`]
which is low-level and assumes a shared linear memory for communicating
compound values, component-level value types assume no shared memory and must
therefore be high-level, describing entire compound values.
```ebnf
type ::= (type <id>? <deftype>)
deftype ::= <defvaltype>
| <resourcetype>
| <functype>
| <componenttype>
| <instancetype>
defvaltype ::= bool
| s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64
| f32 | f64
| char | string
| (record (field "<label>" <valtype>)+)
| (variant (case "<label>" <valtype>?)+)
| (list <valtype>)
| (tuple <valtype>+)
| (flags "<label>"+)
| (enum "<label>"+)
| (option <valtype>)
| (result <valtype>? (error <valtype>)?)
| (own <typeidx>)
| (borrow <typeidx>)
valtype ::= <typeidx>
| <defvaltype>
resourcetype ::= (resource (rep i32) (dtor async? <funcidx> (callback <funcidx>)?)?)
functype ::= (func (param "<label>" <valtype>)* (result <valtype>)?)
componenttype ::= (component <componentdecl>*)
instancetype ::= (instance <instancedecl>*)
componentdecl ::= <importdecl>
| <instancedecl>
instancedecl ::= core-prefix(<core:type>)
| <type>
| <alias>
| <exportdecl>
| <value> 🪙
importdecl ::= (import <importname> bind-id(<externdesc>))
exportdecl ::= (export <exportname> bind-id(<externdesc>))
externdesc ::= (<sort> (type <u32>) )
| core-prefix(<core:moduletype>)
| <functype>
| <componenttype>
| <instancetype>
| (value <valuebound>) 🪙
| (type <typebound>)
typebound ::= (eq <typeidx>)
| (sub resource)
valuebound ::= (eq <valueidx>) 🪙
| <valtype> 🪙
where bind-id(X) parses '(' sort <id>? Y ')' when X parses '(' sort Y ')'
```
Because there is nothing in this type grammar analogous to the [gc] proposal's
[`rectype`], none of these types are recursive.
#### Fundamental value types
The value types in `valtype` can be broken into two categories: *fundamental*
value types and *specialized* value types, where the latter are defined by
expansion into the former. The *fundamental value types* have the following
sets of abstract values:
| Type | Values |
| ------------------------- | ------ |
| `bool` | `true` and `false` |
| `s8`, `s16`, `s32`, `s64` | integers in the range [-2<sup>N-1</sup>, 2<sup>N-1</sup>-1] |
| `u8`, `u16`, `u32`, `u64` | integers in the range [0, 2<sup>N</sup>-1] |
| `f32`, `f64` | [IEEE754] floating-point numbers, with a single NaN value |
| `char` | [Unicode Scalar Values] |
| `record` | heterogeneous [tuples] of named values |
| `variant` | heterogeneous [tagged unions] of named values |
| `list` | homogeneous, variable-length [sequences] of values |
| `own` | a unique, opaque address of a resource that will be destroyed when this value is dropped |
| `borrow` | an opaque address of a resource that must be dropped before the current export call returns |
How these abstract values are produced and consumed from Core WebAssembly
values and linear memory is configured by the component via *canonical lifting
and lowering definitions*, which are introduced [below](#canonical-definitions).
For example, while abstract `variant`s contain a list of `case`s labelled by
name, canonical lifting and lowering map each case to an `i32` value starting
at `0`.
##### Numeric types
While core numeric types are defined in terms of sets of bit-patterns and
operations that interpret the bits in various ways, component-level numeric
types are defined in terms of sets of values. This allows the values to be
translated between source languages and protocols that use different
value representations.
Core integer types are just bit-patterns that don't distinguish between signed
and unsigned, while component-level integer types are sets of integers that
either include negative values or don't. Core floating-point types have many
distinct NaN bit-patterns, while component-level floating-point types have only
a single NaN value. And boolean values in core wasm are usually represented as
`i32`s where operations interpret all-zeros as `false`, while at the
component-level there is a `bool` type with `true` and `false` values.
##### Container types
The `record`, `variant`, and `list` types allow for grouping, categorizing,
and sequencing contained values.
##### Handle types
The `own` and `borrow` value types are both *handle types*. Handles logically
contain the opaque address of a resource and avoid copying the resource when
passed across component boundaries. By way of metaphor to operating systems,
handles are analogous to file descriptors, which are stored in a table and may
only be used indirectly by untrusted user-mode processes via their integer
index in the table.
In the Component Model, handles are lifted-from and lowered-into `i32` values
that index an encapsulated per-component-instance *handle table* that is
maintained by the canonical function definitions described
[below](#canonical-definitions). In the future, handles could be
backwards-compatibly lifted and lowered from [reference types] (via the
addition of a new `canonopt`, as introduced [below](#canonical-abi)).
The uniqueness and dropping conditions mentioned above are enforced at runtime
by the Component Model through these canonical definitions. The `typeidx`
immediate of a handle type must refer to a `resource` type (described below)
that statically classifies the particular kinds of resources the handle can
point to.
#### Specialized value types
The sets of values allowed for the remaining *specialized value types* are
defined by the following mapping:
```
(tuple <valtype>*) ↦ (record (field "𝒊" <valtype>)*) for 𝒊=0,1,...
(flags "<label>"*) ↦ (record (field "<label>" bool)*)
(enum "<label>"+) ↦ (variant (case "<label>")+)
(option <valtype>) ↦ (variant (case "none") (case "some" <valtype>))
(result <valtype>? (error <valtype>)?) ↦ (variant (case "ok" <valtype>?) (case "error" <valtype>?))
string ↦ (list char)
```
Specialized value types have the same set of semantic values as their
corresponding despecialized types, but have distinct type constructors
(which are not type-equal to the unspecialized type constructors) and
thus have distinct binary encodings. This allows specialized value types to
convey a more specific intent. For example, `result` isn't just a variant,
it's a variant that *means* success or failure, so source-code bindings
can expose it via idiomatic source-language error reporting. Additionally,
this can sometimes allow values to be represented differently. For example,
`string` in the Canonical ABI uses various Unicode encodings while
`list<char>` uses a sequence of 4-byte `char` code points. Similarly,
`flags` in the Canonical ABI uses a bit-vector while an equivalent record
of boolean fields uses a sequence of boolean-valued bytes.
Note that, at least initially, variants are required to have a non-empty list of
cases. This could be relaxed in the future to allow an empty list of cases, with
the empty `(variant)` effectively serving as an [empty type] and indicating
unreachability.
#### Definition types
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`.
The `resource` type constructor creates a fresh type for each instance of the
containing component (with "freshness" and its interaction with general
type-checking described in more detail [below](#type-checking)). Resource types
can be referred to by handle types (such as `own` and `borrow`) as well as the
canonical built-ins described [below](#canonical-built-ins). The `rep`
immediate of a `resource` type specifies its *core representation type*, which
is currently fixed to `i32`, but will be relaxed in the future (to at least
include `i64`, but also potentially other types). When the last handle to a
resource is dropped, the resource's destructor function specified by the `dtor`
immediate will be called (if present), allowing the implementing component to
perform clean-up like freeing linear memory allocations. Destructors can be
declared `async`, with the same meaning for the `async` and `callback`
immediates as described below for `canon lift`.
The `instance` type constructor describes a list of named, typed definitions
that can be imported or exported by a component. Informally, instance types
correspond to the usual concept of an "interface" and instance types thus serve
as static interface descriptions. In addition to the S-Expression text format
defined here, which is meant to go inside component definitions, interfaces can
also be defined as standalone, human-friendly text files in the [`wit`](WIT.md)
[Interface Definition Language].
The `component` type constructor is symmetric to the core `module` type
constructor and contains *two* lists of named definitions for the imports
and exports of a component, respectively. As suggested above, instance types
can show up in *both* the import and export types of a component type.
Both `instance` and `component` type constructors are built from a sequence of
"declarators", of which there are four kinds—`type`, `alias`, `import` and
`export`—where only `component` type constructors can contain `import`
declarators. The meanings of these declarators is basically the same as the
core module declarators introduced above, but expanded to cover the additional
capabilities of the component model.
#### Declarators
The `importdecl` and `exportdecl` declarators correspond to component `import`
and `export` definitions, respectively, allowing an identifier to be bound for
use by subsequent declarators. The definitions of `label`, `importname` and
`exportname` are given in the [imports and exports](#import-and-export-definitions)
section below. Following the precedent of [`core:typeuse`], the text format
allows both references to out-of-line type definitions (via `(type <typeidx>)`)
and inline type expressions that the text format desugars into out-of-line type
definitions.
🪙 The `value` case of `externdesc` describes a runtime value that is imported or
exported at instantiation time as described in the
[value definitions](#value-definitions) section below.
The `type` case of `externdesc` describes an imported or exported type along
with its "bound":
The `sub` bound declares that the imported/exported type is an *abstract type*
which is a *subtype* of some other type. Currently, the only supported bound is
`resource` which (following the naming conventions of the [GC] proposal) means
"any resource type". Thus, only resource types can be imported/exported
abstractly, not arbitrary value types. This allows type imports to always be
compiled independently of their arguments using a "universal representation" for
handle values (viz., `i32`, as defined by the [Canonical ABI](CanonicalABI.md)).
In the future, `sub` may be extended to allow referencing other resource types,
thereby allowing abstract resource subtyping.
The `eq` bound says that the imported/exported type must be structurally equal
to some preceding type definition. This allows:
* an imported abstract type to be re-exported;
* components to introduce another label for a preceding abstract type (which
can be necessary when implementing multiple independent interfaces with the
same resource); and
* components to attach transparent type aliases to structural types to be
reflected in source-level bindings (e.g., `(export "bytes" (type (eq (list u64))))`
could generate in C++ a `typedef std::vector<uint64_t> bytes` or in JS an
exported field named `bytes` that aliases `Uint64Array`.
Relaxing the restrictions of `core:alias` declarators mentioned above, `alias`
declarators allow both `outer` and `export` aliases of `type` and `instance`
sorts. This allows the type exports of `instance`-typed import and export
declarators to be used by subsequent declarators in the type:
```wasm
(component
(import "fancy-fs" (instance $fancy-fs
(export $fs "fs" (instance
(export "file" (type (sub resource)))
;; ...
))
(alias export $fs "file" (type $file))
(export "fancy-op" (func (param "f" (borrow $file))))
))
)
```
The `type` declarator is restricted by validation to disallow `resource` type
definitions, thereby preventing "private" resource type definitions from
appearing in component types and avoiding the [avoidance problem]. Thus, the
only resource types possible in an `instancetype` or `componenttype` are
introduced by `importdecl` or `exportdecl`.
With what's defined so far, we can define component types using a mix of type
definitions:
```wasm
(component $C
(type $T (list (tuple string bool)))
(type $U (option $T))
(type $G (func (param "x" (list $T)) (result $U)))
(type $D (component
(alias outer $C $T (type $C_T))
(type $L (list $C_T))
(import "f" (func (param "x" $L) (result (list u8))))
(import "g" (func (type $G)))
(export "g2" (func (type $G)))
(export "h" (func (result $U)))
(import "T" (type $T (sub resource)))
(import "i" (func (param "x" (list (own $T)))))
(export "T2" (type $T' (eq $T)))
(export "U" (type $U' (sub resource)))
(export "j" (func (param "x" (borrow $T')) (result (own $U'))))
))
)
```
Note that the inline use of `$G` and `$U` are syntactic sugar for `outer`
aliases.
#### Type Checking
Like core modules, components have an up-front validation phase in which the
definitions of a component are checked for basic consistency. Type checking
is a central part of validation and, e.g., occurs when validating that the
`with` arguments of an [`instantiate`](#instance-definitions) expression are
type-compatible with the `import`s of the component being instantiated.
To incrementally describe how type-checking works, we'll start by asking how
*type equality* works for non-resource, non-handle, local type definitions and
build up from there.
Type equality for almost all types (except as described below) is purely
*structural*. In a structural setting, types are considered to be Abstract
Syntax Trees whose nodes are type constructors with types like `u8` and
`string` considered to be "nullary" type constructors that appear at leaves and
non-nullary type constructors like `list` and `record` appearing at parent
nodes. Then, type equality is defined to be AST equality. Importantly, these
type ASTs do *not* contain any type indices or depend on index space layout;
these binary format details are consumed by decoding to produce the AST. For
example, in the following compound component:
```wasm
(component $A
(type $ListString1 (list string))
(type $ListListString1 (list $ListString1))
(type $ListListString2 (list $ListString1))
(component $B
(type $ListString2 (list string))
(type $ListListString3 (list $ListString2))
(type $ListString3 (alias outer $A $ListString1))
(type $ListListString4 (list $ListString3))
(type $ListListString5 (alias outer $A $ListListString1))
)
)
```
all 5 variations of `$ListListStringX` are considered equal since, after
decoding, they all have the same AST.
Next, the type equality relation on ASTs is relaxed to a more flexible
[subtyping] relation. Currently, subtyping is only relaxed for `instance` and
`component` types, but may be relaxed for more type constructors in the future
to better support API Evolution (being careful to understand how subtyping
manifests itself in the wide variety of source languages so that
subtype-compatible updates don't inadvertantly break source-level clients).
Component and instance subtyping allows a subtype to export more and import
less than is declared by the supertype, ignoring the exact order of imports and
exports and considering only names. For example, here, `$I1` is a subtype of
`$I2`:
```wat
(component
(type $I1 (instance
(export "foo" (func))
(export "bar" (func))
(export "baz" (func))
))
(type $I2 (instance
(export "bar" (func))
(export "foo" (func))
))
)
```
and `$C1` is a subtype of `$C2`:
```wat
(component
(type $C1 (component
(import "a" (func))
(export "x" (func))
(export "y" (func))
))
(type $C2 (component
(import "a" (func))
(import "b" (func))
(export "x" (func))
))
)
```
When we next consider type imports and exports, there are two distinct
subcases of `typebound` to consider: `eq` and `sub`.
The `eq` bound adds a type equality rule (extending the built-in set of
subtyping rules mentioned above) saying that the imported type is structurally
equivalent to the type referenced in the bound. For example, in the component:
```wasm
(component
(type $L1 (list u8))
(import "L2" (type $L2 (eq $L1)))
(import "L3" (type $L2 (eq $L1)))
(import "L4" (type $L2 (eq $L3)))
)
```
all four `$L*` types are equal (in subtyping terms, they are all subtypes of
each other).
In contrast, the `sub` bound introduces a new *abstract* type which the rest of
the component must conservatively assume can be *any* type that is a subtype of
the bound. What this means for type-checking is that each subtype-bound type
import/export introduces a *fresh* abstract type that is unequal to every
preceding type definition. Currently (and likely in the MVP), the only
supported type bound is `resource` (which means "any resource type") and thus
the only abstract types are abstract *resource* types. As an example, in the
following component:
```wasm
(component
(import "T1" (type $T1 (sub resource)))
(import "T2" (type $T2 (sub resource)))
)
```
the types `$T1` and `$T2` are not equal.
Once a type is imported, it can be referred to by subsequent equality-bound
type imports, thereby adding more types that it is equal to. For example, in
the following component:
```wasm
(component $C
(import "T1" (type $T1 (sub resource)))
(import "T2" (type $T2 (sub resource)))
(import "T3" (type $T3 (eq $T2)))
(type $ListT1 (list (own $T1)))
(type $ListT2 (list (own $T2)))
(type $ListT3 (list (own $T3)))
)
```
the types `$T2` and `$T3` are equal to each other but not to `$T1`. By the
above transitive structural equality rules, the types `$List2` and `$List3` are
equal to each other but not to `$List1`.
Handle types (`own` and `borrow`) are structural types (like `list`) but, since
they refer to resource types, transitively "inherit" the freshness of abstract
resource types. For example, in the following component:
```wasm
(component
(import "T" (type $T (sub resource)))
(import "U" (type $U (sub resource)))
(type $Own1 (own $T))
(type $Own2 (own $T))
(type $Own3 (own $U))
(type $ListOwn1 (list $Own1))
(type $ListOwn2 (list $Own2))
(type $ListOwn3 (list $Own3))
(type $Borrow1 (borrow $T))
(type $Borrow2 (borrow $T))
(type $Borrow3 (borrow $U))
(type $ListBorrow1 (list $Borrow1))
(type $ListBorrow2 (list $Borrow2))
(type $ListBorrow3 (list $Borrow3))
)
```
the types `$Own1` and `$Own2` are equal to each other but not to `$Own3` or
any of the `$Borrow*`. Similarly, `$Borrow1` and `$Borrow2` are equal to
each other but not `$Borrow3`. Transitively, the types `$ListOwn1` and
`$ListOwn2` are equal to each other but not `$ListOwn3` or any of the
`$ListBorrow*`. These type-checking rules for type imports mirror the
*introduction* rule of [universal types] (∀T).
The above examples all show abstract types in terms of *imports*, but the same
"freshness" condition applies when aliasing the *exports* of another component
as well. For example, in this component:
```wasm
(component
(import "C" (component $C
(export "T1" (type (sub resource)))
(export "T2" (type $T2 (sub resource)))
(export "T3" (type (eq $T2)))
))
(instance $c (instantiate $C))
(alias export $c "T1" (type $T1))
(alias export $c "T2" (type $T2))
(alias export $c "T3" (type $T3))
)
```
the types `$T2` and `$T3` are equal to each other but not to `$T1`. These
type-checking rules for aliases of type exports mirror the *elimination* rule
of [existential types] (∃T).
Next, we consider resource type *definitions* which are a *third* source of
abstract types. Unlike the abstract types introduced by type imports and
exports, resource type definitions provide canonical built-ins for setting and
getting a resource's private representation value (that are introduced
[below](#canonical-built-ins)). These built-ins are necessarily scoped to the
component instance that generated the resource type, thereby hiding access to a
resource type's representation from the outside world. Because each component
instantiation generates fresh resource types distinct from all preceding
instances of the same component, resource types are ["generative"].
For example, in the following example component: