@@ -227,7 +227,7 @@ This may be relaxed in the future.
227
227
)
228
228
```
229
229
230
- ## Exact Reference Types
230
+ ## Exact Types
231
231
232
232
Allocating an instance of a type with a custom descriptor necessarily
233
233
requires supplying a custom descriptor value.
@@ -264,59 +264,65 @@ describes precisely the allocated type.
264
264
A better solution would be to allow userspace to perform that check if necessary,
265
265
but also be able to statically prove via the type system that it is not necessary.
266
266
267
- To facilitate that we introduce exact reference types,
268
- which are inhabited by references to a particular heap type (and possibly the null value),
269
- but not any of the heap type's strict subtypes.
267
+ To facilitate that we introduce exact heap types,
268
+ which are subtypes of their base heap types
269
+ but not supertypes of their base heap types' declared subtypes.
270
270
271
- Exact reference types can be nullable with the form ` (ref null exact ht) `
272
- or non-nullable with the form ` (ref exact ht) ` .
271
+ ```
272
+ heaptype ::= absheaptype | exact typeidx | typeidx
273
+ ```
274
+
275
+ The subtyping rules for heap types are extended:
273
276
274
- For any heap type ` ht ` :
275
277
276
278
```
277
- (ref ht) <: (ref null ht)
278
- (ref null exact ht) <: (ref null ht)
279
- (ref exact ht) <: (ref ht)
280
- (ref exact ht) <: (ref null exact ht)
279
+ C |- (exact typeidx_1) <: typeidx_1
280
+
281
281
```
282
282
283
- Furthermore, to ensure that each type hierarchy remains a lattice,
284
- we have:
283
+ Notably, by the existing, unmodified rules for ` none ` , ` nofunc ` , etc.
284
+ it is the case that e.g. ` none <: (exact $some_struct) ` .
285
+ Given these types:
285
286
286
287
```
287
- (ref null exact bot) <: (ref null exact ht )
288
- (ref exact bot) <: (ref exact ht )
288
+ (type $super (sub (struct)) )
289
+ (type $sub (sub $super (struct)) )
289
290
```
290
291
291
- where ` bot ` is the bottom type
292
- (e.g. ` none ` , ` nofunc ` , ` noextern ` , ` noexnt ` , or ` nocont ` ) of ` ht ` 's hierarchy.
292
+ We have the following relationships:
293
+
294
+ ```
295
+ none <: (exact $sub) <: $sub <: $super <: struct <: eq <: any
296
+ (exact $super) <: $super <: ...
297
+ ```
298
+
299
+ But no version of ` $sub ` is in a subtyping relation with ` (exact $super) ` .
300
+
293
301
294
- All instructions that create reference to a particular defined heap type
302
+ All instructions that create references to a particular defined heap type
295
303
(e.g. ` ref.func ` , ` struct.new ` , ` array.new ` , etc.)
296
- are refined to produce exact references to that heap type.
297
- In contrast, instructions that produce references to abstract heap types
298
- generally do not produce exact references.
299
- This preserves our ability to assign them more specific types in the future.
300
- For example, ` ref.i31 ` produces ` (ref i31) ` , not ` (ref exact i31) ` ,
301
- and casting an ` i31 ` reference (or anything else) to ` (ref exact i31) ` will fail.
302
- The only exception to this rule is that ` ref.null ht ` is typed ` (ref null exact ht) ` .
303
- This allows ` ref.null none ` to be used where a ` (ref null exact $Foo) ` is expected.
304
- As a general rule, non-nullable exact references to abstract heap types are uninhabited.
304
+ are refined to produce references to the exact version of that heap type.
305
+
306
+ Since only defined types have exact versions,
307
+ instructions like ` ref.i31 ` or ` any.convert_extern ` that produce
308
+ references to abstract heap types do not produce references to exact types.
305
309
306
310
When allocating types with custom descriptors,
307
- ` struct.new ` and ` struct.new_default ` take exact references to the descriptors
311
+ ` struct.new ` and ` struct.new_default ` take references to the exact descriptors
308
312
as their last operands.
309
313
This makes the unsound program above invalid.
310
314
311
315
```
312
316
struct.new x
313
- C |- struct.new x : t* (ref null exact y) -> (ref exact x)
317
+
318
+ C |- struct.new x : t* (ref null (exact y)) -> (ref (exact x))
314
319
-- C.types[x] ~ descriptor y (struct (field t)*)
315
320
```
316
321
317
322
```
318
323
struct.new_default x
319
- C |- struct.new_default x : (ref null exact y) -> (ref exact x)
324
+
325
+ C |- struct.new_default x : (ref null (exact y)) -> (ref (exact x))
320
326
-- C.types[x] ~ descriptor y (struct (field t)*)
321
327
-- defaultable(t)*
322
328
```
@@ -335,16 +341,16 @@ a reference to the custom descriptor value can be retrieved with `ref.get_desc`.
335
341
```
336
342
ref.get_desc typeidx
337
343
338
- C |- ref.get_desc x : (ref exact_1 null x) -> (ref exact_1 y)
344
+ C |- ref.get_desc x : (ref null (exact_1 x)) -> (ref ( exact_1 y) )
339
345
-- C.types[x] ~ descriptor y ct
340
346
```
341
347
342
- If the provided reference is exact,
348
+ If the provided reference is to an exact heap type ,
343
349
then the type of the custom descriptor is known precisely,
344
350
so the result can be exact as well.
345
351
Otherwise, the subtyping rules described above ensure that there will be some custom descriptor value
346
352
and that it will be a subtype of the custom descriptor type for ` x ` ,
347
- so the result can be a non-null inexact reference .
353
+ so the result can be a non-null reference to the inexact descriptor type .
348
354
349
355
Being able to retrieve a custom descriptor means you can then compare it for equality
350
356
with an expected custom descriptor value.
@@ -362,31 +368,31 @@ then the type of the cast output can also be exact.
362
368
```
363
369
ref.cast_desc reftype
364
370
365
- C |- ref.cast_desc rt : (ref null ht) (ref null exact_1 y) -> rt
366
- -- rt = (ref null? exact_1 x)
371
+ C |- ref.cast_desc rt : (ref null ht) (ref null ( exact_1 y) ) -> rt
372
+ -- rt = (ref null? ( exact_1 x) )
367
373
-- C |- C.types[x] <: ht
368
374
-- C.types[x] ~ descriptor y ct
369
375
```
370
376
371
377
```
372
378
br_on_cast_desc labelidx reftype reftype
373
379
374
- C |- br_on_cast_desc l rt_1 rt_2 : t* rt_1 (ref null exact_1 y) -> t* (rt_1 \ rt_2)
380
+ C |- br_on_cast_desc l rt_1 rt_2 : t* rt_1 (ref null ( exact_1 y) ) -> t* (rt_1 \ rt_2)
375
381
-- C.labels[l] = t* rt
376
382
-- C |- rt_2 <: rt
377
383
-- C |- rt_2 <: rt_1
378
- -- rt_2 = (ref null? exact_1 x)
384
+ -- rt_2 = (ref null? ( exact_1 x) )
379
385
-- C.types[x] ~ descriptor y ct
380
386
```
381
387
382
388
```
383
389
br_on_cast_desc_fail labelidx reftype reftype
384
390
385
- C |- br_on_cast_desc_fail l rt_1 rt_2 : t* rt_1 (ref null exact_1 y) -> t* rt_2
391
+ C |- br_on_cast_desc_fail l rt_1 rt_2 : t* rt_1 (ref null ( exact_1 y) ) -> t* rt_2
386
392
-- C.labels[l] = t* rt
387
393
-- C |- rt_1 \ rt_2 <: rt
388
394
-- C |- rt_2 <: rt_1
389
- -- rt_2 = (ref null? exact_1 x)
395
+ -- rt_2 = (ref null? ( exact_1 x) )
390
396
-- C.types[x] ~ descriptor y ct
391
397
```
392
398
@@ -476,7 +482,7 @@ WebAssembly.
476
482
477
483
(elem declare func $counter.get $counter.inc)
478
484
479
- (global $counter.vtable (ref exact $counter.vtable)
485
+ (global $counter.vtable (ref ( exact $counter.vtable) )
480
486
(struct.new $counter.vtable
481
487
(global.get $counter.proto)
482
488
(ref.func $counter.get)
@@ -570,111 +576,33 @@ subtype ::=
570
576
| ct:sharecomptype => sub final eps ct
571
577
```
572
578
573
- ### Exact Reference Types
579
+ ### Exact Types
574
580
575
- Rather than use two new opcodes in the type opcode space
576
- to represent nullable and non-nullable exact reference types,
577
- we introduce just a single new prefix opcode that encode both:
581
+ Exact heap types are introduced with a prefix byte:
578
582
579
583
```
580
- reftype :: ...
581
- | 0x62 0x64 ht:heaptype => ref exact ht
582
- | 0x62 0x63 ht:heaptype => ref null exact ht
584
+ heaptype :: ... | 0x62 x:u32 => exact x
583
585
```
584
586
585
- To make the most of the existing shorthands for nullable abstract heap types,
586
- we also allow using the exact prefix with those shorthands:
587
-
588
- ```
589
- reftype :: ...
590
- | 0x62 ht:absheaptype => ref null exact ht
591
- ```
592
-
593
- Similarly, we allow combining ` exact ` with the established shorthands in the text format.
594
- For example ` (exact anyref) ` is a shorthand for ` (ref null exact any) ` .
587
+ Note that the type index being encoded as a ` u32 ` instead of a ` u33 `
588
+ intentionally makes it impossible to encode an exact abstract heap type.
595
589
596
590
### Instructions
597
591
598
- The existing ` ref.test ` , ` ref.cast ` , ` br_on_cast ` and ` br_on_cast_fail ` instructions
599
- need to be able to work with exact reference types.
600
- ` ref.test ` and ` ref.cast ` currently have two opcodes each:
601
- one for nullable target types and one for non-nullable target types.
602
- Rather than introducing two new opcodes for each of these instructions
603
- to allow for nullable exact and non-nullable exact target types,
604
- we introduce one new opcode for each that takes a full reference type
605
- rather than a heap type as its immediate.
592
+ All existing instructions that take heap type immediates work without
593
+ modification with the encoding of exact heap types.
606
594
607
- ```
608
- instr ::= ...
609
- | 0xFB 32:u32 rt:reftype => ref.test rt
610
- | 0xFB 33:u32 rt:reftype => ref.cast rt
611
- ```
612
-
613
- (` 0xFB 31 ` is already used by ` ref.i31_shared ` in the shared-everything-threads proposal.)
614
-
615
- Note that these new encodings can be used instead of the existing encodings
616
- to represent casts to inexact reference types.
617
-
618
- > Note: We could alternatively restrict the new encoding to be usable only with exact types,
619
- > but this artificial restriction does not seem useful.
620
-
621
- > Note: We could alternatively continue the existing encoding scheme,
622
- > at the cost of using 4 new opcodes instead of 2.
623
-
624
- ` br_on_cast ` and ` br_on_cast_fail ` already encode the nullability of their
625
- input and output types in a "castflags" u8 immediate. Castflags is extended to encode
626
- exactness as well:
627
-
628
- ```
629
- castflags ::= ...
630
- | 4:u8 => (exact, eps)
631
- | 5:u8 => (null exact, eps)
632
- | 6:u8 => (exact, null)
633
- | 7:u8 => (null exact, null)
634
- | 8:u8 => (eps, exact)
635
- | 9:u8 => (null, exact)
636
- | 10:u8 => (eps, null exact)
637
- | 11:u8 => (null, null exact)
638
- | 12:u8 => (exact, exact)
639
- | 13:u8 => (null exact, exact)
640
- | 14:u8 => (exact, null exact)
641
- | 15:u8 => (null exact, null exact)
642
- ```
643
-
644
- Note that the bits now have the following meanings:
645
-
646
- ```
647
- bit 0: source nullability
648
- bit 1: target nullability
649
- bit 2: source exactness
650
- bit 3: target exactness
651
- ```
652
-
653
- The other new instructions are encoded as follows:
595
+ The new instructions are encoded as follows:
654
596
655
597
```
656
598
instr ::= ...
657
599
| 0xFB 34:u32 x:typeidx => ref.get_desc x
658
- | 0xFB 35:u32 rt:reftype => ref.cast_desc reftype
659
- | 0xFB 36:u32 (null_1? exact_1?, null_2? exact_2?):castflags
600
+ | 0xFB 35:u32 ht:heaptype => ref.cast_desc (ref ht)
601
+ | 0xFB 36:u32 ht:heaptype => ref.cast_desc (ref null ht)
602
+ | 0xFB 37:u32 (null_1?, null_2?):castflags
660
603
l:labelidx ht_1:heaptype ht_2:heaptype =>
661
- br_on_cast_desc l (ref null_1? exact_1? ht_1) (ref null_2? exact_2 ? ht_2)
662
- | 0xFB 37 :u32 (null_1? exact_1? , null_2? exact_2 ?):castflags
604
+ br_on_cast_desc l (ref null_1? ht_1) (ref null_2? ht_2)
605
+ | 0xFB 38 :u32 (null_1?, null_2?):castflags
663
606
l:labelidx ht_1:heaptype ht_2:heaptype =>
664
- br_on_cast_desc_fail l (ref null_1? exact_1? ht_1) (ref null_2? exact_2 ? ht_2)
607
+ br_on_cast_desc_fail l (ref null_1? ht_1) (ref null_2? ht_2)
665
608
```
666
-
667
- ## Minimal Initial Prototyping for JS Interop
668
-
669
- A truly minimal prototype for experimenting with JS interop can skip
670
- implementing most of the new features in this proposal:
671
-
672
- - Arbitrary fields on custom descriptors.
673
- A minimal prototype can allow ` descriptor ` clauses only on empty structs.
674
- - Exact reference types.
675
- A minimal prototype can instead bake an RTT exactness check into the
676
- semantics of ` struct.new ` and ` struct.new_default ` to ensure soundness.
677
- - New instructions.
678
- A minimal prototype only needs to update ` struct.new ` and ` struct.new_default `
679
- to take references to custom descriptors as necessary.
680
- It does not need to implement ` ref.get_desc ` or any of the new casts.
0 commit comments