plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
0e39618
— Peter John
2026-08-10T12:51:35+05:30
feat(plum-wasm-codegen): migrate structs, enums, strings, and closures to wasm-gc
- Cargo.lock +350 -400
- docs/superpowers/plans/2026-07-25-wasm-gc-migration.md +38 -38
- plum-wasm-codegen/Cargo.toml +1 -1
- plum-wasm-codegen/src/lib.rs +650 -649
- plum-wasm-codegen/tests/codegen_tests.rs +23 -13
Cargo.lock
CHANGED
|
@@ -4,20 +4,20 @@ version = 4
|
|
|
4
4
|
|
|
5
5
|
[[package]]
|
|
6
6
|
name = "addr2line"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.25.1"
|
|
8
8
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
-
checksum = "
|
|
9
|
+
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
|
|
10
10
|
dependencies = [
|
|
11
|
-
"gimli 0.
|
|
11
|
+
"gimli 0.32.3",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
[[package]]
|
|
15
15
|
name = "addr2line"
|
|
16
|
-
version = "0.
|
|
16
|
+
version = "0.26.1"
|
|
17
17
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
18
|
-
checksum = "
|
|
18
|
+
checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a"
|
|
19
19
|
dependencies = [
|
|
20
|
-
"gimli 0.
|
|
20
|
+
"gimli 0.33.0",
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
[[package]]
|
|
@@ -89,7 +89,7 @@ version = "1.1.5"
|
|
|
89
89
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
90
90
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
|
91
91
|
dependencies = [
|
|
92
|
-
"windows-sys
|
|
92
|
+
"windows-sys",
|
|
93
93
|
]
|
|
94
94
|
|
|
95
95
|
[[package]]
|
|
@@ -100,7 +100,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|
|
100
100
|
dependencies = [
|
|
101
101
|
"anstyle",
|
|
102
102
|
"once_cell_polyfill",
|
|
103
|
-
"windows-sys
|
|
103
|
+
"windows-sys",
|
|
104
104
|
]
|
|
105
105
|
|
|
106
106
|
[[package]]
|
|
@@ -109,15 +109,6 @@ version = "1.0.104"
|
|
|
109
109
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
110
110
|
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
|
111
111
|
|
|
112
|
-
[[package]]
|
|
113
|
-
name = "ar_archive_writer"
|
|
114
|
-
version = "0.5.2"
|
|
115
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
116
|
-
checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348"
|
|
117
|
-
dependencies = [
|
|
118
|
-
"object 0.37.3",
|
|
119
|
-
]
|
|
120
|
-
|
|
121
112
|
[[package]]
|
|
122
113
|
name = "arbitrary"
|
|
123
114
|
version = "1.4.2"
|
|
@@ -161,9 +152,9 @@ dependencies = [
|
|
|
161
152
|
|
|
162
153
|
[[package]]
|
|
163
154
|
name = "base64"
|
|
164
|
-
version = "0.
|
|
155
|
+
version = "0.22.1"
|
|
165
156
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
-
checksum = "
|
|
157
|
+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|
167
158
|
|
|
168
159
|
[[package]]
|
|
169
160
|
name = "bitflags"
|
|
@@ -189,12 +180,6 @@ dependencies = [
|
|
|
189
180
|
"allocator-api2",
|
|
190
181
|
]
|
|
191
182
|
|
|
192
|
-
[[package]]
|
|
193
|
-
name = "byteorder"
|
|
194
|
-
version = "1.5.0"
|
|
195
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
196
|
-
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
197
|
-
|
|
198
183
|
[[package]]
|
|
199
184
|
name = "cc"
|
|
200
185
|
version = "1.3.0"
|
|
@@ -270,9 +255,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
|
|
270
255
|
|
|
271
256
|
[[package]]
|
|
272
257
|
name = "cpp_demangle"
|
|
273
|
-
version = "0.
|
|
258
|
+
version = "0.5.1"
|
|
274
259
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
275
|
-
checksum = "
|
|
260
|
+
checksum = "0667304c32ea56cb4cd6d2d7c0cfe9a2f8041229db8c033af7f8d69492429def"
|
|
276
261
|
dependencies = [
|
|
277
262
|
"cfg-if",
|
|
278
263
|
]
|
|
@@ -286,32 +271,53 @@ dependencies = [
|
|
|
286
271
|
"libc",
|
|
287
272
|
]
|
|
288
273
|
|
|
274
|
+
[[package]]
|
|
275
|
+
name = "cranelift-assembler-x64"
|
|
276
|
+
version = "0.134.3"
|
|
277
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
278
|
+
checksum = "d552bd33b7a56dc70aeb1e1c960e51a218fa0db50f23873b500a310379450b2d"
|
|
279
|
+
dependencies = [
|
|
280
|
+
"cranelift-assembler-x64-meta",
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
[[package]]
|
|
284
|
+
name = "cranelift-assembler-x64-meta"
|
|
285
|
+
version = "0.134.3"
|
|
286
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
287
|
+
checksum = "078e80e4c222279e3330f6aa1a256ca77ddf156d4453166a9f09defedc4594dd"
|
|
288
|
+
dependencies = [
|
|
289
|
+
"cranelift-srcgen",
|
|
290
|
+
]
|
|
291
|
+
|
|
289
292
|
[[package]]
|
|
290
293
|
name = "cranelift-bforest"
|
|
291
|
-
version = "0.
|
|
294
|
+
version = "0.134.3"
|
|
292
295
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
293
|
-
checksum = "
|
|
296
|
+
checksum = "820ce15d4ad3562d613c31a67b6d0434d403e7091a68d1349903842f7d31737e"
|
|
294
297
|
dependencies = [
|
|
295
298
|
"cranelift-entity",
|
|
299
|
+
"wasmtime-internal-core",
|
|
296
300
|
]
|
|
297
301
|
|
|
298
302
|
[[package]]
|
|
299
303
|
name = "cranelift-bitset"
|
|
300
|
-
version = "0.
|
|
304
|
+
version = "0.134.3"
|
|
301
305
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
302
|
-
checksum = "
|
|
306
|
+
checksum = "61bca563d4b86d285928d9e27f97f27039bb33a0fc524fa130d7d0c106bf8ab3"
|
|
303
307
|
dependencies = [
|
|
304
308
|
"serde",
|
|
305
309
|
"serde_derive",
|
|
310
|
+
"wasmtime-internal-core",
|
|
306
311
|
]
|
|
307
312
|
|
|
308
313
|
[[package]]
|
|
309
314
|
name = "cranelift-codegen"
|
|
310
|
-
version = "0.
|
|
315
|
+
version = "0.134.3"
|
|
311
316
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
312
|
-
checksum = "
|
|
317
|
+
checksum = "709f4b7c0fb57d952658d5b5c07fbdc4149acd7b7f0de9678ae754b9c981949a"
|
|
313
318
|
dependencies = [
|
|
314
319
|
"bumpalo",
|
|
320
|
+
"cranelift-assembler-x64",
|
|
315
321
|
"cranelift-bforest",
|
|
316
322
|
"cranelift-bitset",
|
|
317
323
|
"cranelift-codegen-meta",
|
|
@@ -319,58 +325,70 @@ dependencies = [
|
|
|
319
325
|
"cranelift-control",
|
|
320
326
|
"cranelift-entity",
|
|
321
327
|
"cranelift-isle",
|
|
322
|
-
"gimli 0.
|
|
328
|
+
"gimli 0.33.0",
|
|
323
|
-
"hashbrown 0.
|
|
329
|
+
"hashbrown 0.17.1",
|
|
330
|
+
"libm",
|
|
324
331
|
"log",
|
|
332
|
+
"postcard",
|
|
333
|
+
"pulley-interpreter",
|
|
325
334
|
"regalloc2",
|
|
326
335
|
"rustc-hash",
|
|
327
336
|
"serde",
|
|
337
|
+
"serde_derive",
|
|
338
|
+
"sha2",
|
|
328
339
|
"smallvec",
|
|
329
340
|
"target-lexicon",
|
|
341
|
+
"wasmtime-internal-core",
|
|
330
342
|
]
|
|
331
343
|
|
|
332
344
|
[[package]]
|
|
333
345
|
name = "cranelift-codegen-meta"
|
|
334
|
-
version = "0.
|
|
346
|
+
version = "0.134.3"
|
|
335
347
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
336
|
-
checksum = "
|
|
348
|
+
checksum = "903dc8915af62aad1d9d0f39a5968d33fa80b9aa899ed6f56e47f40ca4512e1e"
|
|
337
349
|
dependencies = [
|
|
350
|
+
"cranelift-assembler-x64-meta",
|
|
338
351
|
"cranelift-codegen-shared",
|
|
352
|
+
"cranelift-srcgen",
|
|
353
|
+
"heck",
|
|
354
|
+
"pulley-interpreter",
|
|
339
355
|
]
|
|
340
356
|
|
|
341
357
|
[[package]]
|
|
342
358
|
name = "cranelift-codegen-shared"
|
|
343
|
-
version = "0.
|
|
359
|
+
version = "0.134.3"
|
|
344
360
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
345
|
-
checksum = "
|
|
361
|
+
checksum = "0522d74c227e49f3fd49ab055311486ae4f09083262b66705bed676952491469"
|
|
346
362
|
|
|
347
363
|
[[package]]
|
|
348
364
|
name = "cranelift-control"
|
|
349
|
-
version = "0.
|
|
365
|
+
version = "0.134.3"
|
|
350
366
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
351
|
-
checksum = "
|
|
367
|
+
checksum = "66560ea1c5cef72e170b18e46d263dba2d3169c9d39e8cafdab2173c6362cc1a"
|
|
352
368
|
dependencies = [
|
|
353
369
|
"arbitrary",
|
|
354
370
|
]
|
|
355
371
|
|
|
356
372
|
[[package]]
|
|
357
373
|
name = "cranelift-entity"
|
|
358
|
-
version = "0.
|
|
374
|
+
version = "0.134.3"
|
|
359
375
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
360
|
-
checksum = "
|
|
376
|
+
checksum = "b62ef5b17cc814d27e96b66a5b46da0e4ce2b8ac55a6d478048bb99d04b05526"
|
|
361
377
|
dependencies = [
|
|
362
378
|
"cranelift-bitset",
|
|
363
379
|
"serde",
|
|
364
380
|
"serde_derive",
|
|
381
|
+
"wasmtime-internal-core",
|
|
365
382
|
]
|
|
366
383
|
|
|
367
384
|
[[package]]
|
|
368
385
|
name = "cranelift-frontend"
|
|
369
|
-
version = "0.
|
|
386
|
+
version = "0.134.3"
|
|
370
387
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
371
|
-
checksum = "
|
|
388
|
+
checksum = "a87e0aaa39dbf70693b348a221e45904111704ee8f9fef140498471005f9842d"
|
|
372
389
|
dependencies = [
|
|
373
390
|
"cranelift-codegen",
|
|
391
|
+
"hashbrown 0.17.1",
|
|
374
392
|
"log",
|
|
375
393
|
"smallvec",
|
|
376
394
|
"target-lexicon",
|
|
@@ -378,21 +396,27 @@ dependencies = [
|
|
|
378
396
|
|
|
379
397
|
[[package]]
|
|
380
398
|
name = "cranelift-isle"
|
|
381
|
-
version = "0.
|
|
399
|
+
version = "0.134.3"
|
|
382
400
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
383
|
-
checksum = "
|
|
401
|
+
checksum = "cf79003ebfa1eed5e87f3b84446ad5236f540268289960e14f387dc9b28e40b7"
|
|
384
402
|
|
|
385
403
|
[[package]]
|
|
386
404
|
name = "cranelift-native"
|
|
387
|
-
version = "0.
|
|
405
|
+
version = "0.134.3"
|
|
388
406
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
389
|
-
checksum = "
|
|
407
|
+
checksum = "05bf4f235743c81e67ee4db617c5a4a0b65d58d3f0cfc575ee0f1a4e0cd58273"
|
|
390
408
|
dependencies = [
|
|
391
409
|
"cranelift-codegen",
|
|
392
410
|
"libc",
|
|
393
411
|
"target-lexicon",
|
|
394
412
|
]
|
|
395
413
|
|
|
414
|
+
[[package]]
|
|
415
|
+
name = "cranelift-srcgen"
|
|
416
|
+
version = "0.134.3"
|
|
417
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
418
|
+
checksum = "f6977c2a71ab1e0d1e62f966b411a498aa04c4dce47d93d52f8a360a06058922"
|
|
419
|
+
|
|
396
420
|
[[package]]
|
|
397
421
|
name = "crc32fast"
|
|
398
422
|
version = "1.5.0"
|
|
@@ -523,15 +547,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
523
547
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
|
524
548
|
dependencies = [
|
|
525
549
|
"libc",
|
|
526
|
-
"windows-sys
|
|
550
|
+
"windows-sys",
|
|
527
551
|
]
|
|
528
552
|
|
|
529
|
-
[[package]]
|
|
530
|
-
name = "fallible-iterator"
|
|
531
|
-
version = "0.3.0"
|
|
532
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
533
|
-
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
|
534
|
-
|
|
535
553
|
[[package]]
|
|
536
554
|
name = "fastrand"
|
|
537
555
|
version = "2.5.0"
|
|
@@ -544,11 +562,23 @@ version = "0.1.9"
|
|
|
544
562
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
545
563
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
|
546
564
|
|
|
565
|
+
[[package]]
|
|
566
|
+
name = "fixedbitset"
|
|
567
|
+
version = "0.4.2"
|
|
568
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
569
|
+
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
|
570
|
+
|
|
571
|
+
[[package]]
|
|
572
|
+
name = "fnv"
|
|
573
|
+
version = "1.0.7"
|
|
574
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
575
|
+
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
576
|
+
|
|
547
577
|
[[package]]
|
|
548
578
|
name = "foldhash"
|
|
549
|
-
version = "0.
|
|
579
|
+
version = "0.2.0"
|
|
550
580
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
551
|
-
checksum = "
|
|
581
|
+
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
|
552
582
|
|
|
553
583
|
[[package]]
|
|
554
584
|
name = "futures"
|
|
@@ -638,25 +668,17 @@ dependencies = [
|
|
|
638
668
|
"slab",
|
|
639
669
|
]
|
|
640
670
|
|
|
641
|
-
[[package]]
|
|
642
|
-
name = "fxhash"
|
|
643
|
-
version = "0.2.1"
|
|
644
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
645
|
-
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
|
646
|
-
dependencies = [
|
|
647
|
-
"byteorder",
|
|
648
|
-
]
|
|
649
|
-
|
|
650
671
|
[[package]]
|
|
651
672
|
name = "fxprof-processed-profile"
|
|
652
|
-
version = "0.
|
|
673
|
+
version = "0.8.1"
|
|
653
674
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
654
|
-
checksum = "
|
|
675
|
+
checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557"
|
|
655
676
|
dependencies = [
|
|
656
677
|
"bitflags",
|
|
657
678
|
"debugid",
|
|
658
|
-
"
|
|
679
|
+
"rustc-hash",
|
|
659
680
|
"serde",
|
|
681
|
+
"serde_derive",
|
|
660
682
|
"serde_json",
|
|
661
683
|
]
|
|
662
684
|
|
|
@@ -694,20 +716,21 @@ dependencies = [
|
|
|
694
716
|
|
|
695
717
|
[[package]]
|
|
696
718
|
name = "gimli"
|
|
697
|
-
version = "0.
|
|
719
|
+
version = "0.32.3"
|
|
698
720
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
699
|
-
checksum = "
|
|
721
|
+
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
|
|
700
|
-
dependencies = [
|
|
701
|
-
"fallible-iterator",
|
|
702
|
-
"indexmap",
|
|
703
|
-
"stable_deref_trait",
|
|
704
|
-
]
|
|
705
722
|
|
|
706
723
|
[[package]]
|
|
707
724
|
name = "gimli"
|
|
708
|
-
version = "0.
|
|
725
|
+
version = "0.33.0"
|
|
709
726
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
710
|
-
checksum = "
|
|
727
|
+
checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c"
|
|
728
|
+
dependencies = [
|
|
729
|
+
"fnv",
|
|
730
|
+
"hashbrown 0.16.1",
|
|
731
|
+
"indexmap",
|
|
732
|
+
"stable_deref_trait",
|
|
733
|
+
]
|
|
711
734
|
|
|
712
735
|
[[package]]
|
|
713
736
|
name = "hashbrown"
|
|
@@ -721,19 +744,20 @@ dependencies = [
|
|
|
721
744
|
|
|
722
745
|
[[package]]
|
|
723
746
|
name = "hashbrown"
|
|
724
|
-
version = "0.
|
|
747
|
+
version = "0.16.1"
|
|
725
748
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
726
|
-
checksum = "
|
|
749
|
+
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
|
727
|
-
dependencies = [
|
|
728
|
-
"foldhash",
|
|
729
|
-
"serde",
|
|
730
|
-
]
|
|
731
750
|
|
|
732
751
|
[[package]]
|
|
733
752
|
name = "hashbrown"
|
|
734
753
|
version = "0.17.1"
|
|
735
754
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
736
755
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
|
756
|
+
dependencies = [
|
|
757
|
+
"foldhash",
|
|
758
|
+
"serde",
|
|
759
|
+
"serde_core",
|
|
760
|
+
]
|
|
737
761
|
|
|
738
762
|
[[package]]
|
|
739
763
|
name = "heck"
|
|
@@ -782,9 +806,9 @@ dependencies = [
|
|
|
782
806
|
|
|
783
807
|
[[package]]
|
|
784
808
|
name = "itertools"
|
|
785
|
-
version = "0.
|
|
809
|
+
version = "0.14.0"
|
|
786
810
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
787
|
-
checksum = "
|
|
811
|
+
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
|
788
812
|
dependencies = [
|
|
789
813
|
"either",
|
|
790
814
|
]
|
|
@@ -868,12 +892,6 @@ dependencies = [
|
|
|
868
892
|
"libc",
|
|
869
893
|
]
|
|
870
894
|
|
|
871
|
-
[[package]]
|
|
872
|
-
name = "linux-raw-sys"
|
|
873
|
-
version = "0.4.15"
|
|
874
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
875
|
-
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
|
876
|
-
|
|
877
895
|
[[package]]
|
|
878
896
|
name = "linux-raw-sys"
|
|
879
897
|
version = "0.12.1"
|
|
@@ -888,12 +906,9 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
|
|
888
906
|
|
|
889
907
|
[[package]]
|
|
890
908
|
name = "mach2"
|
|
891
|
-
version = "0.
|
|
909
|
+
version = "0.6.0"
|
|
892
910
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
893
|
-
checksum = "
|
|
911
|
+
checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b"
|
|
894
|
-
dependencies = [
|
|
895
|
-
"libc",
|
|
896
|
-
]
|
|
897
912
|
|
|
898
913
|
[[package]]
|
|
899
914
|
name = "memchr"
|
|
@@ -907,7 +922,7 @@ version = "0.6.5"
|
|
|
907
922
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
908
923
|
checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227"
|
|
909
924
|
dependencies = [
|
|
910
|
-
"rustix
|
|
925
|
+
"rustix",
|
|
911
926
|
]
|
|
912
927
|
|
|
913
928
|
[[package]]
|
|
@@ -951,22 +966,22 @@ dependencies = [
|
|
|
951
966
|
|
|
952
967
|
[[package]]
|
|
953
968
|
name = "object"
|
|
954
|
-
version = "0.
|
|
969
|
+
version = "0.37.3"
|
|
955
970
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
956
|
-
checksum = "
|
|
971
|
+
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
|
957
972
|
dependencies = [
|
|
958
|
-
"crc32fast",
|
|
959
|
-
"hashbrown 0.15.5",
|
|
960
|
-
"indexmap",
|
|
961
973
|
"memchr",
|
|
962
974
|
]
|
|
963
975
|
|
|
964
976
|
[[package]]
|
|
965
977
|
name = "object"
|
|
966
|
-
version = "0.
|
|
978
|
+
version = "0.39.1"
|
|
967
979
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
968
|
-
checksum = "
|
|
980
|
+
checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b"
|
|
969
981
|
dependencies = [
|
|
982
|
+
"crc32fast",
|
|
983
|
+
"hashbrown 0.17.1",
|
|
984
|
+
"indexmap",
|
|
970
985
|
"memchr",
|
|
971
986
|
]
|
|
972
987
|
|
|
@@ -989,10 +1004,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
989
1004
|
checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"
|
|
990
1005
|
|
|
991
1006
|
[[package]]
|
|
992
|
-
name = "
|
|
1007
|
+
name = "petgraph"
|
|
993
|
-
version = "
|
|
1008
|
+
version = "0.6.5"
|
|
994
1009
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
995
|
-
checksum = "
|
|
1010
|
+
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
|
1011
|
+
dependencies = [
|
|
1012
|
+
"fixedbitset",
|
|
1013
|
+
"indexmap",
|
|
1014
|
+
]
|
|
996
1015
|
|
|
997
1016
|
[[package]]
|
|
998
1017
|
name = "pin-project-lite"
|
|
@@ -1094,24 +1113,26 @@ dependencies = [
|
|
|
1094
1113
|
]
|
|
1095
1114
|
|
|
1096
1115
|
[[package]]
|
|
1097
|
-
name = "
|
|
1116
|
+
name = "pulley-interpreter"
|
|
1098
|
-
version = "0.
|
|
1117
|
+
version = "47.0.3"
|
|
1099
1118
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1100
|
-
checksum = "
|
|
1119
|
+
checksum = "bc5c8c21ea032e4efbdf2d067dc45171779dbe0c8ecf20ef4a57efa7474f2b0a"
|
|
1101
1120
|
dependencies = [
|
|
1102
|
-
"
|
|
1121
|
+
"cranelift-bitset",
|
|
1103
|
-
"
|
|
1122
|
+
"log",
|
|
1123
|
+
"pulley-macros",
|
|
1124
|
+
"wasmtime-internal-core",
|
|
1104
1125
|
]
|
|
1105
1126
|
|
|
1106
1127
|
[[package]]
|
|
1107
|
-
name = "pulley-
|
|
1128
|
+
name = "pulley-macros"
|
|
1108
|
-
version = "
|
|
1129
|
+
version = "47.0.3"
|
|
1109
1130
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1110
|
-
checksum = "
|
|
1131
|
+
checksum = "9f10925455d5dde962e3604eade797ba5488644c8ee14a44191e2f2b58980574"
|
|
1111
1132
|
dependencies = [
|
|
1112
|
-
"cranelift-bitset",
|
|
1113
|
-
"
|
|
1133
|
+
"proc-macro2",
|
|
1114
|
-
"
|
|
1134
|
+
"quote",
|
|
1135
|
+
"syn 2.0.119",
|
|
1115
1136
|
]
|
|
1116
1137
|
|
|
1117
1138
|
[[package]]
|
|
@@ -1162,15 +1183,16 @@ dependencies = [
|
|
|
1162
1183
|
|
|
1163
1184
|
[[package]]
|
|
1164
1185
|
name = "regalloc2"
|
|
1165
|
-
version = "0.
|
|
1186
|
+
version = "0.15.2"
|
|
1166
1187
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1167
|
-
checksum = "
|
|
1188
|
+
checksum = "757712e8e61590d6d4f5d563483755538b5aa13467837a3b41cd9832509a7f85"
|
|
1168
1189
|
dependencies = [
|
|
1169
1190
|
"allocator-api2",
|
|
1170
1191
|
"bumpalo",
|
|
1171
|
-
"hashbrown 0.
|
|
1192
|
+
"hashbrown 0.17.1",
|
|
1172
1193
|
"log",
|
|
1173
1194
|
"rustc-hash",
|
|
1195
|
+
"serde",
|
|
1174
1196
|
"smallvec",
|
|
1175
1197
|
]
|
|
1176
1198
|
|
|
@@ -1215,19 +1237,6 @@ version = "2.1.3"
|
|
|
1215
1237
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1216
1238
|
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
|
1217
1239
|
|
|
1218
|
-
[[package]]
|
|
1219
|
-
name = "rustix"
|
|
1220
|
-
version = "0.38.44"
|
|
1221
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1222
|
-
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
|
1223
|
-
dependencies = [
|
|
1224
|
-
"bitflags",
|
|
1225
|
-
"errno",
|
|
1226
|
-
"libc",
|
|
1227
|
-
"linux-raw-sys 0.4.15",
|
|
1228
|
-
"windows-sys 0.59.0",
|
|
1229
|
-
]
|
|
1230
|
-
|
|
1231
1240
|
[[package]]
|
|
1232
1241
|
name = "rustix"
|
|
1233
1242
|
version = "1.1.4"
|
|
@@ -1237,8 +1246,8 @@ dependencies = [
|
|
|
1237
1246
|
"bitflags",
|
|
1238
1247
|
"errno",
|
|
1239
1248
|
"libc",
|
|
1240
|
-
"linux-raw-sys
|
|
1249
|
+
"linux-raw-sys",
|
|
1241
|
-
"windows-sys
|
|
1250
|
+
"windows-sys",
|
|
1242
1251
|
]
|
|
1243
1252
|
|
|
1244
1253
|
[[package]]
|
|
@@ -1303,11 +1312,11 @@ dependencies = [
|
|
|
1303
1312
|
|
|
1304
1313
|
[[package]]
|
|
1305
1314
|
name = "serde_spanned"
|
|
1306
|
-
version = "
|
|
1315
|
+
version = "1.1.1"
|
|
1307
1316
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1308
|
-
checksum = "
|
|
1317
|
+
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
|
1309
1318
|
dependencies = [
|
|
1310
|
-
"
|
|
1319
|
+
"serde_core",
|
|
1311
1320
|
]
|
|
1312
1321
|
|
|
1313
1322
|
[[package]]
|
|
@@ -1342,12 +1351,6 @@ dependencies = [
|
|
|
1342
1351
|
"serde",
|
|
1343
1352
|
]
|
|
1344
1353
|
|
|
1345
|
-
[[package]]
|
|
1346
|
-
name = "sptr"
|
|
1347
|
-
version = "0.3.2"
|
|
1348
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1349
|
-
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
|
|
1350
|
-
|
|
1351
1354
|
[[package]]
|
|
1352
1355
|
name = "stable_deref_trait"
|
|
1353
1356
|
version = "1.2.1"
|
|
@@ -1411,9 +1414,9 @@ dependencies = [
|
|
|
1411
1414
|
|
|
1412
1415
|
[[package]]
|
|
1413
1416
|
name = "target-lexicon"
|
|
1414
|
-
version = "0.
|
|
1417
|
+
version = "0.13.5"
|
|
1415
1418
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1416
|
-
checksum = "
|
|
1419
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
1417
1420
|
|
|
1418
1421
|
[[package]]
|
|
1419
1422
|
name = "tempfile"
|
|
@@ -1424,8 +1427,8 @@ dependencies = [
|
|
|
1424
1427
|
"fastrand",
|
|
1425
1428
|
"getrandom 0.4.3",
|
|
1426
1429
|
"once_cell",
|
|
1427
|
-
"rustix
|
|
1430
|
+
"rustix",
|
|
1428
|
-
"windows-sys
|
|
1431
|
+
"windows-sys",
|
|
1429
1432
|
]
|
|
1430
1433
|
|
|
1431
1434
|
[[package]]
|
|
@@ -1443,8 +1446,8 @@ version = "0.4.4"
|
|
|
1443
1446
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1444
1447
|
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
|
1445
1448
|
dependencies = [
|
|
1446
|
-
"rustix
|
|
1449
|
+
"rustix",
|
|
1447
|
-
"windows-sys
|
|
1450
|
+
"windows-sys",
|
|
1448
1451
|
]
|
|
1449
1452
|
|
|
1450
1453
|
[[package]]
|
|
@@ -1520,14 +1523,17 @@ dependencies = [
|
|
|
1520
1523
|
|
|
1521
1524
|
[[package]]
|
|
1522
1525
|
name = "toml"
|
|
1523
|
-
version = "0.
|
|
1526
|
+
version = "0.9.12+spec-1.1.0"
|
|
1524
1527
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1525
|
-
checksum = "
|
|
1528
|
+
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
|
1526
1529
|
dependencies = [
|
|
1530
|
+
"indexmap",
|
|
1527
|
-
"
|
|
1531
|
+
"serde_core",
|
|
1528
1532
|
"serde_spanned",
|
|
1533
|
+
"toml_datetime 0.7.5+spec-1.1.0",
|
|
1529
|
-
"
|
|
1534
|
+
"toml_parser",
|
|
1530
|
-
"
|
|
1535
|
+
"toml_writer",
|
|
1536
|
+
"winnow 0.7.15",
|
|
1531
1537
|
]
|
|
1532
1538
|
|
|
1533
1539
|
[[package]]
|
|
@@ -1535,8 +1541,14 @@ name = "toml_datetime"
|
|
|
1535
1541
|
version = "0.6.11"
|
|
1536
1542
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1537
1543
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
|
1544
|
+
|
|
1545
|
+
[[package]]
|
|
1546
|
+
name = "toml_datetime"
|
|
1547
|
+
version = "0.7.5+spec-1.1.0"
|
|
1548
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1549
|
+
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
|
1538
1550
|
dependencies = [
|
|
1539
|
-
"
|
|
1551
|
+
"serde_core",
|
|
1540
1552
|
]
|
|
1541
1553
|
|
|
1542
1554
|
[[package]]
|
|
@@ -1546,11 +1558,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
1546
1558
|
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
|
1547
1559
|
dependencies = [
|
|
1548
1560
|
"indexmap",
|
|
1549
|
-
"serde",
|
|
1550
|
-
"serde_spanned",
|
|
1551
|
-
"toml_datetime",
|
|
1561
|
+
"toml_datetime 0.6.11",
|
|
1552
1562
|
"toml_write",
|
|
1563
|
+
"winnow 0.7.15",
|
|
1564
|
+
]
|
|
1565
|
+
|
|
1566
|
+
[[package]]
|
|
1567
|
+
name = "toml_parser"
|
|
1568
|
+
version = "1.1.3+spec-1.1.0"
|
|
1569
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1570
|
+
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
|
1571
|
+
dependencies = [
|
|
1553
|
-
"winnow",
|
|
1572
|
+
"winnow 1.0.4",
|
|
1554
1573
|
]
|
|
1555
1574
|
|
|
1556
1575
|
[[package]]
|
|
@@ -1559,6 +1578,12 @@ version = "0.1.2"
|
|
|
1559
1578
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1560
1579
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
|
1561
1580
|
|
|
1581
|
+
[[package]]
|
|
1582
|
+
name = "toml_writer"
|
|
1583
|
+
version = "1.1.2+spec-1.1.0"
|
|
1584
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1585
|
+
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
|
1586
|
+
|
|
1562
1587
|
[[package]]
|
|
1563
1588
|
name = "topiary-core"
|
|
1564
1589
|
version = "0.7.3"
|
|
@@ -1668,12 +1693,6 @@ version = "0.2.2"
|
|
|
1668
1693
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1669
1694
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
|
1670
1695
|
|
|
1671
|
-
[[package]]
|
|
1672
|
-
name = "unicode-xid"
|
|
1673
|
-
version = "0.2.6"
|
|
1674
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1675
|
-
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
|
1676
|
-
|
|
1677
1696
|
[[package]]
|
|
1678
1697
|
name = "utf8parse"
|
|
1679
1698
|
version = "0.2.2"
|
|
@@ -1773,6 +1792,23 @@ dependencies = [
|
|
|
1773
1792
|
"unicode-ident",
|
|
1774
1793
|
]
|
|
1775
1794
|
|
|
1795
|
+
[[package]]
|
|
1796
|
+
name = "wasm-compose"
|
|
1797
|
+
version = "0.252.0"
|
|
1798
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1799
|
+
checksum = "d59b710751a35d54732a63851cdfacbfb2266b7160ce174faf269d3f4e84e31b"
|
|
1800
|
+
dependencies = [
|
|
1801
|
+
"anyhow",
|
|
1802
|
+
"heck",
|
|
1803
|
+
"indexmap",
|
|
1804
|
+
"log",
|
|
1805
|
+
"petgraph",
|
|
1806
|
+
"smallvec",
|
|
1807
|
+
"wasm-encoder 0.252.0",
|
|
1808
|
+
"wasmparser 0.252.0",
|
|
1809
|
+
"wat",
|
|
1810
|
+
]
|
|
1811
|
+
|
|
1776
1812
|
[[package]]
|
|
1777
1813
|
name = "wasm-encoder"
|
|
1778
1814
|
version = "0.220.1"
|
|
@@ -1785,12 +1821,12 @@ dependencies = [
|
|
|
1785
1821
|
|
|
1786
1822
|
[[package]]
|
|
1787
1823
|
name = "wasm-encoder"
|
|
1788
|
-
version = "0.
|
|
1824
|
+
version = "0.252.0"
|
|
1789
1825
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1790
|
-
checksum = "
|
|
1826
|
+
checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f"
|
|
1791
1827
|
dependencies = [
|
|
1792
|
-
"
|
|
1828
|
+
"leb128fmt",
|
|
1793
|
-
"wasmparser 0.
|
|
1829
|
+
"wasmparser 0.252.0",
|
|
1794
1830
|
]
|
|
1795
1831
|
|
|
1796
1832
|
[[package]]
|
|
@@ -1819,12 +1855,12 @@ dependencies = [
|
|
|
1819
1855
|
|
|
1820
1856
|
[[package]]
|
|
1821
1857
|
name = "wasmparser"
|
|
1822
|
-
version = "0.
|
|
1858
|
+
version = "0.252.0"
|
|
1823
1859
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1824
|
-
checksum = "
|
|
1860
|
+
checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c"
|
|
1825
1861
|
dependencies = [
|
|
1826
1862
|
"bitflags",
|
|
1827
|
-
"hashbrown 0.
|
|
1863
|
+
"hashbrown 0.17.1",
|
|
1828
1864
|
"indexmap",
|
|
1829
1865
|
"semver",
|
|
1830
1866
|
"serde",
|
|
@@ -1843,223 +1879,236 @@ dependencies = [
|
|
|
1843
1879
|
|
|
1844
1880
|
[[package]]
|
|
1845
1881
|
name = "wasmprinter"
|
|
1846
|
-
version = "0.
|
|
1882
|
+
version = "0.252.0"
|
|
1847
1883
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1848
|
-
checksum = "
|
|
1884
|
+
checksum = "7142797de29b35ab8dbf15c00f55fda75d409da4c423a8ab8bd6b667a785824b"
|
|
1849
1885
|
dependencies = [
|
|
1850
1886
|
"anyhow",
|
|
1851
1887
|
"termcolor",
|
|
1852
|
-
"wasmparser 0.
|
|
1888
|
+
"wasmparser 0.252.0",
|
|
1853
1889
|
]
|
|
1854
1890
|
|
|
1855
1891
|
[[package]]
|
|
1856
1892
|
name = "wasmtime"
|
|
1857
|
-
version = "
|
|
1893
|
+
version = "47.0.3"
|
|
1858
1894
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1859
|
-
checksum = "
|
|
1895
|
+
checksum = "c80ca6098e0d4d06886d91d7f2cc3cb6623eb583c4c0ab3c89cbfb6098c8586c"
|
|
1860
1896
|
dependencies = [
|
|
1861
|
-
"addr2line 0.
|
|
1897
|
+
"addr2line 0.26.1",
|
|
1862
|
-
"anyhow",
|
|
1863
1898
|
"async-trait",
|
|
1864
1899
|
"bitflags",
|
|
1865
1900
|
"bumpalo",
|
|
1866
1901
|
"cc",
|
|
1867
1902
|
"cfg-if",
|
|
1868
1903
|
"encoding_rs",
|
|
1904
|
+
"futures",
|
|
1869
1905
|
"fxprof-processed-profile",
|
|
1870
|
-
"gimli 0.
|
|
1906
|
+
"gimli 0.33.0",
|
|
1871
|
-
"hashbrown 0.14.5",
|
|
1872
|
-
"indexmap",
|
|
1873
1907
|
"ittapi",
|
|
1874
1908
|
"libc",
|
|
1875
|
-
"libm",
|
|
1876
1909
|
"log",
|
|
1877
1910
|
"mach2",
|
|
1878
1911
|
"memfd",
|
|
1879
|
-
"object 0.
|
|
1912
|
+
"object 0.39.1",
|
|
1880
1913
|
"once_cell",
|
|
1881
|
-
"paste",
|
|
1882
1914
|
"postcard",
|
|
1883
|
-
"psm",
|
|
1884
1915
|
"pulley-interpreter",
|
|
1885
1916
|
"rayon",
|
|
1886
|
-
"rustix
|
|
1917
|
+
"rustix",
|
|
1887
1918
|
"semver",
|
|
1888
1919
|
"serde",
|
|
1889
1920
|
"serde_derive",
|
|
1890
1921
|
"serde_json",
|
|
1891
1922
|
"smallvec",
|
|
1892
|
-
"sptr",
|
|
1893
1923
|
"target-lexicon",
|
|
1924
|
+
"tempfile",
|
|
1925
|
+
"wasm-compose",
|
|
1894
|
-
"wasm-encoder 0.
|
|
1926
|
+
"wasm-encoder 0.252.0",
|
|
1895
|
-
"wasmparser 0.
|
|
1927
|
+
"wasmparser 0.252.0",
|
|
1896
|
-
"wasmtime-asm-macros",
|
|
1897
|
-
"wasmtime-cache",
|
|
1898
|
-
"wasmtime-component-macro",
|
|
1899
|
-
"wasmtime-component-util",
|
|
1900
|
-
"wasmtime-cranelift",
|
|
1901
1928
|
"wasmtime-environ",
|
|
1929
|
+
"wasmtime-internal-cache",
|
|
1930
|
+
"wasmtime-internal-component-macro",
|
|
1931
|
+
"wasmtime-internal-component-util",
|
|
1932
|
+
"wasmtime-internal-core",
|
|
1933
|
+
"wasmtime-internal-cranelift",
|
|
1902
|
-
"wasmtime-fiber",
|
|
1934
|
+
"wasmtime-internal-fiber",
|
|
1903
|
-
"wasmtime-jit-debug",
|
|
1935
|
+
"wasmtime-internal-jit-debug",
|
|
1904
|
-
"wasmtime-jit-icache-coherence",
|
|
1936
|
+
"wasmtime-internal-jit-icache-coherence",
|
|
1905
|
-
"wasmtime-
|
|
1937
|
+
"wasmtime-internal-unwinder",
|
|
1906
|
-
"wasmtime-versioned-export-macros",
|
|
1938
|
+
"wasmtime-internal-versioned-export-macros",
|
|
1907
|
-
"wasmtime-winch",
|
|
1908
1939
|
"wat",
|
|
1909
|
-
"windows-sys
|
|
1940
|
+
"windows-sys",
|
|
1941
|
+
"wit-parser",
|
|
1910
1942
|
]
|
|
1911
1943
|
|
|
1912
1944
|
[[package]]
|
|
1913
|
-
name = "wasmtime-
|
|
1945
|
+
name = "wasmtime-environ"
|
|
1914
|
-
version = "
|
|
1946
|
+
version = "47.0.3"
|
|
1915
1947
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1916
|
-
checksum = "
|
|
1948
|
+
checksum = "134f9d136d29c76f6c1b4c9b468e97a2efc38c7d49fd188e39b64870b2fea701"
|
|
1917
1949
|
dependencies = [
|
|
1950
|
+
"anyhow",
|
|
1951
|
+
"cpp_demangle",
|
|
1952
|
+
"cranelift-bforest",
|
|
1953
|
+
"cranelift-bitset",
|
|
1954
|
+
"cranelift-entity",
|
|
1955
|
+
"gimli 0.33.0",
|
|
1956
|
+
"hashbrown 0.17.1",
|
|
1957
|
+
"indexmap",
|
|
1918
|
-
"
|
|
1958
|
+
"log",
|
|
1959
|
+
"object 0.39.1",
|
|
1960
|
+
"postcard",
|
|
1961
|
+
"rustc-demangle",
|
|
1962
|
+
"semver",
|
|
1963
|
+
"serde",
|
|
1964
|
+
"serde_derive",
|
|
1965
|
+
"sha2",
|
|
1966
|
+
"smallvec",
|
|
1967
|
+
"target-lexicon",
|
|
1968
|
+
"wasm-encoder 0.252.0",
|
|
1969
|
+
"wasmparser 0.252.0",
|
|
1970
|
+
"wasmprinter",
|
|
1971
|
+
"wasmtime-internal-component-util",
|
|
1972
|
+
"wasmtime-internal-core",
|
|
1919
1973
|
]
|
|
1920
1974
|
|
|
1921
1975
|
[[package]]
|
|
1922
|
-
name = "wasmtime-cache"
|
|
1976
|
+
name = "wasmtime-internal-cache"
|
|
1923
|
-
version = "
|
|
1977
|
+
version = "47.0.3"
|
|
1924
1978
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1925
|
-
checksum = "
|
|
1979
|
+
checksum = "65db2eb1bfc5371a3b4107dbf539d0b93d05016fc29625b1648146b47db02071"
|
|
1926
1980
|
dependencies = [
|
|
1927
|
-
"anyhow",
|
|
1928
1981
|
"base64",
|
|
1929
1982
|
"directories-next",
|
|
1930
1983
|
"log",
|
|
1931
1984
|
"postcard",
|
|
1932
|
-
"rustix
|
|
1985
|
+
"rustix",
|
|
1933
1986
|
"serde",
|
|
1934
1987
|
"serde_derive",
|
|
1935
1988
|
"sha2",
|
|
1936
1989
|
"toml",
|
|
1990
|
+
"wasmtime-environ",
|
|
1937
|
-
"windows-sys
|
|
1991
|
+
"windows-sys",
|
|
1938
1992
|
"zstd",
|
|
1939
1993
|
]
|
|
1940
1994
|
|
|
1941
1995
|
[[package]]
|
|
1942
|
-
name = "wasmtime-component-macro"
|
|
1996
|
+
name = "wasmtime-internal-component-macro"
|
|
1943
|
-
version = "
|
|
1997
|
+
version = "47.0.3"
|
|
1944
1998
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1945
|
-
checksum = "
|
|
1999
|
+
checksum = "2bf7b91fed3fc34781d57f9c6654ea2513bf0a99f7b0b0523c00de1e6efebe1c"
|
|
1946
2000
|
dependencies = [
|
|
1947
2001
|
"anyhow",
|
|
1948
2002
|
"proc-macro2",
|
|
1949
2003
|
"quote",
|
|
1950
2004
|
"syn 2.0.119",
|
|
1951
|
-
"wasmtime-component-util",
|
|
2005
|
+
"wasmtime-internal-component-util",
|
|
1952
|
-
"wasmtime-wit-bindgen",
|
|
2006
|
+
"wasmtime-internal-wit-bindgen",
|
|
1953
2007
|
"wit-parser",
|
|
1954
2008
|
]
|
|
1955
2009
|
|
|
1956
2010
|
[[package]]
|
|
1957
|
-
name = "wasmtime-component-util"
|
|
2011
|
+
name = "wasmtime-internal-component-util"
|
|
1958
|
-
version = "
|
|
2012
|
+
version = "47.0.3"
|
|
1959
2013
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1960
|
-
checksum = "
|
|
2014
|
+
checksum = "fc8a678149885cae00289f806fbe74c7863084cf74cace0b8dc73602279400e1"
|
|
1961
2015
|
|
|
1962
2016
|
[[package]]
|
|
1963
|
-
name = "wasmtime-
|
|
2017
|
+
name = "wasmtime-internal-core"
|
|
1964
|
-
version = "
|
|
2018
|
+
version = "47.0.3"
|
|
1965
2019
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1966
|
-
checksum = "
|
|
2020
|
+
checksum = "8a0092c4b9d070ac5e278b6d0db10f5e71214190f0347ca57502b4692f796321"
|
|
1967
2021
|
dependencies = [
|
|
1968
2022
|
"anyhow",
|
|
2023
|
+
"hashbrown 0.17.1",
|
|
2024
|
+
"libm",
|
|
2025
|
+
"serde",
|
|
2026
|
+
]
|
|
2027
|
+
|
|
2028
|
+
[[package]]
|
|
2029
|
+
name = "wasmtime-internal-cranelift"
|
|
2030
|
+
version = "47.0.3"
|
|
2031
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2032
|
+
checksum = "6851ebc9e03cab23d9821d2b2505d380bdfe38f35ccec945247b05274d85c98b"
|
|
2033
|
+
dependencies = [
|
|
1969
2034
|
"cfg-if",
|
|
1970
2035
|
"cranelift-codegen",
|
|
1971
2036
|
"cranelift-control",
|
|
1972
2037
|
"cranelift-entity",
|
|
1973
2038
|
"cranelift-frontend",
|
|
1974
2039
|
"cranelift-native",
|
|
1975
|
-
"gimli 0.
|
|
2040
|
+
"gimli 0.33.0",
|
|
1976
|
-
"itertools 0.
|
|
2041
|
+
"itertools 0.14.0",
|
|
1977
2042
|
"log",
|
|
1978
|
-
"object 0.
|
|
2043
|
+
"object 0.39.1",
|
|
2044
|
+
"pulley-interpreter",
|
|
1979
2045
|
"smallvec",
|
|
1980
2046
|
"target-lexicon",
|
|
1981
|
-
"thiserror
|
|
2047
|
+
"thiserror 2.0.19",
|
|
1982
|
-
"wasmparser 0.
|
|
2048
|
+
"wasmparser 0.252.0",
|
|
1983
2049
|
"wasmtime-environ",
|
|
2050
|
+
"wasmtime-internal-core",
|
|
2051
|
+
"wasmtime-internal-unwinder",
|
|
1984
|
-
"wasmtime-versioned-export-macros",
|
|
2052
|
+
"wasmtime-internal-versioned-export-macros",
|
|
1985
|
-
]
|
|
1986
|
-
|
|
1987
|
-
[[package]]
|
|
1988
|
-
name = "wasmtime-environ"
|
|
1989
|
-
version = "28.0.1"
|
|
1990
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1991
|
-
checksum = "264c968c1b81d340355ece2be0bc31a10f567ccb6ce08512c3b7d10e26f3cbe5"
|
|
1992
|
-
dependencies = [
|
|
1993
|
-
"anyhow",
|
|
1994
|
-
"cpp_demangle",
|
|
1995
|
-
"cranelift-bitset",
|
|
1996
|
-
"cranelift-entity",
|
|
1997
|
-
"gimli 0.31.1",
|
|
1998
|
-
"indexmap",
|
|
1999
|
-
"log",
|
|
2000
|
-
"object 0.36.7",
|
|
2001
|
-
"postcard",
|
|
2002
|
-
"rustc-demangle",
|
|
2003
|
-
"semver",
|
|
2004
|
-
"serde",
|
|
2005
|
-
"serde_derive",
|
|
2006
|
-
"smallvec",
|
|
2007
|
-
"target-lexicon",
|
|
2008
|
-
"wasm-encoder 0.221.3",
|
|
2009
|
-
"wasmparser 0.221.3",
|
|
2010
|
-
"wasmprinter",
|
|
2011
|
-
"wasmtime-component-util",
|
|
2012
2053
|
]
|
|
2013
2054
|
|
|
2014
2055
|
[[package]]
|
|
2015
|
-
name = "wasmtime-fiber"
|
|
2056
|
+
name = "wasmtime-internal-fiber"
|
|
2016
|
-
version = "
|
|
2057
|
+
version = "47.0.3"
|
|
2017
2058
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2018
|
-
checksum = "
|
|
2059
|
+
checksum = "9b26da6d5f60d4c438da70bba3553fe810a840533a64156be287dca2081c6991"
|
|
2019
2060
|
dependencies = [
|
|
2020
|
-
"anyhow",
|
|
2021
2061
|
"cc",
|
|
2022
2062
|
"cfg-if",
|
|
2063
|
+
"libc",
|
|
2023
|
-
"rustix
|
|
2064
|
+
"rustix",
|
|
2024
|
-
"wasmtime-
|
|
2065
|
+
"wasmtime-environ",
|
|
2025
|
-
"wasmtime-versioned-export-macros",
|
|
2066
|
+
"wasmtime-internal-versioned-export-macros",
|
|
2026
|
-
"windows-sys
|
|
2067
|
+
"windows-sys",
|
|
2027
2068
|
]
|
|
2028
2069
|
|
|
2029
2070
|
[[package]]
|
|
2030
|
-
name = "wasmtime-jit-debug"
|
|
2071
|
+
name = "wasmtime-internal-jit-debug"
|
|
2031
|
-
version = "
|
|
2072
|
+
version = "47.0.3"
|
|
2032
2073
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2033
|
-
checksum = "
|
|
2074
|
+
checksum = "ed621ba25d7bf78b7edd7b4749abbb27c2e5cdba836c9504a424ee74b6c23149"
|
|
2034
2075
|
dependencies = [
|
|
2076
|
+
"cc",
|
|
2035
|
-
"object 0.
|
|
2077
|
+
"object 0.39.1",
|
|
2036
|
-
"rustix
|
|
2078
|
+
"rustix",
|
|
2037
|
-
"wasmtime-versioned-export-macros",
|
|
2079
|
+
"wasmtime-internal-versioned-export-macros",
|
|
2038
2080
|
]
|
|
2039
2081
|
|
|
2040
2082
|
[[package]]
|
|
2041
|
-
name = "wasmtime-jit-icache-coherence"
|
|
2083
|
+
name = "wasmtime-internal-jit-icache-coherence"
|
|
2042
|
-
version = "
|
|
2084
|
+
version = "47.0.3"
|
|
2043
2085
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2044
|
-
checksum = "
|
|
2086
|
+
checksum = "5684ba160951baad06a725696f3c590e2fb0e8067c2aebee27bf7f9259058e85"
|
|
2045
2087
|
dependencies = [
|
|
2046
|
-
"anyhow",
|
|
2047
2088
|
"cfg-if",
|
|
2048
2089
|
"libc",
|
|
2090
|
+
"wasmtime-internal-core",
|
|
2049
|
-
"windows-sys
|
|
2091
|
+
"windows-sys",
|
|
2050
2092
|
]
|
|
2051
2093
|
|
|
2052
2094
|
[[package]]
|
|
2053
|
-
name = "wasmtime-
|
|
2095
|
+
name = "wasmtime-internal-unwinder"
|
|
2054
|
-
version = "
|
|
2096
|
+
version = "47.0.3"
|
|
2055
2097
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2056
|
-
checksum = "
|
|
2098
|
+
checksum = "112eead527bffa8ff0646a11fb4339a9d52ddd1da2b9a6fce4aff84c815dd94f"
|
|
2099
|
+
dependencies = [
|
|
2100
|
+
"cfg-if",
|
|
2101
|
+
"cranelift-codegen",
|
|
2102
|
+
"log",
|
|
2103
|
+
"object 0.39.1",
|
|
2104
|
+
"wasmtime-environ",
|
|
2105
|
+
]
|
|
2057
2106
|
|
|
2058
2107
|
[[package]]
|
|
2059
|
-
name = "wasmtime-versioned-export-macros"
|
|
2108
|
+
name = "wasmtime-internal-versioned-export-macros"
|
|
2060
|
-
version = "
|
|
2109
|
+
version = "47.0.3"
|
|
2061
2110
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2062
|
-
checksum = "
|
|
2111
|
+
checksum = "153592e0bed824fc13c6696203fa1bd7bd20eb355316475e39a55f081ef80eca"
|
|
2063
2112
|
dependencies = [
|
|
2064
2113
|
"proc-macro2",
|
|
2065
2114
|
"quote",
|
|
@@ -2067,29 +2116,13 @@ dependencies = [
|
|
|
2067
2116
|
]
|
|
2068
2117
|
|
|
2069
2118
|
[[package]]
|
|
2070
|
-
name = "wasmtime-
|
|
2119
|
+
name = "wasmtime-internal-wit-bindgen"
|
|
2071
|
-
version = "
|
|
2120
|
+
version = "47.0.3"
|
|
2072
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2073
|
-
checksum = "b111d909dc604c741bd8ac2f4af373eaa5c68c34b5717271bcb687688212cef8"
|
|
2074
|
-
dependencies = [
|
|
2075
|
-
"anyhow",
|
|
2076
|
-
"cranelift-codegen",
|
|
2077
|
-
"gimli 0.31.1",
|
|
2078
|
-
"object 0.36.7",
|
|
2079
|
-
"target-lexicon",
|
|
2080
|
-
"wasmparser 0.221.3",
|
|
2081
|
-
"wasmtime-cranelift",
|
|
2082
|
-
"wasmtime-environ",
|
|
2083
|
-
"winch-codegen",
|
|
2084
|
-
]
|
|
2085
|
-
|
|
2086
|
-
[[package]]
|
|
2087
|
-
name = "wasmtime-wit-bindgen"
|
|
2088
|
-
version = "28.0.1"
|
|
2089
2121
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2090
|
-
checksum = "
|
|
2122
|
+
checksum = "c456ad6e81e0f46abfeca43687d18ea15e289c395b262ea6e0023e2682e088be"
|
|
2091
2123
|
dependencies = [
|
|
2092
2124
|
"anyhow",
|
|
2125
|
+
"bitflags",
|
|
2093
2126
|
"heck",
|
|
2094
2127
|
"indexmap",
|
|
2095
2128
|
"wit-parser",
|
|
@@ -2149,7 +2182,7 @@ version = "0.1.11"
|
|
|
2149
2182
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2150
2183
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
|
2151
2184
|
dependencies = [
|
|
2152
|
-
"windows-sys
|
|
2185
|
+
"windows-sys",
|
|
2153
2186
|
]
|
|
2154
2187
|
|
|
2155
2188
|
[[package]]
|
|
@@ -2158,38 +2191,12 @@ version = "0.4.0"
|
|
|
2158
2191
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2159
2192
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
2160
2193
|
|
|
2161
|
-
[[package]]
|
|
2162
|
-
name = "winch-codegen"
|
|
2163
|
-
version = "28.0.1"
|
|
2164
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2165
|
-
checksum = "6232f40a795be2ce10fc761ed3b403825126a60d12491ac556ea104a932fd18a"
|
|
2166
|
-
dependencies = [
|
|
2167
|
-
"anyhow",
|
|
2168
|
-
"cranelift-codegen",
|
|
2169
|
-
"gimli 0.31.1",
|
|
2170
|
-
"regalloc2",
|
|
2171
|
-
"smallvec",
|
|
2172
|
-
"target-lexicon",
|
|
2173
|
-
"wasmparser 0.221.3",
|
|
2174
|
-
"wasmtime-cranelift",
|
|
2175
|
-
"wasmtime-environ",
|
|
2176
|
-
]
|
|
2177
|
-
|
|
2178
2194
|
[[package]]
|
|
2179
2195
|
name = "windows-link"
|
|
2180
2196
|
version = "0.2.1"
|
|
2181
2197
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2182
2198
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
2183
2199
|
|
|
2184
|
-
[[package]]
|
|
2185
|
-
name = "windows-sys"
|
|
2186
|
-
version = "0.59.0"
|
|
2187
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2188
|
-
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
|
2189
|
-
dependencies = [
|
|
2190
|
-
"windows-targets",
|
|
2191
|
-
]
|
|
2192
|
-
|
|
2193
2200
|
[[package]]
|
|
2194
2201
|
name = "windows-sys"
|
|
2195
2202
|
version = "0.61.2"
|
|
@@ -2199,70 +2206,6 @@ dependencies = [
|
|
|
2199
2206
|
"windows-link",
|
|
2200
2207
|
]
|
|
2201
2208
|
|
|
2202
|
-
[[package]]
|
|
2203
|
-
name = "windows-targets"
|
|
2204
|
-
version = "0.52.6"
|
|
2205
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2206
|
-
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
|
2207
|
-
dependencies = [
|
|
2208
|
-
"windows_aarch64_gnullvm",
|
|
2209
|
-
"windows_aarch64_msvc",
|
|
2210
|
-
"windows_i686_gnu",
|
|
2211
|
-
"windows_i686_gnullvm",
|
|
2212
|
-
"windows_i686_msvc",
|
|
2213
|
-
"windows_x86_64_gnu",
|
|
2214
|
-
"windows_x86_64_gnullvm",
|
|
2215
|
-
"windows_x86_64_msvc",
|
|
2216
|
-
]
|
|
2217
|
-
|
|
2218
|
-
[[package]]
|
|
2219
|
-
name = "windows_aarch64_gnullvm"
|
|
2220
|
-
version = "0.52.6"
|
|
2221
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2222
|
-
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
|
2223
|
-
|
|
2224
|
-
[[package]]
|
|
2225
|
-
name = "windows_aarch64_msvc"
|
|
2226
|
-
version = "0.52.6"
|
|
2227
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2228
|
-
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
|
2229
|
-
|
|
2230
|
-
[[package]]
|
|
2231
|
-
name = "windows_i686_gnu"
|
|
2232
|
-
version = "0.52.6"
|
|
2233
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2234
|
-
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
|
2235
|
-
|
|
2236
|
-
[[package]]
|
|
2237
|
-
name = "windows_i686_gnullvm"
|
|
2238
|
-
version = "0.52.6"
|
|
2239
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2240
|
-
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
|
2241
|
-
|
|
2242
|
-
[[package]]
|
|
2243
|
-
name = "windows_i686_msvc"
|
|
2244
|
-
version = "0.52.6"
|
|
2245
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2246
|
-
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
|
2247
|
-
|
|
2248
|
-
[[package]]
|
|
2249
|
-
name = "windows_x86_64_gnu"
|
|
2250
|
-
version = "0.52.6"
|
|
2251
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2252
|
-
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
|
2253
|
-
|
|
2254
|
-
[[package]]
|
|
2255
|
-
name = "windows_x86_64_gnullvm"
|
|
2256
|
-
version = "0.52.6"
|
|
2257
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2258
|
-
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
|
2259
|
-
|
|
2260
|
-
[[package]]
|
|
2261
|
-
name = "windows_x86_64_msvc"
|
|
2262
|
-
version = "0.52.6"
|
|
2263
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2264
|
-
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|
2265
|
-
|
|
2266
2209
|
[[package]]
|
|
2267
2210
|
name = "winnow"
|
|
2268
2211
|
version = "0.7.15"
|
|
@@ -2272,13 +2215,20 @@ dependencies = [
|
|
|
2272
2215
|
"memchr",
|
|
2273
2216
|
]
|
|
2274
2217
|
|
|
2218
|
+
[[package]]
|
|
2219
|
+
name = "winnow"
|
|
2220
|
+
version = "1.0.4"
|
|
2221
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2222
|
+
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
|
2223
|
+
|
|
2275
2224
|
[[package]]
|
|
2276
2225
|
name = "wit-parser"
|
|
2277
|
-
version = "0.
|
|
2226
|
+
version = "0.252.0"
|
|
2278
2227
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2279
|
-
checksum = "
|
|
2228
|
+
checksum = "4266bea110371c620ccf3201c5023676046bc4556e5c7cfb5d500bda5ebc162d"
|
|
2280
2229
|
dependencies = [
|
|
2281
2230
|
"anyhow",
|
|
2231
|
+
"hashbrown 0.17.1",
|
|
2282
2232
|
"id-arena",
|
|
2283
2233
|
"indexmap",
|
|
2284
2234
|
"log",
|
|
@@ -2286,8 +2236,8 @@ dependencies = [
|
|
|
2286
2236
|
"serde",
|
|
2287
2237
|
"serde_derive",
|
|
2288
2238
|
"serde_json",
|
|
2289
|
-
"unicode-
|
|
2239
|
+
"unicode-ident",
|
|
2290
|
-
"wasmparser 0.
|
|
2240
|
+
"wasmparser 0.252.0",
|
|
2291
2241
|
]
|
|
2292
2242
|
|
|
2293
2243
|
[[package]]
|
docs/superpowers/plans/2026-07-25-wasm-gc-migration.md
CHANGED
|
@@ -36,31 +36,31 @@
|
|
|
36
36
|
- Consumes: nothing (bottom of this plan's dependency chain).
|
|
37
37
|
- Produces: every `wasmtime::Engine`/`Config` used in tests has the GC proposal enabled; a new type-section emitter that, given the checker's monomorphized `Source`, emits one wasm-gc `struct` per concrete class, one abstract-supertype-plus-per-variant-subtype set per enum (Bool included, built in exactly like the existing hardcoded `True`/`False` `EnumVariantInfo` registration), and one `array<i8>` type for `Str` — **wired up but not yet consumed by any real codegen path**, so all EXISTING tests (still running through the untouched bump-allocator codegen) stay green. This task is purely additive.
|
|
38
38
|
|
|
39
|
-
- [
|
|
39
|
+
- [x] **Step 1: Confirm wasmtime's GC `Config` actually works empirically, first**
|
|
40
40
|
|
|
41
41
|
Before writing any type-emitter code, write a throwaway test (in `codegen_tests.rs`, can be deleted/replaced by Task 2) that: builds a `wasmtime::Config`, calls `.wasm_gc(true)` (and `.wasm_function_references(true)` — check by reading `wasmtime-28.0.1`'s `config.rs`, vendored at `~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wasmtime-28.0.1/src/config.rs`, whether GC has an undeclared dependency on function-references that isn't auto-enabled), constructs an `Engine` from it, and hand-encodes (via `wasm_encoder` directly, no plum involved) the smallest possible module with one GC struct type, one function that does `struct.new_default` + returns it typed as `(ref null $T)`, and confirms it validates AND instantiates+runs under that `Engine`. If this fails or exhibits a real engine limitation, that finding drives every subsequent step — report it before proceeding, don't route around it silently.
|
|
42
42
|
|
|
43
|
-
- [
|
|
43
|
+
- [x] **Step 2: Update the test harness's `Engine`/`Config` construction**
|
|
44
44
|
|
|
45
45
|
Read the current `Engine::default()` (or equivalent) call sites in `codegen_tests.rs` and `examples_test.rs` (search `wasmtime::Engine`) and replace with the `Config` confirmed working in Step 1. Every test in both files must still pass unchanged (they're still exercising the untouched bump-allocator codegen at this point) — this step is purely "can a GC-enabled engine still run today's non-GC output," which it should, since enabling a feature doesn't retroactively require using it.
|
|
46
46
|
|
|
47
|
-
- [
|
|
47
|
+
- [x] **Step 3: Read the current type-registration surface before extending it**
|
|
48
48
|
|
|
49
49
|
Read `plum-checker`'s `ClassEnv`/`EnumVariants`/`EnumVariantInfo` definitions (`plum-checker/src/lib.rs`) and `plum-wasm-codegen`'s `CompileCtx`/`LocalCtx` (`plum-wasm-codegen/src/lib.rs`) exact current fields — this plan's earlier research read them as of the enum-discriminant-values plan's completion, but re-verify, since intervening work may have touched them.
|
|
50
50
|
|
|
51
|
-
- [
|
|
51
|
+
- [x] **Step 4: Add a GC type registry, populated but unconsumed**
|
|
52
52
|
|
|
53
53
|
Add a new struct (e.g. `GcTypeRegistry`) that, given the monomorphized `ast::Source` plus the existing `ClassEnv`/`EnumVariants` tables, assigns and records a wasm type-section index for: every concrete class name, every enum's abstract supertype, every enum variant's concrete subtype, and one shared `array<i8>` index for `Str`. Emit these into a real `TypeSection` via `CoreTypeEncoder::struct_`/`array`/`subtype` (structs need `SubType { is_final: false, supertype_idx: None, ... }` for a class since it needs no subtyping, but an enum's supertype needs `is_final: false` so variants CAN subtype it, and each variant subtype needs `SubType { supertype_idx: Some(super_idx), ... }`) — group the whole enum's supertype+variants in one `rec` group (`CoreTypeEncoder::rec`) since wasm-gc subtyping requires supertype and subtype to be declared in the same recursive group or supertype declared earlier in an earlier group; read `wasm-encoder`'s `rec`/`subtype` doc comments (`~/.cargo/registry/.../wasm-encoder-0.220.1/src/core/types.rs`) to get the ordering constraint exactly right. Thread the resulting index tables through `CompileCtx` as new fields (e.g. `class_type_idx: HashMap<String, u32>`, `enum_super_type_idx: HashMap<String, u32>`, `variant_type_idx: HashMap<String, u32>`, `str_type_idx: u32`), populated in `compileSource` but not read by anything else yet.
|
|
54
54
|
|
|
55
|
-
- [
|
|
55
|
+
- [x] **Step 5: Add a corresponding Rust test proving the type section is well-formed**
|
|
56
56
|
|
|
57
57
|
Add a test that compiles a small source (one class, one enum with a payload variant and a payload-free variant) through `compileSource`, and asserts the resulting bytes validate via `wasmparser::validate` under the GC-enabled config from Step 1 — this proves the new type section alone (still unused by real codegen) is well-formed, before Task 2 starts depending on it.
|
|
58
58
|
|
|
59
|
-
- [
|
|
59
|
+
- [x] **Step 6: Run full verification**
|
|
60
60
|
|
|
61
61
|
`cargo build --workspace --all-targets` then `cargo test --workspace`. Expected: 100% pass, identical results to before this task — nothing behavioral changed yet.
|
|
62
62
|
|
|
63
|
-
- [
|
|
63
|
+
- [x] **Step 7: Commit**
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
git add plum-wasm-codegen/src/lib.rs plum-wasm-codegen/tests/codegen_tests.rs plum-wasm-codegen/tests/examples_test.rs plum-wasm-codegen/Cargo.toml
|
|
@@ -81,105 +81,105 @@ This task is large enough that its steps are grouped by concern. Within each con
|
|
|
81
81
|
|
|
82
82
|
#### 2a. Bool/enum singletons and control-flow conversion (do this first — nearly everything else depends on being able to produce/consume a Bool)
|
|
83
83
|
|
|
84
|
-
- [
|
|
84
|
+
- [x] **Step 1: Read `compileSource`'s current initialization sequence, `compileStmt`'s `If`/`While` arms, `boolean_operator`/comparison codegen, and `assert` codegen**
|
|
85
85
|
|
|
86
86
|
These are all in `plum-wasm-codegen/src/lib.rs` — find them by searching `ast::Stmt::If`, `ast::Stmt::While`, `ast::Expr::Bool(`, `ast::Expr::Compare(`, `ast::Stmt::Assert`.
|
|
87
87
|
|
|
88
|
-
- [
|
|
88
|
+
- [x] **Step 2: Emit one global per payload-free variant across the whole program (including `True`/`False`)**
|
|
89
89
|
|
|
90
90
|
In `compileSource`, after the type registry (Task 1) is built, walk every enum's variants (plus the built-in `Bool`) and for each payload-free one, emit a `global` (mutable: false is ideal but wasm-gc struct globals may need an initializer expression capable of `struct.new` — read `wasm_encoder`'s `ConstExpr`/`GlobalSection` to confirm whether a GC `struct.new` is legal inside a global's constant-initializer expression under this wasm-gc revision; if not, fall back to a `start` function that runs once and stores each singleton into a mutable global before `main`/any export can run). Record `variant_name -> global_index` in a new `CompileCtx` field.
|
|
91
91
|
|
|
92
|
-
- [
|
|
92
|
+
- [x] **Step 3: Rewrite conditional control flow to convert Bool refs to i32 via `ref.test`**
|
|
93
93
|
|
|
94
94
|
Wherever a `Bool`-typed value currently feeds `Instruction::If`/`BrIf` unchanged (it's already an i32 today), insert `Instruction::RefTestNonNull(HeapType::Concrete(true_variant_type_idx))` immediately before it. This applies to at least: `if`/`else if` conditions, `while` conditions, `assert`, and short-circuit `&&`/`||` (which currently, per existing codegen, likely already lower to nested `If` blocks for short-circuiting — read the current `boolean_operator` codegen to confirm the exact shape before changing it).
|
|
95
95
|
|
|
96
|
-
- [
|
|
96
|
+
- [x] **Step 4: Rewrite comparison operators to produce a Bool ref from a native i32 predicate**
|
|
97
97
|
|
|
98
98
|
Currently a comparison (`==`, `<`, etc.) directly leaves an i32 on the stack as the Bool result. Change it to: compute the native i32 predicate as today, then `Instruction::If(BlockType::Result(bool_ref_type))` / push `GlobalGet(true_singleton)` / `Else` / `GlobalGet(false_singleton)` / `End`, so the result is a proper `Bool` ref matching every other Bool-producing expression.
|
|
99
99
|
|
|
100
|
-
- [
|
|
100
|
+
- [x] **Step 5: Update every OTHER payload-free bare-variant construction site to use its singleton global instead of allocating**
|
|
101
101
|
|
|
102
102
|
Search for the current handling of `ast::Expr::TypeName(n)` where `info.field_types.is_empty()` (bare tag construction) — this currently does `Instruction::I32Const(info.tag)`; change it to `Instruction::GlobalGet(singleton_idx)`.
|
|
103
103
|
|
|
104
|
-
- [
|
|
104
|
+
- [x] **Step 6: Build and run affected tests only, iterating**
|
|
105
105
|
|
|
106
106
|
`cargo build -p plum-wasm-codegen` first (fast compile-only check — expect many errors from downstream code still assuming i32 Bool/tags, which is fine, that's 2b/2c/2d's job). Do not attempt `cargo test` yet — the crate won't fully compile again until struct/enum construction (2b) and match dispatch (2d) also land, since they're mutually dependent on the same representation.
|
|
107
107
|
|
|
108
108
|
#### 2b. Struct/class construction and field access
|
|
109
109
|
|
|
110
|
-
- [
|
|
110
|
+
- [x] **Step 1: Read the current `ast::Expr::ClassCall` construction arm and the `Attribute`/`Field` read/write codegen**
|
|
111
111
|
|
|
112
112
|
Search `ast::Expr::ClassCall`, `ast::AttrKind::Field`, `ast::AssignTarget::Field`.
|
|
113
113
|
|
|
114
|
-
- [
|
|
114
|
+
- [x] **Step 2: Rewrite construction to `struct.new`**
|
|
115
115
|
|
|
116
116
|
For each `ClassCall`, look up the class's declared field order (not `call.fields`'s written order — construction must push values in DECLARATION order for `struct.new`). For each declared field, find the matching `call.fields` entry by name and `compileExpr` its value onto the stack; once every field's value is pushed, emit `Instruction::StructNew(class_type_idx)`. The `classcall_scratch` local is very likely no longer needed for this case (no bump-pointer bookkeeping requires holding an intermediate address — verify by reading whether anything else in the current code relies on that scratch slot for `ClassCall` specifically before deleting its allocation for this case).
|
|
117
117
|
|
|
118
|
-
- [
|
|
118
|
+
- [x] **Step 3: Rewrite field reads to `struct.get`**
|
|
119
119
|
|
|
120
120
|
Replace the current class-field-read arm's `emitLoad` at a byte offset with `compileExpr` (pushes the object ref) followed by `Instruction::StructGet { struct_type_index: class_type_idx, field_index }`.
|
|
121
121
|
|
|
122
|
-
- [
|
|
122
|
+
- [x] **Step 4: Rewrite field writes (`self.x = ...`, chained `a.b.c = ...`) to `struct.set`**
|
|
123
123
|
|
|
124
124
|
Same shape: compile the object ref, compile the new value, `Instruction::StructSet { struct_type_index, field_index }`. `StructSet`'s stack order — verify from `wasm-encoder`'s encode impl or the wasm-gc spec whether it expects `(struct_ref, value)` or `(value, struct_ref)` push order; get this from the actual instruction encoding, not assumption, since getting it backwards produces a type-valid-looking-in-isolation-but-wrong-at-runtime bug that validation may not always catch depending on other stack contents.
|
|
125
125
|
|
|
126
|
-
- [
|
|
126
|
+
- [x] **Step 5: Rewrite discriminant-enum field access (`self.n` on an `enum Foo(n: Int)`)** analogously, through `ctx.enum_params`, using `StructGet`/`StructSet` against the enum's own struct layout with the `field_idx + 1` tag-slot offset convention preserved conceptually (though the "+1 for tag" byte-offset trick is a linear-memory artifact — under wasm-gc each variant subtype simply has its own explicit field list starting at field index 0 for the first real field, no tag slot needed at all, since the tag IS the type itself now, discoverable via `ref.test`, not a stored value read at a fixed offset).
|
|
127
127
|
|
|
128
128
|
#### 2c. String migration to `array<i8>`
|
|
129
129
|
|
|
130
|
-
- [
|
|
130
|
+
- [x] **Step 1: Read the current string literal encoding, `registerStringConcatHelper`, `registerIntToStringHelper`, and string interpolation codegen**
|
|
131
131
|
|
|
132
132
|
Search `compile_static_string` (or equivalent), `registerStringConcatHelper`, `registerIntToStringHelper`.
|
|
133
133
|
|
|
134
|
-
- [
|
|
134
|
+
- [x] **Step 2: Static string literals become passive-data-segment `array.new_data`**
|
|
135
135
|
|
|
136
136
|
Each string literal's bytes go into one passive `DataSection::passive(bytes)` segment (no active memory offset — confirm `wasm_encoder`'s `DataSection::passive` doesn't require a preceding memory section to exist at all, per this plan's Decision 4). At the literal's use site, emit `Instruction::ArrayNewData { array_type_index: str_type_idx, array_data_index }`.
|
|
137
137
|
|
|
138
|
-
- [
|
|
138
|
+
- [x] **Step 3: Rewrite `registerStringConcatHelper` against `array.new`/`array.copy`**
|
|
139
139
|
|
|
140
140
|
Read the exact current helper body first. The new version needs the combined length (`array.len` on both inputs, or track lengths already known from a length-prefix design if you keep one — decide whether `Str`'s `array<i8>` needs an explicit stored length or relies on `array.len`, and note that GC arrays DO track their own length natively, so a manual length prefix is no longer needed at all, simplifying this over the current length-prefixed-blob design), allocates a new array of that combined size (`array.new_default` sized dynamically — check whether `array.new_default` accepts a non-constant size operand; it does, per the wasm-gc spec, size is a runtime i32 operand), then two `Instruction::ArrayCopy { array_type_index_dst, array_type_index_src }` calls to copy each source's bytes into the destination at the right offset.
|
|
141
141
|
|
|
142
|
-
- [
|
|
142
|
+
- [x] **Step 4: Rewrite `registerIntToStringHelper` similarly** — read its current byte-writing loop and adapt each byte store to `array.set` (or build into a fixed-size scratch and `array.new_data`-style copy, whichever the current algorithm's shape most directly maps to).
|
|
143
143
|
|
|
144
|
-
- [
|
|
144
|
+
- [x] **Step 5: Update string interpolation codegen** to call the rewritten helpers; verify `Str`-typed class/enum fields (now `ref null $StrArray` instead of i32) round-trip correctly through 2b/2d's `struct.get`/`struct.set` and `ref.test`/variant-subtype field lists.
|
|
145
145
|
|
|
146
146
|
#### 2d. Match dispatch via `ref.test`/`br_on_cast`
|
|
147
147
|
|
|
148
|
-
- [
|
|
148
|
+
- [x] **Step 1: Read `compileMatch`, `compileMatchArmsMulti`, `compileCasePositions`, `compileVariantEqArm` (or its current name — it may have been renamed during the camelCase pass), `compileVariantConstructorArm`, and `compileFieldPatterns` in full before changing any of them** — this is the most intricate part of the existing codegen (recursive multi-subject, multi-position, nested-constructor-pattern dispatch) and the part most likely to have subtle behavioral requirements not obvious from a partial read.
|
|
149
149
|
|
|
150
|
-
- [
|
|
150
|
+
- [x] **Step 2: Replace the `HEAP_BASE` range-check + tag-load-and-compare pattern with `ref.test`**
|
|
151
151
|
|
|
152
152
|
Every place that currently does "range-check against `HEAP_BASE`, then conditionally load+compare a tag" (both the top-level bare-Name/Class-pattern arms and the nested `compileFieldPatterns` equivalents) becomes a single `Instruction::RefTestNonNull(HeapType::Concrete(variant_type_idx))` (or `RefTestNullable` if the value can legitimately be null in context — decide per call site) directly on the subject ref, producing the same "did this match" i32 the existing `If`/`Else` structure around it already expects — the surrounding control-flow shape (fall through to `compileMatchArmsMulti(rest, ...)` on mismatch) doesn't need to change, only how the boolean test itself is computed.
|
|
153
153
|
|
|
154
|
-
- [
|
|
154
|
+
- [x] **Step 3: Replace payload-field extraction with `ref.cast` + `struct.get`**
|
|
155
155
|
|
|
156
156
|
Where a matched constructor pattern currently loads a field via `emitLoad(field_vt, offset)` from the raw pointer, first `Instruction::RefCastNonNull(HeapType::Concrete(variant_type_idx))` (narrowing the statically-typed-as-supertype subject to its concrete variant subtype — the match arm's binding scope is exactly where this narrowing is valid) then `Instruction::StructGet { struct_type_index: variant_type_idx, field_index }`.
|
|
157
157
|
|
|
158
|
-
- [
|
|
158
|
+
- [x] **Step 4: Consider (but don't require) using `br_on_cast` for the top-level dispatch chain** — the spec's Components section suggests `ref.test`/`br_on_cast` as the replacement mechanism; a straightforward `ref.test` + `if/else` chain (mirroring today's control-flow shape exactly, per Step 2) is very likely simpler to get right than restructuring into `br_on_cast`'s branch-table-like shape, and is equally correct. Only reach for `br_on_cast` if the `if/else` chain form turns out to hit a real wasm-gc validation or structural limitation — don't restructure working control flow for its own sake.
|
|
159
159
|
|
|
160
160
|
#### 2e. Closures
|
|
161
161
|
|
|
162
|
-
- [
|
|
162
|
+
- [x] **Step 1: Read `compileClosureLiteral`, `compileClosureCall`, `compileClosureBody`, and the `Collector`'s closure-discovery pass in full.**
|
|
163
163
|
|
|
164
|
-
- [
|
|
164
|
+
- [x] **Step 2: Give every closure literal its own env `struct` type**, one field per captured variable in `free_vars` order, field types matching each captured variable's own type (which may itself now be a `ref` type post-2b/2c). Register these in the type registry alongside classes/enums.
|
|
165
165
|
|
|
166
|
-
- [
|
|
166
|
+
- [x] **Step 3: Rewrite `compileClosureLiteral`**: build the env via `struct.new` (pushing each captured local's current value, per Decision 3 — snapshot-by-value semantics are unchanged, only the allocation mechanism changes), then build the 2-field closure struct `{i32 table_index, anyref env}` via `struct.new`, casting/widening the concrete env ref to `anyref` for storage (a supertype widening — confirm whether `wasm-encoder`/wasm-gc requires an explicit instruction for this or whether it's implicit at the type-checking level when a subtype value is used where a supertype/`anyref` is expected; if implicit, no extra instruction is needed).
|
|
167
167
|
|
|
168
|
-
- [
|
|
168
|
+
- [x] **Step 4: Rewrite `compileClosureBody`**: its first instructions must `RefCastNonNull` (or the nullable variant, matching the declared param type) the incoming `anyref` env parameter down to `(ref $ThisClosuresEnvType)` before any captured-field reads, then use `struct.get` for each.
|
|
169
169
|
|
|
170
|
-
- [
|
|
170
|
+
- [x] **Step 5: Update the shared `call_indirect` function-type signatures** (`fnParamTypeToWasmSig`/`ClosureSigKey` and wherever `call_indirect` types are registered/deduped) so the env-pointer parameter position is `ValType::Ref(RefType::ANYREF)` instead of `ValType::I32`, everywhere this shape is constructed.
|
|
171
171
|
|
|
172
172
|
#### 2f. Retire the bump allocator entirely
|
|
173
173
|
|
|
174
|
-
- [
|
|
175
|
-
- [
|
|
174
|
+
- [x] **Step 1: Remove `HEAP_BASE`, `STRING_BASE`, `bump_global` (the field, its threading through `CompileCtx`/`LocalCtx`, and its initialization in `compileSource`), and the `addMemory`/memory-section code in `WasmModule::finish`** once `cargo build -p plum-wasm-codegen` shows nothing references them anymore. If anything still does, that's a sign an earlier 2a–2e step was missed or incomplete — go back, don't leave a partial bump-allocator fallback path.
|
|
175
|
+
- [x] **Step 2: Remove now-dead scratch-slot bookkeeping** (`classcall_scratch` etc., in `Collector` and `LocalCtx`) for any use case fully replaced by direct stack-order `struct.new` construction (per 2b Step 2) — but only what's actually provably dead; `match`'s subject-holding scratch locals are still needed (now typed as `ref null` instead of `i32`), don't remove those.
|
|
176
176
|
|
|
177
177
|
#### 2g. Full verification for Task 2
|
|
178
178
|
|
|
179
|
-
- [
|
|
180
|
-
- [
|
|
181
|
-
- [
|
|
182
|
-
- [
|
|
179
|
+
- [x] **Step 1: `cargo build --workspace --all-targets`** — iterate until clean.
|
|
180
|
+
- [x] **Step 2: Update every existing `codegen_tests.rs`/`examples_test.rs` test that encoded assumptions about the old representation** (e.g. anything asserting raw byte layout, or relying on `HEAP_BASE`-adjacent behavior) — re-verify each still asserts the right OBSERVABLE behavior (a program's computed result), not the old mechanism.
|
|
181
|
+
- [x] **Step 3: `cargo test --workspace`** — expected: 100% pass. This is the real gate for this entire task; do not commit Task 2 before this is green.
|
|
182
|
+
- [x] **Step 4: Commit**
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
185
|
git add plum-wasm-codegen/src plum-wasm-codegen/tests
|
plum-wasm-codegen/Cargo.toml
CHANGED
|
@@ -12,4 +12,4 @@ plum-checker = { path = "../plum-checker" }
|
|
|
12
12
|
wasmparser = "0.220"
|
|
13
13
|
tree-sitter = "0.26"
|
|
14
14
|
tree-sitter-plum = { path = "../tooling/tree-sitter-plum" }
|
|
15
|
-
wasmtime = "
|
|
15
|
+
wasmtime = "47"
|
plum-wasm-codegen/src/lib.rs
CHANGED
|
@@ -9,12 +9,6 @@ use plum_core::ast;
|
|
|
9
9
|
use plum_checker::types::{PlumType, TypeEnv, TypeScheme};
|
|
10
10
|
use plum_checker::{ClassEnv, MethodEnv, EnumVariants, EnumVariantInfo, EnumParams};
|
|
11
11
|
|
|
12
|
-
/// Bump-allocated heap for class instances starts at the second 64KiB page so it can
|
|
13
|
-
/// never collide with the (small, compile-time-sized) string literal data area below it.
|
|
14
|
-
const HEAP_BASE: u32 = 65536;
|
|
15
|
-
/// String literal data (length-prefixed UTF-8 blobs) is laid out from here upward.
|
|
16
|
-
const STRING_BASE: u32 = 8;
|
|
17
|
-
|
|
18
12
|
/// One entry in the module's type section. Wasm's type section is a SINGLE shared
|
|
19
13
|
/// index space for function types AND (once wasm-gc is in play) composite
|
|
20
14
|
/// struct/array types — `Rec` entries occupy as many consecutive indices as they
|
|
@@ -28,6 +22,14 @@ enum TypeEntry {
|
|
|
28
22
|
Rec(Vec<SubType>),
|
|
29
23
|
}
|
|
30
24
|
|
|
25
|
+
/// One entry in the module's data section. Like the type section, data segments
|
|
26
|
+
/// share ONE index space regardless of kind — a `Passive` segment's index (needed by
|
|
27
|
+
/// `array.new_data`) is its position among ALL segments, active or passive.
|
|
28
|
+
enum DataSegmentEntry {
|
|
29
|
+
Active(u32, Vec<u8>),
|
|
30
|
+
Passive(Vec<u8>),
|
|
31
|
+
}
|
|
32
|
+
|
|
31
33
|
pub struct WasmModule {
|
|
32
34
|
types: Vec<TypeEntry>,
|
|
33
35
|
/// Running count of type-section INDICES assigned so far — NOT the same as
|
|
@@ -37,15 +39,15 @@ pub struct WasmModule {
|
|
|
37
39
|
imports: Vec<(String, String, u32)>,
|
|
38
40
|
functions: Vec<(u32, Vec<u8>)>,
|
|
39
41
|
exports: Vec<(String, ExportKind, u32)>,
|
|
40
|
-
memories: Vec<MemoryType>,
|
|
41
42
|
globals: Vec<(ValType, bool, Vec<u8>)>,
|
|
42
|
-
data_segments: Vec<
|
|
43
|
+
data_segments: Vec<DataSegmentEntry>,
|
|
43
44
|
/// Function indices, in table order — the single funcref table used for
|
|
44
45
|
/// closure `call_indirect` dispatch. Index into this vec IS the table index.
|
|
45
46
|
table_elements: Vec<u32>,
|
|
46
47
|
pub func_import_count: u32,
|
|
47
48
|
pub func_count: u32,
|
|
48
49
|
global_count: u32,
|
|
50
|
+
start_function: Option<u32>,
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
impl WasmModule {
|
|
@@ -56,13 +58,13 @@ impl WasmModule {
|
|
|
56
58
|
imports: Vec::new(),
|
|
57
59
|
functions: Vec::new(),
|
|
58
60
|
exports: Vec::new(),
|
|
59
|
-
memories: Vec::new(),
|
|
60
61
|
globals: Vec::new(),
|
|
61
62
|
data_segments: Vec::new(),
|
|
62
63
|
table_elements: Vec::new(),
|
|
63
64
|
func_import_count: 0,
|
|
64
65
|
func_count: 0,
|
|
65
66
|
global_count: 0,
|
|
67
|
+
start_function: None,
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -105,18 +107,6 @@ impl WasmModule {
|
|
|
105
107
|
self.exports.push((name.to_string(), kind, idx));
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
pub fn addMemory(&mut self, min: u64, max: Option<u64>) -> u32 {
|
|
109
|
-
let idx = self.memories.len() as u32;
|
|
110
|
-
self.memories.push(MemoryType {
|
|
111
|
-
minimum: min,
|
|
112
|
-
maximum: max,
|
|
113
|
-
memory64: false,
|
|
114
|
-
shared: false,
|
|
115
|
-
page_size_log2: None,
|
|
116
|
-
});
|
|
117
|
-
idx
|
|
118
|
-
}
|
|
119
|
-
|
|
120
110
|
pub fn addGlobal(&mut self, val_type: ValType, mutable: bool, init: &[u8]) -> u32 {
|
|
121
111
|
let idx = self.global_count;
|
|
122
112
|
self.globals.push((val_type, mutable, init.to_vec()));
|
|
@@ -125,7 +115,27 @@ impl WasmModule {
|
|
|
125
115
|
}
|
|
126
116
|
|
|
127
117
|
pub fn addDataSegment(&mut self, offset: u32, data: &[u8]) {
|
|
128
|
-
self.data_segments.push((offset, data.to_vec()));
|
|
118
|
+
self.data_segments.push(DataSegmentEntry::Active(offset, data.to_vec()));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Adds a passive segment (no implicit memory-init offset) and returns its index
|
|
122
|
+
/// in the shared active/passive data-segment index space — the index `array.new_data`
|
|
123
|
+
/// needs to reference it. Passive segments require a `DataCountSection` (see `finish`).
|
|
124
|
+
pub fn addPassiveDataSegment(&mut self, data: &[u8]) -> u32 {
|
|
125
|
+
let idx = self.data_segments.len() as u32;
|
|
126
|
+
self.data_segments.push(DataSegmentEntry::Passive(data.to_vec()));
|
|
127
|
+
idx
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Registers `func_idx` to run once automatically at instantiation, before any
|
|
131
|
+
/// export is callable — needed because `struct.new` (and therefore constructing
|
|
132
|
+
/// any GC singleton, like the pre-allocated payload-free enum variants) is not
|
|
133
|
+
/// allowed inside a `global`'s own const-expr initializer (confirmed empirically
|
|
134
|
+
/// in Task 1 — `wasmtimeGcConfigAllowsStructNewInGlobalConstExpr` fails), so those
|
|
135
|
+
/// globals are declared `mutable` with a `ref.null` initial value and populated
|
|
136
|
+
/// here instead.
|
|
137
|
+
pub fn setStartFunction(&mut self, func_idx: u32) {
|
|
138
|
+
self.start_function = Some(func_idx);
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
/// Registers `func_idx` as the next slot in the single funcref table used for
|
|
@@ -184,15 +194,6 @@ impl WasmModule {
|
|
|
184
194
|
module.section(&tables);
|
|
185
195
|
}
|
|
186
196
|
|
|
187
|
-
// Memory section
|
|
188
|
-
if !self.memories.is_empty() {
|
|
189
|
-
let mut mem = MemorySection::new();
|
|
190
|
-
for mt in &self.memories {
|
|
191
|
-
mem.memory(*mt);
|
|
192
|
-
}
|
|
193
|
-
module.section(&mem);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
197
|
// Global section
|
|
197
198
|
if !self.globals.is_empty() {
|
|
198
199
|
let mut globals = GlobalSection::new();
|
|
@@ -215,6 +216,11 @@ impl WasmModule {
|
|
|
215
216
|
module.section(&exports);
|
|
216
217
|
}
|
|
217
218
|
|
|
219
|
+
// Start section
|
|
220
|
+
if let Some(func_idx) = self.start_function {
|
|
221
|
+
module.section(&StartSection { function_index: func_idx });
|
|
222
|
+
}
|
|
223
|
+
|
|
218
224
|
// Element section
|
|
219
225
|
if !self.table_elements.is_empty() {
|
|
220
226
|
let mut elements = ElementSection::new();
|
|
@@ -223,6 +229,13 @@ impl WasmModule {
|
|
|
223
229
|
module.section(&elements);
|
|
224
230
|
}
|
|
225
231
|
|
|
232
|
+
// DataCount section — required whenever `array.new_data`/`data.drop` reference
|
|
233
|
+
// a passive segment, and must appear before the code section.
|
|
234
|
+
let has_passive = self.data_segments.iter().any(|e| matches!(e, DataSegmentEntry::Passive(_)));
|
|
235
|
+
if has_passive {
|
|
236
|
+
module.section(&DataCountSection { count: self.data_segments.len() as u32 });
|
|
237
|
+
}
|
|
238
|
+
|
|
226
239
|
// Code section
|
|
227
240
|
if !self.functions.is_empty() {
|
|
228
241
|
let mut code = CodeSection::new();
|
|
@@ -235,9 +248,16 @@ impl WasmModule {
|
|
|
235
248
|
// Data section
|
|
236
249
|
if !self.data_segments.is_empty() {
|
|
237
250
|
let mut data = DataSection::new();
|
|
238
|
-
for
|
|
251
|
+
for entry in &self.data_segments {
|
|
252
|
+
match entry {
|
|
253
|
+
DataSegmentEntry::Active(offset, bytes) => {
|
|
239
|
-
|
|
254
|
+
let offset_expr = ConstExpr::i32_const(*offset as i32);
|
|
240
|
-
|
|
255
|
+
data.active(0, &offset_expr, bytes.iter().copied());
|
|
256
|
+
}
|
|
257
|
+
DataSegmentEntry::Passive(bytes) => {
|
|
258
|
+
data.passive(bytes.iter().copied());
|
|
259
|
+
}
|
|
260
|
+
}
|
|
241
261
|
}
|
|
242
262
|
module.section(&data);
|
|
243
263
|
}
|
|
@@ -260,15 +280,20 @@ pub struct FuncSig {
|
|
|
260
280
|
|
|
261
281
|
/// Everything codegen needs to know about one closure *literal* found in the program.
|
|
262
282
|
/// wasm has no native closures: each literal `|v| body` becomes its own real wasm
|
|
263
|
-
/// function (registered in the funcref table), and a closure *value* is a
|
|
283
|
+
/// function (registered in the funcref table), and a closure *value* is a `ref` to
|
|
264
|
-
///
|
|
284
|
+
/// the shared `{table_idx: i32, env: anyref}` struct (`GcTypeRegistry::closure_type_idx`).
|
|
285
|
+
/// `env` is a `ref.cast` of this closure literal's OWN env struct type
|
|
265
|
-
///
|
|
286
|
+
/// (`env_type_idx`), one field per captured (free) variable in `free_vars` order.
|
|
266
|
-
/// `free_vars` order.
|
|
267
287
|
pub struct ClosureInfo {
|
|
268
288
|
/// Reserved wasm function index for this closure's compiled body.
|
|
269
289
|
pub func_idx: u32,
|
|
270
|
-
/// Index of `func_idx` in the funcref table (the `i32` stored
|
|
290
|
+
/// Index of `func_idx` in the funcref table (the `i32` stored in the closure
|
|
291
|
+
/// struct's `table_idx` field).
|
|
271
292
|
pub table_idx: u32,
|
|
293
|
+
/// This closure literal's own env struct type index (fields = `free_vars`, in
|
|
294
|
+
/// order) — assigned once all closures are discovered, alongside every other
|
|
295
|
+
/// closure's env type and the shared closure-value struct, in one `rec` group.
|
|
296
|
+
pub env_type_idx: u32,
|
|
272
297
|
/// Closure param val types (NOT including the implicit leading env pointer).
|
|
273
298
|
pub param_vts: Vec<ValType>,
|
|
274
299
|
/// Closure param plum types (for the closure body's own type env).
|
|
@@ -276,7 +301,7 @@ pub struct ClosureInfo {
|
|
|
276
301
|
/// Closure return val type (`None` for a `Unit`-returning closure).
|
|
277
302
|
pub ret_vt: Option<ValType>,
|
|
278
303
|
/// Free variables captured by value, in a stable (first-appearance) order; the
|
|
279
|
-
/// index into this vec IS the variable's
|
|
304
|
+
/// index into this vec IS the variable's field index in the env struct.
|
|
280
305
|
pub free_vars: Vec<(String, PlumType)>,
|
|
281
306
|
}
|
|
282
307
|
|
|
@@ -293,7 +318,6 @@ pub struct CompileCtx<'a> {
|
|
|
293
318
|
pub enum_variants: EnumVariants,
|
|
294
319
|
pub enum_params: EnumParams,
|
|
295
320
|
pub global_env: TypeEnv,
|
|
296
|
-
pub bump_global: u32,
|
|
297
321
|
/// Closure literal (keyed by `&Expr::Closure` pointer identity) -> its `ClosureInfo`.
|
|
298
322
|
pub closures: HashMap<usize, ClosureInfo>,
|
|
299
323
|
/// The AST of each discovered closure literal, keyed the same way, so its body can
|
|
@@ -301,48 +325,42 @@ pub struct CompileCtx<'a> {
|
|
|
301
325
|
pub closure_asts: HashMap<usize, &'a ast::Closure>,
|
|
302
326
|
/// Closure wasm signature -> function-type index, for `call_indirect` at call sites.
|
|
303
327
|
pub closure_call_types: HashMap<ClosureSigKey, u32>,
|
|
304
|
-
/// Top-level function name ->
|
|
328
|
+
/// Top-level function name -> global index holding its zero-capture "trampoline"
|
|
305
|
-
/// closure struct `{table_idx,
|
|
329
|
+
/// closure struct `{table_idx, env=null}`, for using a plain named function
|
|
306
330
|
/// wherever a `fn(...)`-typed value is expected (e.g. `each(double)`). Since the
|
|
307
|
-
/// struct has no captures it never changes, so it's
|
|
331
|
+
/// struct has no captures it never changes, so it's built once by the shared
|
|
308
|
-
///
|
|
332
|
+
/// `start` function instead of being reconstructed per reference.
|
|
309
333
|
pub named_fn_values: HashMap<String, u32>,
|
|
310
|
-
/// Shared runtime helper `(a:
|
|
334
|
+
/// Shared runtime helper `(a: ref Str, b: ref Str) -> ref Str`: allocates a new
|
|
311
|
-
///
|
|
335
|
+
/// `array<i8>` exactly long enough to hold `a`'s bytes followed by `b`'s, for
|
|
312
|
-
///
|
|
336
|
+
/// lowering string interpolation (`"{expr}"`).
|
|
313
337
|
pub string_concat_func: u32,
|
|
314
|
-
/// Shared runtime helper `(n: i64) ->
|
|
338
|
+
/// Shared runtime helper `(n: i64) -> ref Str`: allocates a new `array<i8>`
|
|
315
|
-
///
|
|
339
|
+
/// holding `n`'s decimal representation, for interpolating an `Int`.
|
|
316
340
|
pub int_to_string_func: u32,
|
|
317
|
-
/// wasm-gc type-section indices for this program's classes/enums/Str
|
|
341
|
+
/// wasm-gc type-section indices for this program's classes/enums/Str/closures —
|
|
318
|
-
/// docs/superpowers/plans/2026-07-25-wasm-gc-migration.md)
|
|
342
|
+
/// every value's real representation (see `docs/superpowers/plans/2026-07-25-wasm-gc-migration.md`).
|
|
319
|
-
/// yet consumed by any codegen below; the bump allocator remains the real
|
|
320
|
-
/// representation until Task 2 lands.
|
|
321
343
|
pub gc_types: GcTypeRegistry,
|
|
344
|
+
/// Payload-free variant name (True/False/None/...) -> the global index holding
|
|
345
|
+
/// its one pre-allocated instance (see this migration plan's Decision 2).
|
|
346
|
+
pub singleton_globals: HashMap<String, u32>,
|
|
322
347
|
}
|
|
323
348
|
|
|
324
|
-
/// Per-module state that accumulates as function bodies are compiled:
|
|
349
|
+
/// Per-module state that accumulates as function bodies are compiled: every static
|
|
350
|
+
/// string literal's bytes, staged here (rather than added straight to `WasmModule`)
|
|
351
|
+
/// so `compileStaticString` can know a segment's final passive-data-section index —
|
|
325
|
-
///
|
|
352
|
+
/// its position among ALL staged segments — before that section is actually
|
|
353
|
+
/// assembled at the end of `compileSource`.
|
|
326
354
|
struct ModuleState {
|
|
327
|
-
next_string_offset: u32,
|
|
328
|
-
|
|
355
|
+
passive_segments: Vec<Vec<u8>>,
|
|
329
356
|
}
|
|
330
357
|
|
|
331
358
|
struct LocalCtx<'a> {
|
|
332
359
|
locals: HashMap<String, u32>,
|
|
333
|
-
/// First local index reserved for `ClassCall` scratch temporaries.
|
|
334
|
-
classcall_scratch_base: u32,
|
|
335
|
-
/// `ClassCall` expr identity (pointer address) -> scratch slot offset.
|
|
336
|
-
classcall_scratch: HashMap<usize, u32>,
|
|
337
360
|
/// First local index reserved for `match` subject scratch temporaries.
|
|
338
361
|
match_scratch_base: u32,
|
|
339
362
|
/// `Match` stmt identity (pointer address) -> scratch slot offset.
|
|
340
363
|
match_scratch_index: HashMap<usize, u32>,
|
|
341
|
-
/// First local index reserved for closure-construction scratch temporaries; each
|
|
342
|
-
/// closure literal uses two consecutive slots (env struct ptr, closure struct ptr).
|
|
343
|
-
closure_scratch_base: u32,
|
|
344
|
-
/// `Closure` expr identity (pointer address) -> offset of its first (of two) slots.
|
|
345
|
-
closure_scratch: HashMap<usize, u32>,
|
|
346
364
|
/// First local index reserved for nested-constructor-pattern scratch temporaries
|
|
347
365
|
/// (`Some(Some(v))`'s inner `Some(v)`); the outermost pattern uses a
|
|
348
366
|
/// `match_scratch` slot instead, so this only covers depth >= 1.
|
|
@@ -366,6 +384,8 @@ struct LocalCtx<'a> {
|
|
|
366
384
|
methods: &'a MethodEnv,
|
|
367
385
|
enum_variants: &'a EnumVariants,
|
|
368
386
|
enum_params: &'a EnumParams,
|
|
387
|
+
gc_types: &'a GcTypeRegistry,
|
|
388
|
+
singleton_globals: &'a HashMap<String, u32>,
|
|
369
389
|
/// Tracks each binding's inferred type as compilation proceeds through
|
|
370
390
|
/// statements in order, mirroring `plum-checker`'s own env evolution — needed
|
|
371
391
|
/// to resolve `Attribute`/`ClassCall` targets and pick the right load/store width.
|
|
@@ -379,7 +399,6 @@ struct LocalCtx<'a> {
|
|
|
379
399
|
/// and would fall back to its old TVar-defaults-to-Int behavior, disagreeing
|
|
380
400
|
/// with the (now correct) signature the closure's body was actually compiled with.
|
|
381
401
|
closure_local_sigs: RefCell<HashMap<String, ClosureSigKey>>,
|
|
382
|
-
bump_global: u32,
|
|
383
402
|
}
|
|
384
403
|
|
|
385
404
|
fn fnKey(f: &ast::Fn) -> String {
|
|
@@ -399,36 +418,77 @@ fn paramTypeName(pt: &ast::ParamType) -> &str {
|
|
|
399
418
|
}
|
|
400
419
|
}
|
|
401
420
|
|
|
421
|
+
thread_local! {
|
|
422
|
+
/// The current `compileSource` call's wasm-gc type registry. `compileSource` is
|
|
423
|
+
/// the sole entry point and is never re-entrant/concurrent within one thread, so
|
|
424
|
+
/// a thread-local avoids threading an explicit `&GcTypeRegistry` parameter through
|
|
425
|
+
/// every one of `plumTypeToValtype`/`astTypeToWasm`'s ~25 call sites (many several
|
|
426
|
+
/// functions removed from anywhere a `CompileCtx`/`LocalCtx` is in scope, e.g.
|
|
427
|
+
/// function-signature registration that runs before any `LocalCtx` exists). Set
|
|
428
|
+
/// once near the top of `compileSource`, before anything below reads it.
|
|
429
|
+
static CURRENT_GC_TYPES: RefCell<Option<GcTypeRegistry>> = const { RefCell::new(None) };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
fn withGcTypes<R>(f: impl FnOnce(&GcTypeRegistry) -> R) -> R {
|
|
433
|
+
CURRENT_GC_TYPES.with(|c| {
|
|
434
|
+
let borrow = c.borrow();
|
|
435
|
+
let registry = borrow.as_ref().expect("internal codegen error: GC type registry read before compileSource initialized it");
|
|
436
|
+
f(registry)
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/// Resolves an `ast::Type`/`ast::ParamType`'s bare name (e.g. from a function
|
|
441
|
+
/// signature, before any `PlumType`/checker involvement) to its wasm-gc `ValType`.
|
|
402
442
|
fn astTypeToWasm(name: &str) -> Option<ValType> {
|
|
403
443
|
match name {
|
|
404
444
|
"Int" => Some(ValType::I64),
|
|
405
445
|
"Float" => Some(ValType::F64),
|
|
406
|
-
"Bool" => Some(ValType::I32),
|
|
407
|
-
"Str" => Some(ValType::I32),
|
|
408
446
|
"Unit" => None,
|
|
447
|
+
"Bool" => Some(plumTypeToValtype(&PlumType::TBool)),
|
|
448
|
+
"Str" => Some(plumTypeToValtype(&PlumType::TStr)),
|
|
409
|
-
|
|
449
|
+
other => Some(plumTypeToValtype(&PlumType::TNamed(other.to_string()))),
|
|
410
450
|
}
|
|
411
451
|
}
|
|
412
452
|
|
|
453
|
+
/// A `ref null $Ty` — every heap value (class instance, enum/Bool variant, Str
|
|
454
|
+
/// array, closure struct) is nullable-by-convention, matching how a bump-allocator
|
|
455
|
+
/// i32 pointer could be "null" (0) too; nothing in this codegen currently relies on
|
|
456
|
+
/// non-nullable refs for an optimization, so nullable everywhere keeps this simple.
|
|
457
|
+
fn gcRef(idx: u32) -> ValType {
|
|
458
|
+
ValType::Ref(RefType { nullable: true, heap_type: HeapType::Concrete(idx) })
|
|
459
|
+
}
|
|
460
|
+
|
|
413
461
|
fn plumTypeToValtype(t: &PlumType) -> ValType {
|
|
414
462
|
match t {
|
|
415
463
|
PlumType::TInt => ValType::I64,
|
|
416
464
|
PlumType::TFloat => ValType::F64,
|
|
465
|
+
PlumType::TBool => withGcTypes(|r| gcRef(*r.enum_super_type_idx.get("Bool").expect("Bool must be registered"))),
|
|
466
|
+
PlumType::TStr => withGcTypes(|r| gcRef(r.str_type_idx)),
|
|
467
|
+
PlumType::TNamed(name) => withGcTypes(|r| {
|
|
468
|
+
match r.class_type_idx.get(name).or_else(|| r.enum_super_type_idx.get(name)) {
|
|
469
|
+
Some(idx) => gcRef(*idx),
|
|
470
|
+
// Genuinely unmodeled type name (not a real class/enum) — permissive
|
|
471
|
+
// fallback, matching this function's pre-wasm-gc "unmodeled type:
|
|
472
|
+
// pointer" behavior; codegen sites that actually need a concrete
|
|
473
|
+
// struct type still resolve it themselves and error clearly if absent.
|
|
474
|
+
None => ValType::Ref(RefType::ANYREF),
|
|
475
|
+
}
|
|
476
|
+
}),
|
|
417
|
-
// A closure value is a
|
|
477
|
+
// A closure value is a 2-field `{table_index: i32, env: anyref}` struct —
|
|
478
|
+
// its OWN concrete GC type, registered per Task 2e (closures), not through
|
|
418
|
-
//
|
|
479
|
+
// this general resolver; `anyref` here is a safe placeholder used only
|
|
480
|
+
// where a closure's exact struct type isn't being constructed/destructured
|
|
481
|
+
// directly (e.g. deciding a local's storage class), matching `TVariadic`'s
|
|
419
|
-
//
|
|
482
|
+
// existing "not modeled as a concrete shape here" treatment.
|
|
420
|
-
PlumType::
|
|
483
|
+
PlumType::TFun(_, _) | PlumType::TVariadic(_) => ValType::Ref(RefType::ANYREF),
|
|
421
484
|
PlumType::TVar(_) | PlumType::TUnit => ValType::I64,
|
|
422
485
|
}
|
|
423
486
|
}
|
|
424
487
|
|
|
425
488
|
/// Type-section indices for every wasm-gc composite type this program's monomorphized
|
|
426
489
|
/// classes/enums/`Str` need. Populated once in `compileSource` from the checker's
|
|
427
|
-
/// global tables. Per Task 1 of docs/superpowers/plans/2026-07-25-wasm-gc-migration.md,
|
|
428
|
-
/// this is currently populated but NOT YET consumed by construction/field-access/match
|
|
429
|
-
/// codegen (that's Task 2) — its job right now is only to prove the type section itself
|
|
430
|
-
/// is well-formed under the new representation, alongside the still-untouched
|
|
431
|
-
///
|
|
490
|
+
/// global tables.
|
|
491
|
+
#[derive(Clone)]
|
|
432
492
|
pub struct GcTypeRegistry {
|
|
433
493
|
/// Concrete class/struct name -> its one wasm-gc `struct` type index.
|
|
434
494
|
pub class_type_idx: HashMap<String, u32>,
|
|
@@ -442,6 +502,16 @@ pub struct GcTypeRegistry {
|
|
|
442
502
|
pub variant_type_idx: HashMap<String, u32>,
|
|
443
503
|
/// The single shared `array<i8>` type index every `Str` value uses.
|
|
444
504
|
pub str_type_idx: u32,
|
|
505
|
+
/// The single shared closure-value struct type index — `{table_idx: i32, env:
|
|
506
|
+
/// anyref}` — every closure (and zero-capture "trampoline") uses regardless of
|
|
507
|
+
/// its own captures; only assigned once closures are discovered (see
|
|
508
|
+
/// `compileSource`), so `0` until then (nothing reads it earlier).
|
|
509
|
+
pub closure_type_idx: u32,
|
|
510
|
+
/// Wasm value type -> the shared `array<T>` type index used to pass a `...T`
|
|
511
|
+
/// variadic argument pack as one GC array (replacing the old bump-allocated
|
|
512
|
+
/// `[count][elem...]` blob — `array.len` replaces the explicit count). Populated
|
|
513
|
+
/// alongside `closure_type_idx`, from every `ParamType::Variadic` in the program.
|
|
514
|
+
pub variadic_array_type_idx: HashMap<ValType, u32>,
|
|
445
515
|
}
|
|
446
516
|
|
|
447
517
|
/// Resolves a plum type to the wasm-gc `ValType` its values are represented as, given
|
|
@@ -484,11 +554,12 @@ fn buildGcTypeRegistry(
|
|
|
484
554
|
source: &ast::Source,
|
|
485
555
|
classes: &ClassEnv,
|
|
486
556
|
enum_variants: &EnumVariants,
|
|
557
|
+
enum_params: &EnumParams,
|
|
487
558
|
) -> GcTypeRegistry {
|
|
488
559
|
enum Slot {
|
|
489
560
|
Str,
|
|
490
561
|
Class(String),
|
|
491
|
-
EnumSuper,
|
|
562
|
+
EnumSuper(String),
|
|
492
563
|
Variant(String),
|
|
493
564
|
}
|
|
494
565
|
|
|
@@ -519,7 +590,7 @@ fn buildGcTypeRegistry(
|
|
|
519
590
|
}
|
|
520
591
|
for (enum_name, variant_names) in &enum_decls {
|
|
521
592
|
enum_super_type_idx.insert(enum_name.clone(), slots.len() as u32);
|
|
522
|
-
slots.push(Slot::EnumSuper);
|
|
593
|
+
slots.push(Slot::EnumSuper(enum_name.clone()));
|
|
523
594
|
for vname in variant_names {
|
|
524
595
|
variant_type_idx.insert(vname.clone(), slots.len() as u32);
|
|
525
596
|
slots.push(Slot::Variant(vname.clone()));
|
|
@@ -534,6 +605,8 @@ fn buildGcTypeRegistry(
|
|
|
534
605
|
enum_super_type_idx,
|
|
535
606
|
variant_type_idx,
|
|
536
607
|
str_type_idx: 0,
|
|
608
|
+
closure_type_idx: 0,
|
|
609
|
+
variadic_array_type_idx: HashMap::new(),
|
|
537
610
|
};
|
|
538
611
|
|
|
539
612
|
// Pass 2: build the real SubType for every slot, now that every cross-reference
|
|
@@ -559,16 +632,36 @@ fn buildGcTypeRegistry(
|
|
|
559
632
|
composite_type: CompositeType { inner: CompositeInnerType::Struct(StructType { fields: field_types.into() }), shared: false },
|
|
560
633
|
}
|
|
561
634
|
}
|
|
635
|
+
// An ORDINARY enum's supertype declares zero fields (each variant adds its
|
|
636
|
+
// own distinct payload fields below it). A DISCRIMINANT enum (`enum
|
|
637
|
+
// Foo(n: Int) = ...`) is different: every variant shares the EXACT SAME
|
|
638
|
+
// field list (`plum-checker::buildGlobalTables` already gives every variant
|
|
639
|
+
// of such an enum identical `field_types`, equal to the shared params), so
|
|
640
|
+
// the supertype declares those fields directly — this is what lets `self.n`
|
|
641
|
+
// field access work on the plain supertype-typed reference with a
|
|
642
|
+
// `struct.get`, no `ref.cast` to one arbitrary variant required (which would
|
|
643
|
+
// trap at runtime whenever `self` isn't actually THAT variant).
|
|
562
|
-
Slot::EnumSuper =>
|
|
644
|
+
Slot::EnumSuper(enum_name) => {
|
|
645
|
+
let params = enum_params.get(enum_name).cloned().unwrap_or_default();
|
|
646
|
+
let field_types: Vec<FieldType> = params.iter().map(|(_, ty)| FieldType {
|
|
647
|
+
element_type: StorageType::Val(plumTypeToGcValtype(ty, ®istry)),
|
|
648
|
+
mutable: false,
|
|
649
|
+
}).collect();
|
|
650
|
+
SubType {
|
|
563
|
-
|
|
651
|
+
is_final: false,
|
|
564
|
-
|
|
652
|
+
supertype_idx: None,
|
|
565
|
-
|
|
653
|
+
composite_type: CompositeType { inner: CompositeInnerType::Struct(StructType { fields: field_types.into() }), shared: false },
|
|
566
|
-
|
|
654
|
+
}
|
|
655
|
+
}
|
|
567
656
|
Slot::Variant(vname) => {
|
|
568
657
|
let info = enum_variants.get(vname)
|
|
569
658
|
.unwrap_or_else(|| panic!("internal codegen error: variant '{}' missing from EnumVariants", vname));
|
|
570
659
|
let super_idx = *registry.enum_super_type_idx.get(&info.enum_name)
|
|
571
660
|
.unwrap_or_else(|| panic!("internal codegen error: enum '{}' missing its supertype slot", info.enum_name));
|
|
661
|
+
// For a discriminant enum, `info.field_types` is ALREADY identical to the
|
|
662
|
+
// supertype's own fields (see the `EnumSuper` arm above) — a variant with
|
|
663
|
+
// zero ADDED fields beyond its supertype is still valid wasm-gc
|
|
664
|
+
// subtyping, so no special-casing is needed here.
|
|
572
665
|
let field_types: Vec<FieldType> = info.field_types.iter().map(|ty| FieldType {
|
|
573
666
|
element_type: StorageType::Val(plumTypeToGcValtype(ty, ®istry)),
|
|
574
667
|
mutable: false,
|
|
@@ -587,10 +680,10 @@ fn buildGcTypeRegistry(
|
|
|
587
680
|
}
|
|
588
681
|
|
|
589
682
|
/// Maps an `ast::ParamType::Fn(params, ret)` to the wasm signature of the *closure
|
|
590
|
-
/// function* it compiles to: an implicit leading `
|
|
683
|
+
/// function* it compiles to: an implicit leading `env: anyref`, then one param per
|
|
591
684
|
/// declared param type, returning `ret`'s val type (or nothing for `Unit`).
|
|
592
685
|
fn fnParamTypeToWasmSig(params: &[ast::Type], ret: &Option<Box<ast::Type>>) -> (Vec<ValType>, Option<ValType>) {
|
|
593
|
-
let mut vts = vec![ValType::
|
|
686
|
+
let mut vts = vec![ValType::Ref(RefType::ANYREF)]; // env pointer
|
|
594
687
|
for p in params {
|
|
595
688
|
vts.push(astTypeToWasm(&p.name).unwrap_or(ValType::I32));
|
|
596
689
|
}
|
|
@@ -641,17 +734,71 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
641
734
|
let (global_env, classes, methods, enum_variants, enum_params) = plum_checker::buildGlobalTables(source);
|
|
642
735
|
|
|
643
736
|
let mut module = WasmModule::new();
|
|
737
|
+
|
|
738
|
+
let mut gc_types = buildGcTypeRegistry(&mut module, source, &classes, &enum_variants, &enum_params);
|
|
739
|
+
CURRENT_GC_TYPES.with(|c| *c.borrow_mut() = Some(gc_types.clone()));
|
|
740
|
+
|
|
741
|
+
// Register one shared `array<T>` GC type per distinct wasm value type used by a
|
|
742
|
+
// `...T` variadic parameter anywhere in the program (almost always just one, e.g.
|
|
743
|
+
// `Int...`) — replaces the old bump-allocated `[count][elem...]` blob (`array.len`
|
|
744
|
+
// replaces the explicit count word). Must happen before function signatures are
|
|
745
|
+
// registered below, since a variadic param's wasm type is this array's `ref`.
|
|
746
|
+
let mut variadic_elem_vts: Vec<ValType> = Vec::new();
|
|
747
|
+
for item in &source.items {
|
|
748
|
+
if let ast::Item::Fn(f) = item {
|
|
749
|
+
for p in &f.params {
|
|
750
|
+
if let ast::ParamType::Variadic(t) = &p.ty {
|
|
751
|
+
let vt = astTypeToWasm(&t.name).unwrap_or(ValType::I64);
|
|
752
|
+
if !variadic_elem_vts.contains(&vt) {
|
|
753
|
+
variadic_elem_vts.push(vt);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
let variadic_array_subtypes: Vec<SubType> = variadic_elem_vts.iter().map(|vt| SubType {
|
|
760
|
+
is_final: true,
|
|
644
|
-
|
|
761
|
+
supertype_idx: None,
|
|
762
|
+
composite_type: CompositeType {
|
|
763
|
+
inner: CompositeInnerType::Array(ArrayType(FieldType { element_type: StorageType::Val(*vt), mutable: false })),
|
|
764
|
+
shared: false,
|
|
765
|
+
},
|
|
766
|
+
}).collect();
|
|
767
|
+
if !variadic_array_subtypes.is_empty() {
|
|
768
|
+
let indices = module.addGcTypes(variadic_array_subtypes);
|
|
769
|
+
for (vt, idx) in variadic_elem_vts.iter().zip(indices) {
|
|
770
|
+
gc_types.variadic_array_type_idx.insert(*vt, idx);
|
|
771
|
+
}
|
|
645
|
-
|
|
772
|
+
CURRENT_GC_TYPES.with(|c| *c.borrow_mut() = Some(gc_types.clone()));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Pre-allocate one instance of every payload-free variant (True/False/None/...)
|
|
776
|
+
// as a global, populated once by a `start` function rather than reconstructed on
|
|
777
|
+
// every reference — see this migration plan's Decision 2. `struct.new` isn't
|
|
778
|
+
// allowed inside a global's own const-expr initializer (confirmed empirically in
|
|
779
|
+
// Task 1), so each global starts `ref.null` and a `start` function fills it in
|
|
780
|
+
// before any export is callable. The zero-capture "trampoline" closures
|
|
781
|
+
// registered below (once closures are discovered) append to this SAME start
|
|
782
|
+
// function, so its body isn't finalized/patched into the module until then.
|
|
783
|
+
let mut singleton_globals: HashMap<String, u32> = HashMap::new();
|
|
784
|
+
let mut start_body = Vec::new();
|
|
785
|
+
for (name, info) in &enum_variants {
|
|
786
|
+
if !info.field_types.is_empty() {
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
let variant_idx = *gc_types.variant_type_idx.get(name)
|
|
790
|
+
.unwrap_or_else(|| panic!("internal codegen error: payload-free variant '{}' missing from GC type registry", name));
|
|
646
|
-
|
|
791
|
+
let mut init = Vec::new();
|
|
647
|
-
|
|
792
|
+
Instruction::RefNull(HeapType::Concrete(variant_idx)).encode(&mut init);
|
|
648
|
-
|
|
793
|
+
let global_idx = module.addGlobal(gcRef(variant_idx), true, &init);
|
|
649
|
-
|
|
794
|
+
singleton_globals.insert(name.clone(), global_idx);
|
|
795
|
+
|
|
650
|
-
|
|
796
|
+
Instruction::StructNewDefault(variant_idx).encode(&mut start_body);
|
|
651
|
-
// every class/enum/Str up front. Purely additive right now — nothing below reads
|
|
652
|
-
|
|
797
|
+
Instruction::GlobalSet(global_idx).encode(&mut start_body);
|
|
653
|
-
|
|
798
|
+
}
|
|
654
|
-
let
|
|
799
|
+
let start_type_idx = module.addType(&[], &[]);
|
|
800
|
+
let start_func_idx = module.addFunction(start_type_idx, &[]);
|
|
801
|
+
module.setStartFunction(start_func_idx);
|
|
655
802
|
|
|
656
803
|
let mut func_ids: HashMap<String, u32> = HashMap::new();
|
|
657
804
|
let mut func_sigs: HashMap<String, FuncSig> = HashMap::new();
|
|
@@ -664,12 +811,17 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
664
811
|
for item in &source.items {
|
|
665
812
|
if let ast::Item::Fn(f) = item {
|
|
666
813
|
let mut param_types: Vec<ValType> = Vec::new();
|
|
667
|
-
if f.type_param
|
|
814
|
+
if let Some(recv) = &f.type_param {
|
|
668
|
-
param_types.push(ValType::I32);
|
|
815
|
+
param_types.push(astTypeToWasm(recv).unwrap_or(ValType::I32));
|
|
669
816
|
}
|
|
670
817
|
for p in &f.params {
|
|
671
818
|
let vt = match &p.ty {
|
|
672
|
-
ast::ParamType::Variadic(
|
|
819
|
+
ast::ParamType::Variadic(t) => {
|
|
820
|
+
let elem_vt = astTypeToWasm(&t.name).unwrap_or(ValType::I64);
|
|
821
|
+
let arr_idx = *gc_types.variadic_array_type_idx.get(&elem_vt)
|
|
822
|
+
.expect("internal codegen error: variadic array type must be pre-registered for every elem type in the program");
|
|
823
|
+
gcRef(arr_idx)
|
|
824
|
+
}
|
|
673
825
|
other => astTypeToWasm(paramTypeName(other)).unwrap_or(ValType::I32),
|
|
674
826
|
};
|
|
675
827
|
param_types.push(vt);
|
|
@@ -737,10 +889,43 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
737
889
|
named_fn_refs.extend(walker.named_fn_refs);
|
|
738
890
|
}
|
|
739
891
|
|
|
892
|
+
// Register every closure literal's own env struct type, plus the ONE shared
|
|
893
|
+
// closure-value struct `{table_idx: i32, env: anyref}` every closure (and
|
|
894
|
+
// trampoline, below) wraps its env in — declared together as a single `rec`
|
|
895
|
+
// group now that every closure's free-variable types are known. The shared
|
|
896
|
+
// struct is always slot 0; closure `i`'s env type is slot `i + 1`.
|
|
897
|
+
let mut closure_subtypes: Vec<SubType> = vec![SubType {
|
|
898
|
+
is_final: true,
|
|
899
|
+
supertype_idx: None,
|
|
900
|
+
composite_type: CompositeType {
|
|
901
|
+
inner: CompositeInnerType::Struct(StructType {
|
|
902
|
+
fields: vec![
|
|
903
|
+
FieldType { element_type: StorageType::Val(ValType::I32), mutable: false },
|
|
904
|
+
FieldType { element_type: StorageType::Val(ValType::Ref(RefType::ANYREF)), mutable: false },
|
|
905
|
+
].into(),
|
|
906
|
+
}),
|
|
907
|
+
shared: false,
|
|
908
|
+
},
|
|
909
|
+
}];
|
|
910
|
+
for rc in &raw_closures {
|
|
911
|
+
let field_types: Vec<FieldType> = rc.free_vars.iter().map(|(_, ty)| FieldType {
|
|
912
|
+
element_type: StorageType::Val(plumTypeToValtype(ty)),
|
|
913
|
+
mutable: false,
|
|
914
|
+
}).collect();
|
|
915
|
+
closure_subtypes.push(SubType {
|
|
916
|
+
is_final: true,
|
|
917
|
+
supertype_idx: None,
|
|
918
|
+
composite_type: CompositeType { inner: CompositeInnerType::Struct(StructType { fields: field_types.into() }), shared: false },
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
let closure_type_indices = module.addGcTypes(closure_subtypes);
|
|
922
|
+
gc_types.closure_type_idx = closure_type_indices[0];
|
|
923
|
+
CURRENT_GC_TYPES.with(|c| *c.borrow_mut() = Some(gc_types.clone()));
|
|
924
|
+
|
|
740
925
|
let mut closures: HashMap<usize, ClosureInfo> = HashMap::new();
|
|
741
926
|
let mut closure_asts: HashMap<usize, &ast::Closure> = HashMap::new();
|
|
742
|
-
for rc in
|
|
927
|
+
for (i, rc) in raw_closures.iter().enumerate() {
|
|
743
|
-
let mut sig_params = vec![ValType::
|
|
928
|
+
let mut sig_params = vec![ValType::Ref(RefType::ANYREF)]; // env pointer
|
|
744
929
|
sig_params.extend(rc.param_vts.iter().copied());
|
|
745
930
|
let sig_key: ClosureSigKey = (sig_params.clone(), rc.ret_vt);
|
|
746
931
|
let type_idx = match closure_call_types.get(&sig_key) {
|
|
@@ -757,6 +942,7 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
757
942
|
closures.insert(rc.ptr, ClosureInfo {
|
|
758
943
|
func_idx,
|
|
759
944
|
table_idx,
|
|
945
|
+
env_type_idx: closure_type_indices[i + 1],
|
|
760
946
|
param_vts: rc.param_vts.clone(),
|
|
761
947
|
param_ptypes: rc.param_ptypes.clone(),
|
|
762
948
|
ret_vt: rc.ret_vt,
|
|
@@ -767,19 +953,17 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
767
953
|
|
|
768
954
|
// Register a zero-capture "trampoline" closure for every top-level function
|
|
769
955
|
// referenced as a bare value (e.g. `each(double)`): a real wasm function with the
|
|
770
|
-
// closure calling convention `(
|
|
956
|
+
// closure calling convention `(env, ...real_params) -> ret` that ignores its env
|
|
771
|
-
//
|
|
957
|
+
// and forwards straight to the real function, plus a funcref-table entry for it.
|
|
772
|
-
//
|
|
958
|
+
// Since it never captures anything, its `{table_idx, env=null}` closure struct is
|
|
773
|
-
//
|
|
959
|
+
// a compile-time constant — like every payload-free enum variant, built once by
|
|
774
|
-
//
|
|
960
|
+
// the shared `start` function and stored in its own global.
|
|
775
961
|
let mut named_fn_values: HashMap<String, u32> = HashMap::new();
|
|
776
|
-
let mut named_fn_data_segments: Vec<(u32, Vec<u8>)> = Vec::new();
|
|
777
|
-
let mut next_static_offset = STRING_BASE;
|
|
778
962
|
for name in &named_fn_refs {
|
|
779
963
|
let sig = func_sigs.get(name).expect("named fn ref must be a registered top-level function");
|
|
780
964
|
let real_func_idx = *func_ids.get(name).expect("named fn ref must be a registered top-level function");
|
|
781
965
|
|
|
782
|
-
let mut sig_params = vec![ValType::
|
|
966
|
+
let mut sig_params = vec![ValType::Ref(RefType::ANYREF)]; // env pointer
|
|
783
967
|
sig_params.extend(sig.params.iter().copied());
|
|
784
968
|
let sig_key: ClosureSigKey = (sig_params.clone(), sig.ret);
|
|
785
969
|
let type_idx = match closure_call_types.get(&sig_key) {
|
|
@@ -795,34 +979,47 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
795
979
|
let mut tbody = Vec::new();
|
|
796
980
|
tbody.push(0u8); // no locals beyond the params already in the signature
|
|
797
981
|
for i in 0..sig.params.len() {
|
|
798
|
-
Instruction::LocalGet((i + 1) as u32).encode(&mut tbody); // local 0 is
|
|
982
|
+
Instruction::LocalGet((i + 1) as u32).encode(&mut tbody); // local 0 is env, ignored
|
|
799
983
|
}
|
|
800
984
|
Instruction::Call(real_func_idx).encode(&mut tbody);
|
|
801
985
|
Instruction::End.encode(&mut tbody);
|
|
802
986
|
let func_idx = module.addFunction(type_idx, &tbody);
|
|
803
987
|
let table_idx = module.addTableElement(func_idx);
|
|
804
988
|
|
|
805
|
-
let offset = next_static_offset;
|
|
806
|
-
next_static_offset += 8;
|
|
807
|
-
let mut
|
|
989
|
+
let mut init = Vec::new();
|
|
990
|
+
Instruction::RefNull(HeapType::Concrete(gc_types.closure_type_idx)).encode(&mut init);
|
|
808
|
-
|
|
991
|
+
let global_idx = module.addGlobal(gcRef(gc_types.closure_type_idx), true, &init);
|
|
809
|
-
bytes.extend_from_slice(&0i32.to_le_bytes());
|
|
810
|
-
named_fn_data_segments.push((offset, bytes));
|
|
811
|
-
named_fn_values.insert(name.clone(),
|
|
992
|
+
named_fn_values.insert(name.clone(), global_idx);
|
|
993
|
+
|
|
994
|
+
Instruction::I32Const(table_idx as i32).encode(&mut start_body);
|
|
995
|
+
Instruction::RefNull(HeapType::ANY).encode(&mut start_body);
|
|
996
|
+
Instruction::StructNew(gc_types.closure_type_idx).encode(&mut start_body);
|
|
997
|
+
Instruction::GlobalSet(global_idx).encode(&mut start_body);
|
|
812
998
|
}
|
|
813
999
|
|
|
1000
|
+
// The `start` function is now complete — every payload-free variant singleton
|
|
1001
|
+
// (above) and every trampoline closure (above) has appended its own init code.
|
|
1002
|
+
// It declares no locals of its own, but a function body's raw bytes must still
|
|
1003
|
+
// start with the (empty) locals-declaration vector — a single `0x00` — before
|
|
1004
|
+
// the instruction stream, exactly like every other compiled function body below.
|
|
1005
|
+
Instruction::End.encode(&mut start_body);
|
|
1006
|
+
let mut final_start_body = vec![0u8];
|
|
1007
|
+
final_start_body.extend(start_body);
|
|
1008
|
+
let start_slot = (start_func_idx - module.func_import_count) as usize;
|
|
1009
|
+
module.functions[start_slot].1 = final_start_body;
|
|
1010
|
+
|
|
814
1011
|
// Runtime helpers backing string interpolation (`"{expr}"`): always registered
|
|
815
1012
|
// (unconditionally, for simplicity) since they're cheap and self-contained.
|
|
816
|
-
let string_concat_func = registerStringConcatHelper(&mut module,
|
|
1013
|
+
let string_concat_func = registerStringConcatHelper(&mut module, gc_types.str_type_idx);
|
|
817
|
-
let int_to_string_func = registerIntToStringHelper(&mut module,
|
|
1014
|
+
let int_to_string_func = registerIntToStringHelper(&mut module, gc_types.str_type_idx);
|
|
818
1015
|
|
|
819
1016
|
let ctx = CompileCtx {
|
|
820
|
-
func_ids, func_sigs, classes, methods, enum_variants, enum_params, global_env,
|
|
1017
|
+
func_ids, func_sigs, classes, methods, enum_variants, enum_params, global_env,
|
|
821
1018
|
closures, closure_asts, closure_call_types, named_fn_values,
|
|
822
|
-
string_concat_func, int_to_string_func, gc_types,
|
|
1019
|
+
string_concat_func, int_to_string_func, gc_types, singleton_globals,
|
|
823
1020
|
};
|
|
824
1021
|
|
|
825
|
-
let mut state = ModuleState {
|
|
1022
|
+
let mut state = ModuleState { passive_segments: Vec::new() };
|
|
826
1023
|
|
|
827
1024
|
let mut compiled_bodies: Vec<(String, Vec<u8>)> = Vec::new();
|
|
828
1025
|
for f in &fns {
|
|
@@ -849,8 +1046,12 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
849
1046
|
module.functions[slot].1 = body.clone();
|
|
850
1047
|
}
|
|
851
1048
|
|
|
1049
|
+
// Flush every staged string literal into the module in the SAME order they were
|
|
1050
|
+
// staged — `compileStaticString` already baked each one's index (its position in
|
|
1051
|
+
// `state.passive_segments` at staging time) into an `array.new_data` instruction,
|
|
1052
|
+
// so that order must be preserved exactly for those indices to still be correct.
|
|
852
|
-
for
|
|
1053
|
+
for bytes in &state.passive_segments {
|
|
853
|
-
module.
|
|
1054
|
+
module.addPassiveDataSegment(bytes);
|
|
854
1055
|
}
|
|
855
1056
|
|
|
856
1057
|
if let Some(&main_idx) = ctx.func_ids.get("main") {
|
|
@@ -860,110 +1061,59 @@ pub fn compileSource(source: &ast::Source) -> Result<Vec<u8>, String> {
|
|
|
860
1061
|
Ok(module.finish())
|
|
861
1062
|
}
|
|
862
1063
|
|
|
863
|
-
/// Registers `__string_concat(a:
|
|
1064
|
+
/// Registers `__string_concat(a: ref Str, b: ref Str) -> ref Str`, a hand-written
|
|
864
|
-
/// helper backing string interpolation
|
|
1065
|
+
/// runtime helper backing string interpolation. `Str` is a wasm-gc `array<i8>`,
|
|
865
|
-
/// string (`[len: u32][bytes]`, matching a literal string's own layout) exactly
|
|
866
|
-
///
|
|
1066
|
+
/// which tracks its own length (`array.len`) — building the concatenation is:
|
|
867
|
-
///
|
|
1067
|
+
/// allocate a new array sized `len(a) + len(b)`, then two `array.copy`s (a
|
|
868
|
-
///
|
|
1068
|
+
/// whole-array-in-one-instruction bulk copy).
|
|
869
|
-
fn registerStringConcatHelper(module: &mut WasmModule,
|
|
1069
|
+
fn registerStringConcatHelper(module: &mut WasmModule, str_type_idx: u32) -> u32 {
|
|
1070
|
+
let str_ref = gcRef(str_type_idx);
|
|
870
|
-
let type_idx = module.addType(&[
|
|
1071
|
+
let type_idx = module.addType(&[str_ref, str_ref], &[str_ref]);
|
|
871
1072
|
|
|
872
|
-
// locals: 0=a (param), 1=b (param), 2=len_a, 3=len_b, 4=result
|
|
1073
|
+
// locals: 0=a (param), 1=b (param), 2=len_a, 3=len_b, 4=result
|
|
873
1074
|
const A: u32 = 0;
|
|
874
1075
|
const B: u32 = 1;
|
|
875
1076
|
const LEN_A: u32 = 2;
|
|
876
1077
|
const LEN_B: u32 = 3;
|
|
877
1078
|
const RESULT: u32 = 4;
|
|
878
|
-
const I: u32 = 5;
|
|
879
1079
|
|
|
880
1080
|
let mut body = Vec::new();
|
|
881
|
-
body.extend(encodeLeb128U32(
|
|
1081
|
+
body.extend(encodeLeb128U32(2)); // two locals groups
|
|
882
|
-
body.extend(encodeLeb128U32(
|
|
1082
|
+
body.extend(encodeLeb128U32(2)); // len_a, len_b: i32
|
|
883
1083
|
ValType::I32.encode(&mut body);
|
|
1084
|
+
body.extend(encodeLeb128U32(1)); // result: ref
|
|
1085
|
+
str_ref.encode(&mut body);
|
|
884
1086
|
|
|
885
|
-
// len_a =
|
|
1087
|
+
// len_a = array.len(a); len_b = array.len(b)
|
|
886
1088
|
Instruction::LocalGet(A).encode(&mut body);
|
|
887
|
-
Instruction::
|
|
1089
|
+
Instruction::ArrayLen.encode(&mut body);
|
|
888
1090
|
Instruction::LocalSet(LEN_A).encode(&mut body);
|
|
889
1091
|
Instruction::LocalGet(B).encode(&mut body);
|
|
890
|
-
Instruction::
|
|
1092
|
+
Instruction::ArrayLen.encode(&mut body);
|
|
891
1093
|
Instruction::LocalSet(LEN_B).encode(&mut body);
|
|
892
1094
|
|
|
893
|
-
// result =
|
|
1095
|
+
// result = array.new_default(str_type_idx, len_a + len_b)
|
|
894
|
-
Instruction::GlobalGet(bump_global).encode(&mut body);
|
|
895
|
-
Instruction::LocalSet(RESULT).encode(&mut body);
|
|
896
|
-
Instruction::GlobalGet(bump_global).encode(&mut body);
|
|
897
1096
|
Instruction::LocalGet(LEN_A).encode(&mut body);
|
|
898
1097
|
Instruction::LocalGet(LEN_B).encode(&mut body);
|
|
899
1098
|
Instruction::I32Add.encode(&mut body);
|
|
900
|
-
Instruction::
|
|
1099
|
+
Instruction::ArrayNewDefault(str_type_idx).encode(&mut body);
|
|
901
|
-
Instruction::I32Add.encode(&mut body);
|
|
902
|
-
Instruction::I32Add.encode(&mut body);
|
|
903
|
-
Instruction::
|
|
1100
|
+
Instruction::LocalSet(RESULT).encode(&mut body);
|
|
904
1101
|
|
|
905
|
-
//
|
|
1102
|
+
// array.copy(dst: result, dst_offset: 0, src: a, src_offset: 0, len: len_a)
|
|
906
1103
|
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
907
|
-
Instruction::LocalGet(LEN_A).encode(&mut body);
|
|
908
|
-
Instruction::LocalGet(LEN_B).encode(&mut body);
|
|
909
|
-
Instruction::I32Add.encode(&mut body);
|
|
910
|
-
Instruction::I32Store(MemArg { offset: 0, align: 2, memory_index: 0 }).encode(&mut body);
|
|
911
|
-
|
|
912
|
-
// for (i = 0; i < len_a; i++) result[4+i] = a[4+i]
|
|
913
1104
|
Instruction::I32Const(0).encode(&mut body);
|
|
914
|
-
Instruction::LocalSet(I).encode(&mut body);
|
|
915
|
-
Instruction::Block(BlockType::Empty).encode(&mut body);
|
|
916
|
-
Instruction::Loop(BlockType::Empty).encode(&mut body);
|
|
917
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
918
|
-
Instruction::LocalGet(LEN_A).encode(&mut body);
|
|
919
|
-
Instruction::I32GeU.encode(&mut body);
|
|
920
|
-
Instruction::BrIf(1).encode(&mut body);
|
|
921
|
-
// address = result + i
|
|
922
|
-
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
923
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
924
|
-
Instruction::I32Add.encode(&mut body);
|
|
925
|
-
// value = load8u(a + i, offset 4)
|
|
926
1105
|
Instruction::LocalGet(A).encode(&mut body);
|
|
927
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
928
|
-
Instruction::I32Add.encode(&mut body);
|
|
929
|
-
Instruction::I32Load8U(MemArg { offset: 4, align: 0, memory_index: 0 }).encode(&mut body);
|
|
930
|
-
Instruction::I32Store8(MemArg { offset: 4, align: 0, memory_index: 0 }).encode(&mut body);
|
|
931
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
932
|
-
Instruction::I32Const(1).encode(&mut body);
|
|
933
|
-
Instruction::I32Add.encode(&mut body);
|
|
934
|
-
Instruction::LocalSet(I).encode(&mut body);
|
|
935
|
-
Instruction::Br(0).encode(&mut body);
|
|
936
|
-
Instruction::End.encode(&mut body);
|
|
937
|
-
Instruction::End.encode(&mut body);
|
|
938
|
-
|
|
939
|
-
// for (i = 0; i < len_b; i++) result[4+len_a+i] = b[4+i]
|
|
940
1106
|
Instruction::I32Const(0).encode(&mut body);
|
|
941
|
-
Instruction::LocalSet(I).encode(&mut body);
|
|
942
|
-
Instruction::Block(BlockType::Empty).encode(&mut body);
|
|
943
|
-
Instruction::Loop(BlockType::Empty).encode(&mut body);
|
|
944
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
945
|
-
Instruction::LocalGet(
|
|
1107
|
+
Instruction::LocalGet(LEN_A).encode(&mut body);
|
|
946
|
-
Instruction::I32GeU.encode(&mut body);
|
|
947
|
-
Instruction::
|
|
1108
|
+
Instruction::ArrayCopy { array_type_index_dst: str_type_idx, array_type_index_src: str_type_idx }.encode(&mut body);
|
|
1109
|
+
|
|
948
|
-
//
|
|
1110
|
+
// array.copy(dst: result, dst_offset: len_a, src: b, src_offset: 0, len: len_b)
|
|
949
1111
|
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
950
1112
|
Instruction::LocalGet(LEN_A).encode(&mut body);
|
|
951
|
-
Instruction::I32Add.encode(&mut body);
|
|
952
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
953
|
-
Instruction::I32Add.encode(&mut body);
|
|
954
|
-
// value = load8u(b + i, offset 4)
|
|
955
1113
|
Instruction::LocalGet(B).encode(&mut body);
|
|
956
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
957
|
-
Instruction::I32Add.encode(&mut body);
|
|
958
|
-
Instruction::I32Load8U(MemArg { offset: 4, align: 0, memory_index: 0 }).encode(&mut body);
|
|
959
|
-
Instruction::I32Store8(MemArg { offset: 4, align: 0, memory_index: 0 }).encode(&mut body);
|
|
960
|
-
Instruction::LocalGet(I).encode(&mut body);
|
|
961
|
-
Instruction::I32Const(
|
|
1114
|
+
Instruction::I32Const(0).encode(&mut body);
|
|
962
|
-
Instruction::I32Add.encode(&mut body);
|
|
963
|
-
Instruction::
|
|
1115
|
+
Instruction::LocalGet(LEN_B).encode(&mut body);
|
|
964
|
-
Instruction::
|
|
1116
|
+
Instruction::ArrayCopy { array_type_index_dst: str_type_idx, array_type_index_src: str_type_idx }.encode(&mut body);
|
|
965
|
-
Instruction::End.encode(&mut body);
|
|
966
|
-
Instruction::End.encode(&mut body);
|
|
967
1117
|
|
|
968
1118
|
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
969
1119
|
Instruction::End.encode(&mut body);
|
|
@@ -971,31 +1121,34 @@ fn registerStringConcatHelper(module: &mut WasmModule, bump_global: u32) -> u32
|
|
|
971
1121
|
module.addFunction(type_idx, &body)
|
|
972
1122
|
}
|
|
973
1123
|
|
|
974
|
-
/// Registers `__int_to_string(n: i64) ->
|
|
1124
|
+
/// Registers `__int_to_string(n: i64) -> ref Str`, a hand-written runtime helper
|
|
975
|
-
/// backing string interpolation:
|
|
1125
|
+
/// backing string interpolation: allocates a new `array<i8>` holding `n`'s decimal
|
|
976
|
-
///
|
|
1126
|
+
/// representation (handling a leading `-` for negatives, and `0` correctly via a
|
|
977
|
-
///
|
|
1127
|
+
/// do-while digit count that always runs at least once).
|
|
978
|
-
fn registerIntToStringHelper(module: &mut WasmModule,
|
|
1128
|
+
fn registerIntToStringHelper(module: &mut WasmModule, str_type_idx: u32) -> u32 {
|
|
1129
|
+
let str_ref = gcRef(str_type_idx);
|
|
979
|
-
let type_idx = module.addType(&[ValType::I64], &[
|
|
1130
|
+
let type_idx = module.addType(&[ValType::I64], &[str_ref]);
|
|
980
1131
|
|
|
981
|
-
// locals: 0=n (param, i64), 1=is_neg (i32), 2=
|
|
1132
|
+
// locals: 0=n (param, i64), 1=is_neg (i32), 2=count (i32), 3=total_len (i32),
|
|
982
|
-
// 4=
|
|
1133
|
+
// 4=pos (i32), 5=result (ref), 6=abs_n (i64), 7=temp (i64)
|
|
983
|
-
// Locals are declared as one group of
|
|
1134
|
+
// Locals are declared as one group of 4 `i32`s, one group of 1 `ref`, then one
|
|
984
|
-
// `i64`s (see below), so indices must stay grouped by type in that
|
|
1135
|
+
// group of 2 `i64`s (see below), so indices must stay grouped by type in that
|
|
985
|
-
// NOT in whatever order reads best logically.
|
|
1136
|
+
// same order — NOT in whatever order reads best logically.
|
|
986
1137
|
const N: u32 = 0;
|
|
987
1138
|
const IS_NEG: u32 = 1;
|
|
988
1139
|
const COUNT: u32 = 2;
|
|
989
1140
|
const TOTAL_LEN: u32 = 3;
|
|
1141
|
+
const POS: u32 = 4;
|
|
990
|
-
const RESULT: u32 =
|
|
1142
|
+
const RESULT: u32 = 5;
|
|
991
|
-
const POS: u32 = 5;
|
|
992
1143
|
const ABS_N: u32 = 6;
|
|
993
1144
|
const TEMP: u32 = 7;
|
|
994
1145
|
|
|
995
1146
|
let mut body = Vec::new();
|
|
996
|
-
body.extend(encodeLeb128U32(
|
|
1147
|
+
body.extend(encodeLeb128U32(3)); // three locals groups
|
|
997
|
-
body.extend(encodeLeb128U32(
|
|
1148
|
+
body.extend(encodeLeb128U32(4)); // is_neg, count, total_len, pos: i32
|
|
998
1149
|
ValType::I32.encode(&mut body);
|
|
1150
|
+
body.extend(encodeLeb128U32(1)); // result: ref
|
|
1151
|
+
str_ref.encode(&mut body);
|
|
999
1152
|
body.extend(encodeLeb128U32(2)); // abs_n, temp: i64
|
|
1000
1153
|
ValType::I64.encode(&mut body);
|
|
1001
1154
|
|
|
@@ -1042,39 +1195,27 @@ fn registerIntToStringHelper(module: &mut WasmModule, bump_global: u32) -> u32 {
|
|
|
1042
1195
|
Instruction::I32Add.encode(&mut body);
|
|
1043
1196
|
Instruction::LocalSet(TOTAL_LEN).encode(&mut body);
|
|
1044
1197
|
|
|
1198
|
+
// result = array.new_default(str_type_idx, total_len) — no length prefix needed,
|
|
1045
|
-
//
|
|
1199
|
+
// `array.len` reads it back natively.
|
|
1046
|
-
Instruction::GlobalGet(bump_global).encode(&mut body);
|
|
1047
|
-
Instruction::LocalSet(RESULT).encode(&mut body);
|
|
1048
|
-
Instruction::GlobalGet(bump_global).encode(&mut body);
|
|
1049
|
-
Instruction::LocalGet(TOTAL_LEN).encode(&mut body);
|
|
1050
|
-
Instruction::I32Const(4).encode(&mut body);
|
|
1051
|
-
Instruction::I32Add.encode(&mut body);
|
|
1052
|
-
Instruction::I32Add.encode(&mut body);
|
|
1053
|
-
Instruction::GlobalSet(bump_global).encode(&mut body);
|
|
1054
|
-
|
|
1055
|
-
// store total_len at result+0
|
|
1056
|
-
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
1057
1200
|
Instruction::LocalGet(TOTAL_LEN).encode(&mut body);
|
|
1058
|
-
Instruction::
|
|
1201
|
+
Instruction::ArrayNewDefault(str_type_idx).encode(&mut body);
|
|
1202
|
+
Instruction::LocalSet(RESULT).encode(&mut body);
|
|
1059
1203
|
|
|
1060
|
-
// pos =
|
|
1204
|
+
// pos = total_len; temp = abs_n
|
|
1061
|
-
Instruction::I32Const(4).encode(&mut body);
|
|
1062
1205
|
Instruction::LocalGet(TOTAL_LEN).encode(&mut body);
|
|
1063
|
-
Instruction::I32Add.encode(&mut body);
|
|
1064
1206
|
Instruction::LocalSet(POS).encode(&mut body);
|
|
1065
1207
|
Instruction::LocalGet(ABS_N).encode(&mut body);
|
|
1066
1208
|
Instruction::LocalSet(TEMP).encode(&mut body);
|
|
1067
1209
|
|
|
1068
|
-
// do { pos--; result[pos] = '0' + temp % 10; temp /= 10 } while (pos >
|
|
1210
|
+
// do { pos--; result[pos] = '0' + temp % 10; temp /= 10 } while (pos > is_neg)
|
|
1069
1211
|
Instruction::Loop(BlockType::Empty).encode(&mut body);
|
|
1070
1212
|
Instruction::LocalGet(POS).encode(&mut body);
|
|
1071
1213
|
Instruction::I32Const(1).encode(&mut body);
|
|
1072
1214
|
Instruction::I32Sub.encode(&mut body);
|
|
1073
1215
|
Instruction::LocalSet(POS).encode(&mut body);
|
|
1074
|
-
//
|
|
1216
|
+
// array.set(result, pos, value) — stack order [array_ref, index, value]
|
|
1075
1217
|
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
1076
1218
|
Instruction::LocalGet(POS).encode(&mut body);
|
|
1077
|
-
Instruction::I32Add.encode(&mut body);
|
|
1078
1219
|
// value = '0' + (temp % 10)
|
|
1079
1220
|
Instruction::LocalGet(TEMP).encode(&mut body);
|
|
1080
1221
|
Instruction::I64Const(10).encode(&mut body);
|
|
@@ -1082,29 +1223,26 @@ fn registerIntToStringHelper(module: &mut WasmModule, bump_global: u32) -> u32 {
|
|
|
1082
1223
|
Instruction::I64Const(48).encode(&mut body);
|
|
1083
1224
|
Instruction::I64Add.encode(&mut body);
|
|
1084
1225
|
Instruction::I32WrapI64.encode(&mut body);
|
|
1085
|
-
Instruction::
|
|
1226
|
+
Instruction::ArraySet(str_type_idx).encode(&mut body);
|
|
1086
1227
|
// temp /= 10
|
|
1087
1228
|
Instruction::LocalGet(TEMP).encode(&mut body);
|
|
1088
1229
|
Instruction::I64Const(10).encode(&mut body);
|
|
1089
1230
|
Instruction::I64DivS.encode(&mut body);
|
|
1090
1231
|
Instruction::LocalSet(TEMP).encode(&mut body);
|
|
1091
|
-
// while (pos >
|
|
1232
|
+
// while (pos > is_neg)
|
|
1092
1233
|
Instruction::LocalGet(POS).encode(&mut body);
|
|
1093
|
-
Instruction::I32Const(4).encode(&mut body);
|
|
1094
1234
|
Instruction::LocalGet(IS_NEG).encode(&mut body);
|
|
1095
|
-
Instruction::I32Add.encode(&mut body);
|
|
1096
1235
|
Instruction::I32GtU.encode(&mut body);
|
|
1097
1236
|
Instruction::BrIf(0).encode(&mut body);
|
|
1098
1237
|
Instruction::End.encode(&mut body);
|
|
1099
1238
|
|
|
1100
|
-
// if (is_neg) result[
|
|
1239
|
+
// if (is_neg) result[0] = '-'
|
|
1101
1240
|
Instruction::LocalGet(IS_NEG).encode(&mut body);
|
|
1102
1241
|
Instruction::If(BlockType::Empty).encode(&mut body);
|
|
1103
1242
|
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
1104
|
-
Instruction::I32Const(
|
|
1243
|
+
Instruction::I32Const(0).encode(&mut body);
|
|
1105
|
-
Instruction::I32Add.encode(&mut body);
|
|
1106
1244
|
Instruction::I32Const(45).encode(&mut body); // '-'
|
|
1107
|
-
Instruction::
|
|
1245
|
+
Instruction::ArraySet(str_type_idx).encode(&mut body);
|
|
1108
1246
|
Instruction::End.encode(&mut body);
|
|
1109
1247
|
|
|
1110
1248
|
Instruction::LocalGet(RESULT).encode(&mut body);
|
|
@@ -1757,18 +1895,22 @@ struct Collector<'a> {
|
|
|
1757
1895
|
cctx: plum_checker::CheckCtx<'a>,
|
|
1758
1896
|
named: Vec<(String, PlumType)>,
|
|
1759
1897
|
named_set: std::collections::HashSet<String>,
|
|
1760
|
-
classcall_scratch: HashMap<usize, u32>,
|
|
1761
1898
|
/// One scratch-local type per subject (usually one, more for `match a, b, ...`).
|
|
1762
1899
|
match_scratch: HashMap<usize, Vec<PlumType>>,
|
|
1763
|
-
next_classcall_slot: u32,
|
|
1764
|
-
/// `Closure` expr identity -> offset of its first (of two) construction scratch slots.
|
|
1765
|
-
closure_scratch: HashMap<usize, u32>,
|
|
1766
|
-
next_closure_slot: u32,
|
|
1767
|
-
/// `CasePattern::Class` identity -> its scratch local
|
|
1900
|
+
/// `CasePattern::Class` identity -> its scratch local. Covers EVERY constructor
|
|
1901
|
+
/// pattern, including top-level ones — under wasm-gc, matching `Some(v)` needs a
|
|
1902
|
+
/// `ref.cast` from the subject's static supertype down to the concrete variant
|
|
1903
|
+
/// type before any `struct.get` on it validates, and that narrowed value needs
|
|
1904
|
+
/// its OWN local (declared with the concrete variant's ref type) distinct from
|
|
1905
|
+
/// the original wide-typed subject local, which stays declared at the
|
|
1906
|
+
/// supertype's type for the whole function. (Before wasm-gc, this only covered
|
|
1768
|
-
///
|
|
1907
|
+
/// patterns nested inside another constructor pattern's fields, depth >= 1,
|
|
1769
|
-
///
|
|
1908
|
+
/// since a plain i32 pointer needed no per-pattern static type at all.)
|
|
1770
|
-
/// / `all_subjects`, so this only covers depth >= 1.
|
|
1771
1909
|
nested_class_scratch: HashMap<usize, u32>,
|
|
1910
|
+
/// Slot number (the `u32` values in `nested_class_scratch`) -> the variant name
|
|
1911
|
+
/// it narrows to, so its scratch local can be declared with that variant's exact
|
|
1912
|
+
/// concrete ref type instead of a uniform placeholder type.
|
|
1913
|
+
nested_class_scratch_types: Vec<String>,
|
|
1772
1914
|
next_nested_class_slot: u32,
|
|
1773
1915
|
/// `For` stmt identity (pointer address) -> a slot number; each slot reserves 2
|
|
1774
1916
|
/// consecutive `i32` scratch locals for variadic iteration (`for v in nums`):
|
|
@@ -1791,7 +1933,7 @@ impl<'a> Collector<'a> {
|
|
|
1791
1933
|
/// its correct field type, and reserves a scratch local for every `Class`
|
|
1792
1934
|
/// sub-pattern found *nested* inside another constructor pattern's fields (the
|
|
1793
1935
|
/// outermost, per-subject pattern doesn't need one — see `nested_class_scratch`).
|
|
1794
|
-
fn collectPattern(&mut self, pat: &ast::CasePattern, ty: &PlumType
|
|
1936
|
+
fn collectPattern(&mut self, pat: &ast::CasePattern, ty: &PlumType) {
|
|
1795
1937
|
match pat {
|
|
1796
1938
|
ast::CasePattern::Name(n) => {
|
|
1797
1939
|
let is_variant = n.chars().next().map(|c| c.is_uppercase()).unwrap_or(false)
|
|
@@ -1801,16 +1943,15 @@ impl<'a> Collector<'a> {
|
|
|
1801
1943
|
}
|
|
1802
1944
|
}
|
|
1803
1945
|
ast::CasePattern::Class { name, fields } => {
|
|
1804
|
-
if !top_level {
|
|
1805
|
-
|
|
1946
|
+
let key = pat as *const ast::CasePattern as usize;
|
|
1806
|
-
|
|
1947
|
+
let slot = self.next_nested_class_slot;
|
|
1807
|
-
|
|
1948
|
+
self.next_nested_class_slot += 1;
|
|
1808
|
-
|
|
1949
|
+
self.nested_class_scratch.insert(key, slot);
|
|
1809
|
-
|
|
1950
|
+
self.nested_class_scratch_types.push(name.clone());
|
|
1810
1951
|
if let Some(info) = self.cctx.enum_variants.get(name) {
|
|
1811
1952
|
let field_types = info.field_types.clone();
|
|
1812
1953
|
for (f, fty) in fields.iter().zip(field_types.iter()) {
|
|
1813
|
-
self.collectPattern(f, fty
|
|
1954
|
+
self.collectPattern(f, fty);
|
|
1814
1955
|
}
|
|
1815
1956
|
}
|
|
1816
1957
|
}
|
|
@@ -1900,7 +2041,7 @@ impl<'a> Collector<'a> {
|
|
|
1900
2041
|
for case in &m.cases {
|
|
1901
2042
|
let saved = self.env.clone();
|
|
1902
2043
|
for (pat, subject_ty) in case.patterns.iter().zip(subject_types.iter()) {
|
|
1903
|
-
self.collectPattern(pat, subject_ty
|
|
2044
|
+
self.collectPattern(pat, subject_ty);
|
|
1904
2045
|
}
|
|
1905
2046
|
self.walkBlock(&case.body);
|
|
1906
2047
|
self.env = saved;
|
|
@@ -1913,9 +2054,6 @@ impl<'a> Collector<'a> {
|
|
|
1913
2054
|
fn walkExpr(&mut self, expr: &ast::Expr) {
|
|
1914
2055
|
match expr {
|
|
1915
2056
|
ast::Expr::ClassCall(call) => {
|
|
1916
|
-
let idx = self.next_classcall_slot;
|
|
1917
|
-
self.next_classcall_slot += 1;
|
|
1918
|
-
self.classcall_scratch.insert(expr as *const ast::Expr as usize, idx);
|
|
1919
2057
|
for fa in &call.fields {
|
|
1920
2058
|
self.walkExpr(&fa.value);
|
|
1921
2059
|
}
|
|
@@ -1941,18 +2079,6 @@ impl<'a> Collector<'a> {
|
|
|
1941
2079
|
self.walkExpr(&t.else_);
|
|
1942
2080
|
}
|
|
1943
2081
|
ast::Expr::FnCall(call) => {
|
|
1944
|
-
let carries_payload = self.cctx.enum_variants.get(&call.name)
|
|
1945
|
-
.map(|info| !info.field_types.is_empty())
|
|
1946
|
-
.unwrap_or(false);
|
|
1947
|
-
let is_variadic_call = matches!(
|
|
1948
|
-
plum_checker::lookup(&self.env, &call.name),
|
|
1949
|
-
Ok(PlumType::TFun(params, _)) if matches!(params.last(), Some(PlumType::TVariadic(_)))
|
|
1950
|
-
);
|
|
1951
|
-
if carries_payload || is_variadic_call {
|
|
1952
|
-
let idx = self.next_classcall_slot;
|
|
1953
|
-
self.next_classcall_slot += 1;
|
|
1954
|
-
self.classcall_scratch.insert(expr as *const ast::Expr as usize, idx);
|
|
1955
|
-
}
|
|
1956
2082
|
for arg in &call.args {
|
|
1957
2083
|
self.walkArg(arg);
|
|
1958
2084
|
}
|
|
@@ -1965,34 +2091,15 @@ impl<'a> Collector<'a> {
|
|
|
1965
2091
|
}
|
|
1966
2092
|
}
|
|
1967
2093
|
}
|
|
1968
|
-
ast::Expr::TypeName(
|
|
2094
|
+
ast::Expr::TypeName(_) => {}
|
|
1969
|
-
// A bare discriminant-variant reference (e.g. `READ_MIN_OCCURANCES`) heap-
|
|
1970
|
-
// allocates via the SAME `compileVariantConstruction` path a payload-carrying
|
|
1971
|
-
// `FnCall` variant construction uses (see the `Expr::TypeName` arm in
|
|
1972
|
-
// `compileExpr`), so it needs the same scratch slot registered here.
|
|
1973
|
-
let carries_baked_in_payload = self.cctx.enum_variants.get(n)
|
|
1974
|
-
.map(|info| !info.values.is_empty())
|
|
1975
|
-
.unwrap_or(false);
|
|
1976
|
-
if carries_baked_in_payload {
|
|
1977
|
-
let idx = self.next_classcall_slot;
|
|
1978
|
-
self.next_classcall_slot += 1;
|
|
1979
|
-
self.classcall_scratch.insert(expr as *const ast::Expr as usize, idx);
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
2095
|
ast::Expr::Int(_)
|
|
1983
2096
|
| ast::Expr::Float(_)
|
|
1984
2097
|
| ast::Expr::String(_)
|
|
1985
2098
|
| ast::Expr::Self_
|
|
1986
2099
|
| ast::Expr::Var(_) => {}
|
|
1987
|
-
// A closure literal
|
|
2100
|
+
// A closure literal's body has its own locals, belonging to the separate
|
|
1988
|
-
// function (env struct ptr, closure struct ptr). Its body's own locals are
|
|
1989
|
-
//
|
|
2101
|
+
// closure function it compiles to — nothing to recurse into here.
|
|
1990
|
-
// don't recurse into the body here.
|
|
1991
|
-
ast::Expr::Closure(
|
|
2102
|
+
ast::Expr::Closure(_) => {}
|
|
1992
|
-
let base = self.next_closure_slot;
|
|
1993
|
-
self.next_closure_slot += 2;
|
|
1994
|
-
self.closure_scratch.insert(cl.as_ref() as *const ast::Closure as usize, base);
|
|
1995
|
-
}
|
|
1996
2103
|
}
|
|
1997
2104
|
}
|
|
1998
2105
|
|
|
@@ -2030,12 +2137,9 @@ fn compileFnBody(f: &ast::Fn, ctx: &CompileCtx, state: &mut ModuleState) -> Resu
|
|
|
2030
2137
|
cctx: checkCtxOf(&ctx.classes, &ctx.methods, &ctx.enum_variants, &ctx.enum_params),
|
|
2031
2138
|
named: Vec::new(),
|
|
2032
2139
|
named_set: Default::default(),
|
|
2033
|
-
classcall_scratch: HashMap::new(),
|
|
2034
2140
|
match_scratch: HashMap::new(),
|
|
2035
|
-
next_classcall_slot: 0,
|
|
2036
|
-
closure_scratch: HashMap::new(),
|
|
2037
|
-
next_closure_slot: 0,
|
|
2038
2141
|
nested_class_scratch: HashMap::new(),
|
|
2142
|
+
nested_class_scratch_types: Vec::new(),
|
|
2039
2143
|
next_nested_class_slot: 0,
|
|
2040
2144
|
variadic_for_scratch: HashMap::new(),
|
|
2041
2145
|
next_variadic_for_slot: 0,
|
|
@@ -2067,13 +2171,6 @@ fn compileFnBody(f: &ast::Fn, ctx: &CompileCtx, state: &mut ModuleState) -> Resu
|
|
|
2067
2171
|
idx += 1;
|
|
2068
2172
|
}
|
|
2069
2173
|
|
|
2070
|
-
let classcall_scratch_base = idx;
|
|
2071
|
-
let classcall_count = collector.classcall_scratch.values().copied().max().map(|m| m + 1).unwrap_or(0);
|
|
2072
|
-
for _ in 0..classcall_count {
|
|
2073
|
-
groups.push(ValType::I32);
|
|
2074
|
-
idx += 1;
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
2174
|
let match_scratch_base = idx;
|
|
2078
2175
|
let mut match_scratch_index: HashMap<usize, u32> = HashMap::new();
|
|
2079
2176
|
for (ptr, types) in collector.match_scratch.iter() {
|
|
@@ -2085,9 +2182,14 @@ fn compileFnBody(f: &ast::Fn, ctx: &CompileCtx, state: &mut ModuleState) -> Resu
|
|
|
2085
2182
|
}
|
|
2086
2183
|
|
|
2087
2184
|
let nested_class_scratch_base = idx;
|
|
2185
|
+
// Each slot is declared with its OWN concrete variant ref type (not a uniform
|
|
2186
|
+
// placeholder) — `struct.get` on a constructor-pattern match requires the local
|
|
2088
|
-
|
|
2187
|
+
// holding the narrowed (`ref.cast`) value to be statically typed as that exact
|
|
2188
|
+
// variant, and different slots very likely narrow to different variants.
|
|
2089
|
-
for
|
|
2189
|
+
for vname in &collector.nested_class_scratch_types {
|
|
2190
|
+
let variant_idx = withGcTypes(|r| *r.variant_type_idx.get(vname)
|
|
2191
|
+
.unwrap_or_else(|| panic!("internal codegen error: variant '{}' missing from the GC type registry", vname)));
|
|
2090
|
-
groups.push(
|
|
2192
|
+
groups.push(gcRef(variant_idx));
|
|
2091
2193
|
idx += 1;
|
|
2092
2194
|
}
|
|
2093
2195
|
|
|
@@ -2099,12 +2201,6 @@ fn compileFnBody(f: &ast::Fn, ctx: &CompileCtx, state: &mut ModuleState) -> Resu
|
|
|
2099
2201
|
idx += 2;
|
|
2100
2202
|
}
|
|
2101
2203
|
|
|
2102
|
-
let closure_scratch_base = idx;
|
|
2103
|
-
let closure_scratch_count = collector.closure_scratch.values().copied().max().map(|m| m + 2).unwrap_or(0);
|
|
2104
|
-
for _ in 0..closure_scratch_count {
|
|
2105
|
-
groups.push(ValType::I32);
|
|
2106
|
-
}
|
|
2107
|
-
|
|
2108
2204
|
if groups.is_empty() {
|
|
2109
2205
|
body.push(0);
|
|
2110
2206
|
} else {
|
|
@@ -2117,12 +2213,8 @@ fn compileFnBody(f: &ast::Fn, ctx: &CompileCtx, state: &mut ModuleState) -> Resu
|
|
|
2117
2213
|
|
|
2118
2214
|
let local_ctx = LocalCtx {
|
|
2119
2215
|
locals,
|
|
2120
|
-
classcall_scratch_base,
|
|
2121
|
-
classcall_scratch: collector.classcall_scratch,
|
|
2122
2216
|
match_scratch_base,
|
|
2123
2217
|
match_scratch_index,
|
|
2124
|
-
closure_scratch_base,
|
|
2125
|
-
closure_scratch: collector.closure_scratch,
|
|
2126
2218
|
nested_class_scratch_base,
|
|
2127
2219
|
nested_class_scratch: collector.nested_class_scratch,
|
|
2128
2220
|
variadic_for_scratch_base,
|
|
@@ -2138,9 +2230,10 @@ fn compileFnBody(f: &ast::Fn, ctx: &CompileCtx, state: &mut ModuleState) -> Resu
|
|
|
2138
2230
|
methods: &ctx.methods,
|
|
2139
2231
|
enum_variants: &ctx.enum_variants,
|
|
2140
2232
|
enum_params: &ctx.enum_params,
|
|
2233
|
+
gc_types: &ctx.gc_types,
|
|
2234
|
+
singleton_globals: &ctx.singleton_globals,
|
|
2141
2235
|
type_env: RefCell::new(base_env),
|
|
2142
2236
|
closure_local_sigs: RefCell::new(HashMap::new()),
|
|
2143
|
-
bump_global: ctx.bump_global,
|
|
2144
2237
|
};
|
|
2145
2238
|
|
|
2146
2239
|
let result_vt = retTypeToWasm(f.returns.as_ref());
|
|
@@ -2228,6 +2321,35 @@ fn compileStmtInValuePosition(
|
|
|
2228
2321
|
/// (each branch is `BlockType::Empty`, nothing left on the stack) or `Some(vt)` when this
|
|
2229
2322
|
/// `if` is in value position — every branch must then leave a `vt` value on the stack, which
|
|
2230
2323
|
/// requires an `else` (a value can't be produced on a path that doesn't exist).
|
|
2324
|
+
/// Compiles a `Bool`-typed expression, then a `ref.test` against the `True` variant's
|
|
2325
|
+
/// concrete type, leaving a plain `i32` (1/0) on the stack. Every place a `Bool` value
|
|
2326
|
+
/// drives wasm's OWN native control flow (`if`/`br_if`, which require a raw `i32`
|
|
2327
|
+
/// condition, not a `ref`) goes through this — see this migration plan's Decision 1
|
|
2328
|
+
/// (Bool is a full wasm-gc struct, no special-casing).
|
|
2329
|
+
fn compileBoolConditionAsI32(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut ModuleState) -> Result<(), String> {
|
|
2330
|
+
compileExpr(expr, body, ctx, state)?;
|
|
2331
|
+
let true_idx = *ctx.gc_types.variant_type_idx.get("True")
|
|
2332
|
+
.expect("internal codegen error: True must be registered in the GC type registry");
|
|
2333
|
+
Instruction::RefTestNonNull(HeapType::Concrete(true_idx)).encode(body);
|
|
2334
|
+
Ok(())
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
/// Given an `i32` boolean (1/0) already on the stack, converts it into a `Bool` ref by
|
|
2338
|
+
/// selecting the pre-allocated `True`/`False` singleton (this migration plan's
|
|
2339
|
+
/// Decision 2) — the reverse of `compileBoolConditionAsI32`. Used wherever a native
|
|
2340
|
+
/// wasm comparison/logical-op instruction just left a raw `i32` predicate on the
|
|
2341
|
+
/// stack that needs to become a proper `Bool` value.
|
|
2342
|
+
fn pushBoolRefFromI32Flag(body: &mut Vec<u8>, ctx: &LocalCtx) {
|
|
2343
|
+
let bool_ref_ty = plumTypeToValtype(&PlumType::TBool);
|
|
2344
|
+
let true_global = *ctx.singleton_globals.get("True").expect("internal codegen error: True singleton global missing");
|
|
2345
|
+
let false_global = *ctx.singleton_globals.get("False").expect("internal codegen error: False singleton global missing");
|
|
2346
|
+
Instruction::If(BlockType::Result(bool_ref_ty)).encode(body);
|
|
2347
|
+
Instruction::GlobalGet(true_global).encode(body);
|
|
2348
|
+
Instruction::Else.encode(body);
|
|
2349
|
+
Instruction::GlobalGet(false_global).encode(body);
|
|
2350
|
+
Instruction::End.encode(body);
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2231
2353
|
fn compileIf(
|
|
2232
2354
|
if_: &ast::If,
|
|
2233
2355
|
result_vt: Option<ValType>,
|
|
@@ -2241,13 +2363,13 @@ fn compileIf(
|
|
|
2241
2363
|
);
|
|
2242
2364
|
}
|
|
2243
2365
|
let bt = blockTypeFor(result_vt);
|
|
2244
|
-
|
|
2366
|
+
compileBoolConditionAsI32(&if_.condition, body, ctx, state)?;
|
|
2245
2367
|
Instruction::If(bt).encode(body);
|
|
2246
2368
|
compileCaseBody(&if_.body, result_vt, body, ctx, state)?;
|
|
2247
2369
|
if !if_.else_ifs.is_empty() || if_.else_.is_some() {
|
|
2248
2370
|
Instruction::Else.encode(body);
|
|
2249
2371
|
for ei in &if_.else_ifs {
|
|
2250
|
-
|
|
2372
|
+
compileBoolConditionAsI32(&ei.condition, body, ctx, state)?;
|
|
2251
2373
|
Instruction::If(bt).encode(body);
|
|
2252
2374
|
compileCaseBody(&ei.body, result_vt, body, ctx, state)?;
|
|
2253
2375
|
Instruction::Else.encode(body);
|
|
@@ -2309,7 +2431,7 @@ fn compileStmt(stmt: &ast::Stmt, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2309
2431
|
if let ast::Expr::Closure(cl) = value {
|
|
2310
2432
|
let key = cl.as_ref() as *const ast::Closure as usize;
|
|
2311
2433
|
if let Some(info) = ctx.closures.get(&key) {
|
|
2312
|
-
let mut sig_params = vec![ValType::
|
|
2434
|
+
let mut sig_params = vec![ValType::Ref(RefType::ANYREF)];
|
|
2313
2435
|
sig_params.extend(info.param_vts.iter().copied());
|
|
2314
2436
|
ctx.closure_local_sigs.borrow_mut().insert(name.clone(), (sig_params, info.ret_vt));
|
|
2315
2437
|
}
|
|
@@ -2325,19 +2447,18 @@ fn compileStmt(stmt: &ast::Stmt, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2325
2447
|
.classes
|
|
2326
2448
|
.get(&class_name)
|
|
2327
2449
|
.ok_or_else(|| format!("codegen: unknown class '{}'", class_name))?;
|
|
2328
|
-
let
|
|
2450
|
+
let field_idx = fields
|
|
2329
2451
|
.iter()
|
|
2330
2452
|
.position(|(n, _)| n == field_name)
|
|
2331
|
-
.map(|i| (i, fields[i].1.clone()))
|
|
2332
2453
|
.ok_or_else(|| format!("codegen: no field '{}' on class '{}'", field_name, class_name))?;
|
|
2454
|
+
let class_type_idx = *ctx.gc_types.class_type_idx.get(&class_name)
|
|
2455
|
+
.ok_or_else(|| format!("codegen: class '{}' missing from the GC type registry", class_name))?;
|
|
2456
|
+
// struct.set expects [(ref null $t) value] on the stack (ref
|
|
2457
|
+
// pushed first/deeper, value second/on top) — same push order
|
|
2458
|
+
// this already used for the old memory store.
|
|
2333
2459
|
compileExpr(object, body, ctx, state)?;
|
|
2334
2460
|
compileExpr(value, body, ctx, state)?;
|
|
2335
|
-
let offset = (field_idx as u64) * 8;
|
|
2336
|
-
match plumTypeToValtype(&field_ty) {
|
|
2337
|
-
ValType::I64 => Instruction::I64Store(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
2338
|
-
ValType::F64 => Instruction::F64Store(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
2339
|
-
|
|
2461
|
+
Instruction::StructSet { struct_type_index: class_type_idx, field_index: field_idx as u32 }.encode(body);
|
|
2340
|
-
};
|
|
2341
2462
|
}
|
|
2342
2463
|
}
|
|
2343
2464
|
}
|
|
@@ -2355,7 +2476,7 @@ fn compileStmt(stmt: &ast::Stmt, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2355
2476
|
ast::Stmt::While(w) => {
|
|
2356
2477
|
Instruction::Block(BlockType::Empty).encode(body);
|
|
2357
2478
|
Instruction::Loop(BlockType::Empty).encode(body);
|
|
2358
|
-
|
|
2479
|
+
compileBoolConditionAsI32(&w.condition, body, ctx, state)?;
|
|
2359
2480
|
Instruction::I32Eqz.encode(body);
|
|
2360
2481
|
Instruction::BrIf(1).encode(body);
|
|
2361
2482
|
compileBlock(&w.body, body, ctx, state)?;
|
|
@@ -2412,11 +2533,15 @@ fn compileStmt(stmt: &ast::Stmt, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2412
2533
|
let count_local = ctx.variadic_for_scratch_base + slot * 2;
|
|
2413
2534
|
let index_local = count_local + 1;
|
|
2414
2535
|
let elem_vt = plumTypeToValtype(&elem_ty);
|
|
2536
|
+
let array_type_idx = *ctx
|
|
2537
|
+
.gc_types
|
|
2538
|
+
.variadic_array_type_idx
|
|
2539
|
+
.get(&elem_vt)
|
|
2540
|
+
.ok_or_else(|| "internal codegen error: no variadic array type registered for this elem type".to_string())?;
|
|
2415
2541
|
|
|
2416
|
-
// count_local =
|
|
2542
|
+
// count_local = array.len(iter)
|
|
2417
2543
|
compileExpr(&f.iter, body, ctx, state)?;
|
|
2418
|
-
Instruction::I64Load(MemArg { offset: 0, align: 3, memory_index: 0 }).encode(body);
|
|
2419
|
-
Instruction::
|
|
2544
|
+
Instruction::ArrayLen.encode(body);
|
|
2420
2545
|
Instruction::LocalSet(count_local).encode(body);
|
|
2421
2546
|
|
|
2422
2547
|
// index_local = 0
|
|
@@ -2430,15 +2555,10 @@ fn compileStmt(stmt: &ast::Stmt, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2430
2555
|
Instruction::I32GeS.encode(body);
|
|
2431
2556
|
Instruction::BrIf(1).encode(body);
|
|
2432
2557
|
|
|
2433
|
-
// var =
|
|
2558
|
+
// var = array.get(iter, index)
|
|
2434
2559
|
compileExpr(&f.iter, body, ctx, state)?;
|
|
2435
|
-
Instruction::I32Const(8).encode(body);
|
|
2436
|
-
Instruction::I32Add.encode(body);
|
|
2437
2560
|
Instruction::LocalGet(index_local).encode(body);
|
|
2438
|
-
Instruction::
|
|
2561
|
+
Instruction::ArrayGet(array_type_idx).encode(body);
|
|
2439
|
-
Instruction::I32Mul.encode(body);
|
|
2440
|
-
Instruction::I32Add.encode(body);
|
|
2441
|
-
emitLoad(elem_vt, 0, body);
|
|
2442
2562
|
Instruction::LocalSet(var_idx).encode(body);
|
|
2443
2563
|
|
|
2444
2564
|
compileBlock(&f.body, body, ctx, state)?;
|
|
@@ -2472,7 +2592,7 @@ fn compileStmt(stmt: &ast::Stmt, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2472
2592
|
compileMatch(m, body, ctx, state, None)?;
|
|
2473
2593
|
}
|
|
2474
2594
|
ast::Stmt::Assert(e) => {
|
|
2475
|
-
|
|
2595
|
+
compileBoolConditionAsI32(e, body, ctx, state)?;
|
|
2476
2596
|
Instruction::I32Eqz.encode(body);
|
|
2477
2597
|
Instruction::If(BlockType::Empty).encode(body);
|
|
2478
2598
|
Instruction::Unreachable.encode(body);
|
|
@@ -2531,7 +2651,7 @@ fn exprHasResult(expr: &ast::Expr, ctx: &LocalCtx) -> bool {
|
|
|
2531
2651
|
/// for `match a, b, ...` it's the full cross product (e.g. `Bool, Bool` needs all
|
|
2532
2652
|
/// 4 combinations named, matching `libs/std/bool.plum`'s `and`/`or`).
|
|
2533
2653
|
fn matchCoversEveryEnumVariant(cases: &[ast::Case], subject_vts: &[ValType], ctx: &LocalCtx) -> bool {
|
|
2534
|
-
if subject_vts.is_empty() || subject_vts.iter().any(|vt|
|
|
2654
|
+
if subject_vts.is_empty() || subject_vts.iter().any(|vt| !matches!(vt, ValType::Ref(_))) {
|
|
2535
2655
|
return false;
|
|
2536
2656
|
}
|
|
2537
2657
|
let n = subject_vts.len();
|
|
@@ -2727,7 +2847,7 @@ fn compileCasePositions(
|
|
|
2727
2847
|
ast::CasePattern::String(_) => Err("codegen: string match patterns are not yet supported".to_string()),
|
|
2728
2848
|
ast::CasePattern::Float(_) => Err("codegen: float match patterns are not yet supported".to_string()),
|
|
2729
2849
|
ast::CasePattern::Class { name, fields } => {
|
|
2730
|
-
compileVariantConstructorArm(name, fields, subject_vt, scratch_local, case, pos, all_subjects, rest, result_vt, exhaustive_fallback, body, ctx, state)
|
|
2850
|
+
compileVariantConstructorArm(pat, name, fields, subject_vt, scratch_local, case, pos, all_subjects, rest, result_vt, exhaustive_fallback, body, ctx, state)
|
|
2731
2851
|
}
|
|
2732
2852
|
}
|
|
2733
2853
|
}
|
|
@@ -2747,23 +2867,19 @@ fn compileVariantEqArm(
|
|
|
2747
2867
|
ctx: &LocalCtx,
|
|
2748
2868
|
state: &mut ModuleState,
|
|
2749
2869
|
) -> Result<(), String> {
|
|
2750
|
-
let info = ctx
|
|
2751
|
-
|
|
2870
|
+
ctx.enum_variants
|
|
2752
2871
|
.get(name)
|
|
2753
2872
|
.ok_or_else(|| format!("codegen: unknown enum variant '{}'", name))?;
|
|
2754
|
-
if subject_vt
|
|
2873
|
+
if !matches!(subject_vt, ValType::Ref(_)) {
|
|
2755
2874
|
return Err(format!("codegen: enum tag pattern '{}' against a non-enum subject", name));
|
|
2756
2875
|
}
|
|
2757
|
-
let
|
|
2876
|
+
let variant_idx = *ctx.gc_types.variant_type_idx.get(name)
|
|
2877
|
+
.ok_or_else(|| format!("codegen: variant '{}' missing from the GC type registry", name))?;
|
|
2878
|
+
// A single ref.test against the variant's exact concrete type replaces the old
|
|
2879
|
+
// "range-check against HEAP_BASE, then conditionally load+compare a tag" dance —
|
|
2880
|
+
// there's no tag to load at all anymore, the type itself IS the discriminant.
|
|
2758
2881
|
Instruction::LocalGet(scratch_local).encode(body);
|
|
2759
|
-
if !info.field_types.is_empty() {
|
|
2760
|
-
// A payload-carrying variant's scratch local holds a heap pointer, not the
|
|
2761
|
-
// tag itself (unlike a payload-free variant, which stores the tag directly
|
|
2762
|
-
// as its runtime value) — dereference slot 0 to compare against the tag.
|
|
2763
|
-
emitLoad(ValType::I32, 0, body);
|
|
2764
|
-
}
|
|
2765
|
-
Instruction::
|
|
2882
|
+
Instruction::RefTestNonNull(HeapType::Concrete(variant_idx)).encode(body);
|
|
2766
|
-
Instruction::I32Eq.encode(body);
|
|
2767
2883
|
Instruction::If(blockTypeFor(result_vt)).encode(body);
|
|
2768
2884
|
compileCasePositions(case, pos + 1, all_subjects, rest, result_vt, exhaustive_fallback, body, ctx, state)?;
|
|
2769
2885
|
Instruction::Else.encode(body);
|
|
@@ -2772,8 +2888,10 @@ fn compileVariantEqArm(
|
|
|
2772
2888
|
Ok(())
|
|
2773
2889
|
}
|
|
2774
2890
|
|
|
2891
|
+
#[allow(clippy::too_many_arguments)]
|
|
2775
2892
|
#[allow(clippy::too_many_arguments)]
|
|
2776
2893
|
fn compileVariantConstructorArm(
|
|
2894
|
+
pat: &ast::CasePattern,
|
|
2777
2895
|
name: &str,
|
|
2778
2896
|
fields: &[ast::CasePattern],
|
|
2779
2897
|
subject_vt: ValType,
|
|
@@ -2792,7 +2910,7 @@ fn compileVariantConstructorArm(
|
|
|
2792
2910
|
.enum_variants
|
|
2793
2911
|
.get(name)
|
|
2794
2912
|
.ok_or_else(|| format!("codegen: unknown enum variant '{}'", name))?;
|
|
2795
|
-
if subject_vt
|
|
2913
|
+
if !matches!(subject_vt, ValType::Ref(_)) {
|
|
2796
2914
|
return Err(format!("codegen: constructor pattern '{}' against a non-enum subject", name));
|
|
2797
2915
|
}
|
|
2798
2916
|
if fields.len() != info.field_types.len() {
|
|
@@ -2801,28 +2919,33 @@ fn compileVariantConstructorArm(
|
|
|
2801
2919
|
name, info.field_types.len(), fields.len()
|
|
2802
2920
|
));
|
|
2803
2921
|
}
|
|
2804
|
-
let
|
|
2922
|
+
let variant_idx = *ctx.gc_types.variant_type_idx.get(name)
|
|
2923
|
+
.ok_or_else(|| format!("codegen: variant '{}' missing from the GC type registry", name))?;
|
|
2805
2924
|
let field_types = info.field_types.clone();
|
|
2806
2925
|
|
|
2807
|
-
//
|
|
2926
|
+
// This pattern's own narrowly-typed scratch local (declared with variant's exact
|
|
2808
|
-
// a heap pointer (payload variants are always >= HEAP_BASE); a
|
|
2809
|
-
//
|
|
2927
|
+
// concrete ref type — see `nested_class_scratch_types`), distinct from
|
|
2928
|
+
// `scratch_local` (which stays declared at the subject's wide supertype type for
|
|
2810
|
-
//
|
|
2929
|
+
// the whole function). `struct.get` on the fields below requires this narrowed
|
|
2930
|
+
// static type; the old bump-allocator version needed no such narrowing since
|
|
2811
|
-
//
|
|
2931
|
+
// every heap reference was a uniformly-typed, untyped-at-the-wasm-level i32.
|
|
2932
|
+
let narrow_key = pat as *const ast::CasePattern as usize;
|
|
2812
|
-
|
|
2933
|
+
let narrow_slot = *ctx.nested_class_scratch.get(&narrow_key)
|
|
2813
|
-
|
|
2934
|
+
.ok_or_else(|| "internal codegen error: missing constructor-pattern scratch slot".to_string())?;
|
|
2814
|
-
Instruction::I32GeU.encode(body);
|
|
2815
|
-
|
|
2935
|
+
let narrow_local = ctx.nested_class_scratch_base + narrow_slot;
|
|
2936
|
+
|
|
2937
|
+
// A single ref.test against the variant's exact concrete type replaces the old
|
|
2938
|
+
// "range-check against HEAP_BASE, then conditionally load+compare a tag" dance.
|
|
2816
2939
|
Instruction::LocalGet(scratch_local).encode(body);
|
|
2817
|
-
Instruction::
|
|
2940
|
+
Instruction::RefTestNonNull(HeapType::Concrete(variant_idx)).encode(body);
|
|
2818
|
-
Instruction::I32Const(tag).encode(body);
|
|
2819
|
-
Instruction::I32Eq.encode(body);
|
|
2820
|
-
Instruction::Else.encode(body);
|
|
2821
|
-
Instruction::I32Const(0).encode(body);
|
|
2822
|
-
Instruction::End.encode(body);
|
|
2823
2941
|
Instruction::If(blockTypeFor(result_vt)).encode(body);
|
|
2942
|
+
// Narrow the subject down to this variant's concrete type before destructuring
|
|
2943
|
+
// its fields — valid here specifically because the ref.test just above proved it.
|
|
2944
|
+
Instruction::LocalGet(scratch_local).encode(body);
|
|
2945
|
+
Instruction::RefCastNonNull(HeapType::Concrete(variant_idx)).encode(body);
|
|
2946
|
+
Instruction::LocalSet(narrow_local).encode(body);
|
|
2824
2947
|
compileFieldPatterns(
|
|
2825
|
-
fields, &field_types, 0,
|
|
2948
|
+
fields, &field_types, 0, narrow_local, variant_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state,
|
|
2826
2949
|
&mut |body, state| compileCasePositions(case, pos + 1, all_subjects, rest, result_vt, exhaustive_fallback, body, ctx, state),
|
|
2827
2950
|
)?;
|
|
2828
2951
|
Instruction::Else.encode(body);
|
|
@@ -2838,11 +2961,13 @@ fn compileVariantConstructorArm(
|
|
|
2838
2961
|
/// position). A mismatch at any field — at any nesting depth — falls through to
|
|
2839
2962
|
/// `compileMatchArmsMulti(rest, ...)`, exactly like a top-level pattern mismatch.
|
|
2840
2963
|
#[allow(clippy::too_many_arguments)]
|
|
2964
|
+
#[allow(clippy::too_many_arguments)]
|
|
2841
2965
|
fn compileFieldPatterns(
|
|
2842
2966
|
fields: &[ast::CasePattern],
|
|
2843
2967
|
field_types: &[PlumType],
|
|
2844
2968
|
fpos: usize,
|
|
2845
2969
|
container_local: u32,
|
|
2970
|
+
container_type_idx: u32,
|
|
2846
2971
|
rest: &[ast::Case],
|
|
2847
2972
|
all_subjects: &[(ValType, u32)],
|
|
2848
2973
|
result_vt: Option<ValType>,
|
|
@@ -2857,12 +2982,10 @@ fn compileFieldPatterns(
|
|
|
2857
2982
|
}
|
|
2858
2983
|
let pat = &fields[fpos];
|
|
2859
2984
|
let field_ty = &field_types[fpos];
|
|
2860
|
-
let field_vt = plumTypeToValtype(field_ty);
|
|
2861
|
-
let offset = ((fpos + 1) as u64) * 8;
|
|
2862
2985
|
|
|
2863
2986
|
match pat {
|
|
2864
2987
|
ast::CasePattern::Wildcard => {
|
|
2865
|
-
compileFieldPatterns(fields, field_types, fpos + 1, container_local, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)
|
|
2988
|
+
compileFieldPatterns(fields, field_types, fpos + 1, container_local, container_type_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)
|
|
2866
2989
|
}
|
|
2867
2990
|
ast::CasePattern::Name(n) if !(n.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) && ctx.enum_variants.contains_key(n)) => {
|
|
2868
2991
|
// A plain binding always matches this field; load it straight into its
|
|
@@ -2873,47 +2996,42 @@ fn compileFieldPatterns(
|
|
|
2873
2996
|
.copied()
|
|
2874
2997
|
.ok_or_else(|| format!("internal codegen error: missing binding local '{}'", n))?;
|
|
2875
2998
|
Instruction::LocalGet(container_local).encode(body);
|
|
2876
|
-
|
|
2999
|
+
Instruction::StructGet { struct_type_index: container_type_idx, field_index: fpos as u32 }.encode(body);
|
|
2877
3000
|
Instruction::LocalSet(idx).encode(body);
|
|
2878
3001
|
ctx.type_env.borrow_mut().insert(n.to_string(), TypeScheme::mono(field_ty.clone()));
|
|
2879
|
-
compileFieldPatterns(fields, field_types, fpos + 1, container_local, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)
|
|
3002
|
+
compileFieldPatterns(fields, field_types, fpos + 1, container_local, container_type_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)
|
|
2880
3003
|
}
|
|
2881
3004
|
ast::CasePattern::Name(n) => {
|
|
2882
3005
|
// An uppercase, payload-free variant name used as a field pattern (e.g.
|
|
2883
3006
|
// matching a nested `None` rather than binding a name to it).
|
|
2884
|
-
let info = ctx
|
|
2885
|
-
|
|
3007
|
+
ctx.enum_variants
|
|
2886
3008
|
.get(n)
|
|
2887
3009
|
.ok_or_else(|| format!("codegen: unknown enum variant '{}'", n))?;
|
|
2888
|
-
if
|
|
3010
|
+
if !matches!(plumTypeToValtype(field_ty), ValType::Ref(_)) {
|
|
2889
3011
|
return Err(format!("codegen: enum tag pattern '{}' against a non-enum field", n));
|
|
2890
3012
|
}
|
|
3013
|
+
let variant_idx = *ctx.gc_types.variant_type_idx.get(n)
|
|
3014
|
+
.ok_or_else(|| format!("codegen: variant '{}' missing from the GC type registry", n))?;
|
|
2891
3015
|
Instruction::LocalGet(container_local).encode(body);
|
|
2892
|
-
emitLoad(field_vt, offset, body);
|
|
2893
|
-
if !info.field_types.is_empty() {
|
|
2894
|
-
// Payload-carrying nested variant: the just-loaded field is a heap
|
|
2895
|
-
|
|
3016
|
+
Instruction::StructGet { struct_type_index: container_type_idx, field_index: fpos as u32 }.encode(body);
|
|
2896
|
-
emitLoad(ValType::I32, 0, body);
|
|
2897
|
-
}
|
|
2898
|
-
Instruction::
|
|
3017
|
+
Instruction::RefTestNonNull(HeapType::Concrete(variant_idx)).encode(body);
|
|
2899
|
-
Instruction::I32Eq.encode(body);
|
|
2900
3018
|
Instruction::If(blockTypeFor(result_vt)).encode(body);
|
|
2901
|
-
compileFieldPatterns(fields, field_types, fpos + 1, container_local, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)?;
|
|
3019
|
+
compileFieldPatterns(fields, field_types, fpos + 1, container_local, container_type_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)?;
|
|
2902
3020
|
Instruction::Else.encode(body);
|
|
2903
3021
|
compileMatchArmsMulti(rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state)?;
|
|
2904
3022
|
Instruction::End.encode(body);
|
|
2905
3023
|
Ok(())
|
|
2906
3024
|
}
|
|
2907
3025
|
ast::CasePattern::Int(n) => {
|
|
2908
|
-
if
|
|
3026
|
+
if plumTypeToValtype(field_ty) != ValType::I64 {
|
|
2909
3027
|
return Err("codegen: integer match pattern against a non-Int field".to_string());
|
|
2910
3028
|
}
|
|
2911
3029
|
Instruction::LocalGet(container_local).encode(body);
|
|
2912
|
-
|
|
3030
|
+
Instruction::StructGet { struct_type_index: container_type_idx, field_index: fpos as u32 }.encode(body);
|
|
2913
3031
|
Instruction::I64Const(*n).encode(body);
|
|
2914
3032
|
Instruction::I64Eq.encode(body);
|
|
2915
3033
|
Instruction::If(blockTypeFor(result_vt)).encode(body);
|
|
2916
|
-
compileFieldPatterns(fields, field_types, fpos + 1, container_local, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)?;
|
|
3034
|
+
compileFieldPatterns(fields, field_types, fpos + 1, container_local, container_type_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)?;
|
|
2917
3035
|
Instruction::Else.encode(body);
|
|
2918
3036
|
compileMatchArmsMulti(rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state)?;
|
|
2919
3037
|
Instruction::End.encode(body);
|
|
@@ -2926,7 +3044,7 @@ fn compileFieldPatterns(
|
|
|
2926
3044
|
.enum_variants
|
|
2927
3045
|
.get(name)
|
|
2928
3046
|
.ok_or_else(|| format!("codegen: unknown enum variant '{}'", name))?;
|
|
2929
|
-
if
|
|
3047
|
+
if !matches!(plumTypeToValtype(field_ty), ValType::Ref(_)) {
|
|
2930
3048
|
return Err(format!("codegen: constructor pattern '{}' against a non-enum field", name));
|
|
2931
3049
|
}
|
|
2932
3050
|
if inner_fields.len() != info.field_types.len() {
|
|
@@ -2935,35 +3053,27 @@ fn compileFieldPatterns(
|
|
|
2935
3053
|
name, info.field_types.len(), inner_fields.len()
|
|
2936
3054
|
));
|
|
2937
3055
|
}
|
|
3056
|
+
let inner_variant_idx = *ctx.gc_types.variant_type_idx.get(name)
|
|
3057
|
+
.ok_or_else(|| format!("codegen: variant '{}' missing from the GC type registry", name))?;
|
|
2938
3058
|
let key = pat as *const ast::CasePattern as usize;
|
|
2939
3059
|
let slot = *ctx
|
|
2940
3060
|
.nested_class_scratch
|
|
2941
3061
|
.get(&key)
|
|
2942
3062
|
.ok_or_else(|| "internal codegen error: missing nested constructor pattern scratch slot".to_string())?;
|
|
2943
3063
|
let nested_local = ctx.nested_class_scratch_base + slot;
|
|
2944
|
-
let tag = info.tag;
|
|
2945
3064
|
let inner_field_types = info.field_types.clone();
|
|
2946
3065
|
|
|
2947
3066
|
Instruction::LocalGet(container_local).encode(body);
|
|
2948
|
-
emitLoad(field_vt, offset, body);
|
|
2949
|
-
Instruction::LocalSet(nested_local).encode(body);
|
|
2950
|
-
|
|
2951
|
-
// Same heap-pointer guard as the top-level constructor check: a
|
|
2952
|
-
// payload-free sibling variant is a small int tag, not a pointer.
|
|
2953
|
-
Instruction::LocalGet(nested_local).encode(body);
|
|
2954
|
-
Instruction::
|
|
3067
|
+
Instruction::StructGet { struct_type_index: container_type_idx, field_index: fpos as u32 }.encode(body);
|
|
2955
|
-
Instruction::I32GeU.encode(body);
|
|
2956
|
-
Instruction::
|
|
3068
|
+
Instruction::RefTestNonNull(HeapType::Concrete(inner_variant_idx)).encode(body);
|
|
2957
|
-
Instruction::LocalGet(nested_local).encode(body);
|
|
2958
|
-
Instruction::I32Load(MemArg { offset: 0, align: 2, memory_index: 0 }).encode(body);
|
|
2959
|
-
Instruction::I32Const(tag).encode(body);
|
|
2960
|
-
Instruction::I32Eq.encode(body);
|
|
2961
|
-
Instruction::Else.encode(body);
|
|
2962
|
-
Instruction::I32Const(0).encode(body);
|
|
2963
|
-
Instruction::End.encode(body);
|
|
2964
3069
|
Instruction::If(blockTypeFor(result_vt)).encode(body);
|
|
3070
|
+
// Narrow into `nested_local` now that ref.test just above proved it's safe.
|
|
3071
|
+
Instruction::LocalGet(container_local).encode(body);
|
|
3072
|
+
Instruction::StructGet { struct_type_index: container_type_idx, field_index: fpos as u32 }.encode(body);
|
|
3073
|
+
Instruction::RefCastNonNull(HeapType::Concrete(inner_variant_idx)).encode(body);
|
|
3074
|
+
Instruction::LocalSet(nested_local).encode(body);
|
|
2965
|
-
compileFieldPatterns(inner_fields, &inner_field_types, 0, nested_local, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, &mut |body, state| {
|
|
3075
|
+
compileFieldPatterns(inner_fields, &inner_field_types, 0, nested_local, inner_variant_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, &mut |body, state| {
|
|
2966
|
-
compileFieldPatterns(fields, field_types, fpos + 1, container_local, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)
|
|
3076
|
+
compileFieldPatterns(fields, field_types, fpos + 1, container_local, container_type_idx, rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state, on_match)
|
|
2967
3077
|
})?;
|
|
2968
3078
|
Instruction::Else.encode(body);
|
|
2969
3079
|
compileMatchArmsMulti(rest, all_subjects, result_vt, exhaustive_fallback, body, ctx, state)?;
|
|
@@ -2994,13 +3104,13 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
2994
3104
|
Some(idx) => Instruction::LocalGet(*idx).encode(body),
|
|
2995
3105
|
// Not a local: a bare reference to a top-level function used as a
|
|
2996
3106
|
// value (e.g. `each(double)`) — push its zero-capture trampoline
|
|
2997
|
-
// closure's (
|
|
3107
|
+
// closure's (pre-built by the `start` function) global.
|
|
2998
3108
|
None => {
|
|
2999
|
-
let
|
|
3109
|
+
let global_idx = *ctx
|
|
3000
3110
|
.named_fn_values
|
|
3001
3111
|
.get(name.as_str())
|
|
3002
3112
|
.ok_or_else(|| format!("undeclared variable '{}'", name))?;
|
|
3003
|
-
Instruction::
|
|
3113
|
+
Instruction::GlobalGet(global_idx).encode(body);
|
|
3004
3114
|
}
|
|
3005
3115
|
}
|
|
3006
3116
|
}
|
|
@@ -3044,16 +3154,18 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3044
3154
|
}
|
|
3045
3155
|
}
|
|
3046
3156
|
ast::Expr::Bool(b) => {
|
|
3047
|
-
|
|
3157
|
+
compileBoolConditionAsI32(&b.left, body, ctx, state)?;
|
|
3048
|
-
|
|
3158
|
+
compileBoolConditionAsI32(&b.right, body, ctx, state)?;
|
|
3049
3159
|
match b.op {
|
|
3050
3160
|
ast::BoolOp::And => Instruction::I32And.encode(body),
|
|
3051
3161
|
ast::BoolOp::Or => Instruction::I32Or.encode(body),
|
|
3052
|
-
}
|
|
3162
|
+
};
|
|
3163
|
+
pushBoolRefFromI32Flag(body, ctx);
|
|
3053
3164
|
}
|
|
3054
3165
|
ast::Expr::Not(inner) => {
|
|
3055
|
-
|
|
3166
|
+
compileBoolConditionAsI32(inner, body, ctx, state)?;
|
|
3056
3167
|
Instruction::I32Eqz.encode(body);
|
|
3168
|
+
pushBoolRefFromI32Flag(body, ctx);
|
|
3057
3169
|
}
|
|
3058
3170
|
ast::Expr::Compare(c) => {
|
|
3059
3171
|
let is_float = matches!(inferLocalType(&c.left, ctx), PlumType::TFloat);
|
|
@@ -3080,10 +3192,11 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3080
3192
|
}
|
|
3081
3193
|
.encode(body);
|
|
3082
3194
|
}
|
|
3195
|
+
pushBoolRefFromI32Flag(body, ctx);
|
|
3083
3196
|
}
|
|
3084
3197
|
ast::Expr::Ternary(t) => {
|
|
3085
3198
|
let result_vt = plumTypeToValtype(&inferLocalType(&t.then, ctx));
|
|
3086
|
-
|
|
3199
|
+
compileBoolConditionAsI32(&t.condition, body, ctx, state)?;
|
|
3087
3200
|
Instruction::If(BlockType::Result(result_vt)).encode(body);
|
|
3088
3201
|
compileExpr(&t.then, body, ctx, state)?;
|
|
3089
3202
|
Instruction::Else.encode(body);
|
|
@@ -3121,34 +3234,16 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3121
3234
|
compileExpr(argExprOf(arg), body, ctx, state)?;
|
|
3122
3235
|
}
|
|
3123
3236
|
let trailing: Vec<&ast::Expr> = call.args.iter().skip(fixed_count).map(argExprOf).collect();
|
|
3124
|
-
let count = trailing.len() as i32;
|
|
3125
|
-
let size = 8 * (count + 1);
|
|
3126
|
-
|
|
3127
|
-
let scratch_key = expr as *const ast::Expr as usize;
|
|
3128
|
-
let scratch_idx = *ctx
|
|
3129
|
-
.classcall_scratch
|
|
3130
|
-
.get(&scratch_key)
|
|
3131
|
-
.ok_or_else(|| "internal codegen error: missing variadic-call scratch slot".to_string())?;
|
|
3132
|
-
let scratch_local = ctx.classcall_scratch_base + scratch_idx;
|
|
3133
|
-
|
|
3134
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3135
|
-
Instruction::LocalSet(scratch_local).encode(body);
|
|
3136
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3137
|
-
Instruction::I32Const(size).encode(body);
|
|
3138
|
-
Instruction::I32Add.encode(body);
|
|
3139
|
-
Instruction::GlobalSet(ctx.bump_global).encode(body);
|
|
3140
|
-
|
|
3141
|
-
Instruction::LocalGet(scratch_local).encode(body);
|
|
3142
|
-
Instruction::I64Const(count as i64).encode(body);
|
|
3143
|
-
emitStore(ValType::I64, 0, body);
|
|
3144
|
-
|
|
3145
3237
|
let elem_vt = plumTypeToValtype(&elem_ty);
|
|
3238
|
+
let array_type_idx = *ctx
|
|
3239
|
+
.gc_types
|
|
3240
|
+
.variadic_array_type_idx
|
|
3241
|
+
.get(&elem_vt)
|
|
3242
|
+
.ok_or_else(|| "internal codegen error: no variadic array type registered for this elem type".to_string())?;
|
|
3146
|
-
for
|
|
3243
|
+
for arg_expr in &trailing {
|
|
3147
|
-
Instruction::LocalGet(scratch_local).encode(body);
|
|
3148
3244
|
compileExpr(arg_expr, body, ctx, state)?;
|
|
3149
|
-
emitStore(elem_vt, 8 * (i as u64 + 1), body);
|
|
3150
3245
|
}
|
|
3151
|
-
Instruction::
|
|
3246
|
+
Instruction::ArrayNewFixed { array_type_index: array_type_idx, array_size: trailing.len() as u32 }.encode(body);
|
|
3152
3247
|
|
|
3153
3248
|
let func_idx = ctx
|
|
3154
3249
|
.func_ids
|
|
@@ -3179,7 +3274,9 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3179
3274
|
}
|
|
3180
3275
|
ast::Expr::TypeName(n) => match ctx.enum_variants.get(n) {
|
|
3181
3276
|
Some(info) if info.field_types.is_empty() => {
|
|
3277
|
+
let global_idx = *ctx.singleton_globals.get(n)
|
|
3278
|
+
.unwrap_or_else(|| panic!("internal codegen error: payload-free variant '{}' has no singleton global", n));
|
|
3182
|
-
Instruction::
|
|
3279
|
+
Instruction::GlobalGet(global_idx).encode(body);
|
|
3183
3280
|
}
|
|
3184
3281
|
Some(info) if !info.values.is_empty() => {
|
|
3185
3282
|
// A discriminant variant's own declared literal values ARE its
|
|
@@ -3195,45 +3292,24 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3195
3292
|
None => return Err(format!("codegen: type name '{}' is not yet supported as a value", n)),
|
|
3196
3293
|
},
|
|
3197
3294
|
ast::Expr::ClassCall(call) => {
|
|
3295
|
+
// struct.new needs every field value pushed in DECLARATION order (not
|
|
3296
|
+
// `call.fields`'s written order) immediately before the single
|
|
3297
|
+
// construction instruction — no intermediate scratch pointer needed at
|
|
3298
|
+
// all, unlike the old bump-pointer-then-store approach.
|
|
3198
3299
|
let fields = ctx
|
|
3199
3300
|
.classes
|
|
3200
3301
|
.get(&call.type_name)
|
|
3201
3302
|
.ok_or_else(|| format!("codegen: unknown class '{}'", call.type_name))?
|
|
3202
3303
|
.clone();
|
|
3203
|
-
let size = (fields.len() as i32) * 8;
|
|
3204
|
-
|
|
3205
|
-
let
|
|
3304
|
+
let class_type_idx = *ctx.gc_types.class_type_idx.get(&call.type_name)
|
|
3206
|
-
let scratch_idx = *ctx
|
|
3207
|
-
.classcall_scratch
|
|
3208
|
-
.get(&scratch_key)
|
|
3209
|
-
.ok_or_else(|| "
|
|
3305
|
+
.ok_or_else(|| format!("codegen: class '{}' missing from the GC type registry", call.type_name))?;
|
|
3210
|
-
let scratch_local = ctx.classcall_scratch_base + scratch_idx;
|
|
3211
|
-
|
|
3212
|
-
// base = bump_global; bump_global += size (runtime bump allocation, so every
|
|
3213
|
-
// call — even inside a loop — gets a fresh, non-overlapping region).
|
|
3214
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3215
|
-
Instruction::LocalSet(scratch_local).encode(body);
|
|
3216
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3217
|
-
Instruction::I32Const(size).encode(body);
|
|
3218
|
-
Instruction::I32Add.encode(body);
|
|
3219
|
-
Instruction::GlobalSet(ctx.bump_global).encode(body);
|
|
3220
3306
|
|
|
3221
|
-
for
|
|
3307
|
+
for (field_name, _) in &fields {
|
|
3222
|
-
let
|
|
3308
|
+
let fa = call.fields.iter().find(|fa| &fa.name == field_name)
|
|
3223
|
-
.iter()
|
|
3224
|
-
.position(|(n, _)| n == &fa.name)
|
|
3225
|
-
.map(|i| (i, fields[i].1.clone()))
|
|
3226
|
-
.ok_or_else(|| format!("codegen:
|
|
3309
|
+
.ok_or_else(|| format!("codegen: class '{}' missing field '{}'", call.type_name, field_name))?;
|
|
3227
|
-
Instruction::LocalGet(scratch_local).encode(body);
|
|
3228
3310
|
compileExpr(&fa.value, body, ctx, state)?;
|
|
3229
|
-
let offset = (field_idx as u64) * 8;
|
|
3230
|
-
match plumTypeToValtype(&field_ty) {
|
|
3231
|
-
ValType::I64 => Instruction::I64Store(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
3232
|
-
ValType::F64 => Instruction::F64Store(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
3233
|
-
_ => Instruction::I32Store(MemArg { offset, align: 2, memory_index: 0 }).encode(body),
|
|
3234
|
-
};
|
|
3235
3311
|
}
|
|
3236
|
-
Instruction::
|
|
3312
|
+
Instruction::StructNew(class_type_idx).encode(body);
|
|
3237
3313
|
}
|
|
3238
3314
|
ast::Expr::Attribute(attr) => {
|
|
3239
3315
|
let obj_ty = inferLocalType(&attr.object, ctx);
|
|
@@ -3245,39 +3321,33 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3245
3321
|
};
|
|
3246
3322
|
match ctx.classes.get(&class_name) {
|
|
3247
3323
|
Some(fields) => {
|
|
3248
|
-
let
|
|
3324
|
+
let field_idx = fields
|
|
3249
3325
|
.iter()
|
|
3250
3326
|
.position(|(n, _)| n == field_name)
|
|
3251
|
-
.map(|i| (i, fields[i].1.clone()))
|
|
3252
3327
|
.ok_or_else(|| format!("codegen: no field '{}' on class '{}'", field_name, class_name))?;
|
|
3328
|
+
let class_type_idx = *ctx.gc_types.class_type_idx.get(&class_name)
|
|
3329
|
+
.ok_or_else(|| format!("codegen: class '{}' missing from the GC type registry", class_name))?;
|
|
3253
3330
|
compileExpr(&attr.object, body, ctx, state)?;
|
|
3254
|
-
let offset = (field_idx as u64) * 8;
|
|
3255
|
-
match plumTypeToValtype(&field_ty) {
|
|
3256
|
-
|
|
3331
|
+
Instruction::StructGet { struct_type_index: class_type_idx, field_index: field_idx as u32 }.encode(body);
|
|
3257
|
-
ValType::F64 => Instruction::F64Load(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
3258
|
-
_ => Instruction::I32Load(MemArg { offset, align: 2, memory_index: 0 }).encode(body),
|
|
3259
|
-
};
|
|
3260
3332
|
}
|
|
3261
|
-
// Not a class: fall back to a discriminant enum's shared params
|
|
3333
|
+
// Not a class: fall back to a discriminant enum's shared params,
|
|
3334
|
+
// declared directly on the enum's SUPERTYPE (see
|
|
3335
|
+
// `buildGcTypeRegistry`'s `EnumSuper` arm) — no `ref.cast` to any
|
|
3262
|
-
//
|
|
3336
|
+
// particular variant needed, since every variant has the exact
|
|
3263
|
-
//
|
|
3337
|
+
// same field list as the supertype itself.
|
|
3264
3338
|
None => {
|
|
3265
3339
|
let params = ctx
|
|
3266
3340
|
.enum_params
|
|
3267
3341
|
.get(&class_name)
|
|
3268
3342
|
.ok_or_else(|| format!("codegen: unknown class '{}'", class_name))?;
|
|
3269
|
-
let
|
|
3343
|
+
let field_idx = params
|
|
3270
3344
|
.iter()
|
|
3271
3345
|
.position(|(n, _)| n == field_name)
|
|
3272
|
-
.map(|i| (i, params[i].1.clone()))
|
|
3273
3346
|
.ok_or_else(|| format!("codegen: no field '{}' on enum '{}'", field_name, class_name))?;
|
|
3347
|
+
let super_type_idx = *ctx.gc_types.enum_super_type_idx.get(&class_name)
|
|
3348
|
+
.ok_or_else(|| format!("codegen: enum '{}' missing from the GC type registry", class_name))?;
|
|
3274
3349
|
compileExpr(&attr.object, body, ctx, state)?;
|
|
3275
|
-
let offset = ((field_idx + 1) as u64) * 8;
|
|
3276
|
-
match plumTypeToValtype(&field_ty) {
|
|
3277
|
-
|
|
3350
|
+
Instruction::StructGet { struct_type_index: super_type_idx, field_index: field_idx as u32 }.encode(body);
|
|
3278
|
-
ValType::F64 => Instruction::F64Load(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
3279
|
-
_ => Instruction::I32Load(MemArg { offset, align: 2, memory_index: 0 }).encode(body),
|
|
3280
|
-
};
|
|
3281
3351
|
}
|
|
3282
3352
|
}
|
|
3283
3353
|
}
|
|
@@ -3328,13 +3398,13 @@ fn compileExpr(expr: &ast::Expr, body: &mut Vec<u8>, ctx: &LocalCtx, state: &mut
|
|
|
3328
3398
|
}
|
|
3329
3399
|
|
|
3330
3400
|
/// Compiles a variant-construction call. A payload-free variant (`None`, called as
|
|
3331
|
-
/// `None()` rather than used bare) is
|
|
3401
|
+
/// `None()` rather than used bare) is its pre-allocated singleton global. A payload
|
|
3332
|
-
///
|
|
3402
|
+
/// variant pushes its field values in order and does `struct.new` into its own
|
|
3333
|
-
///
|
|
3403
|
+
/// concrete variant type.
|
|
3334
3404
|
fn compileVariantConstruction(
|
|
3335
3405
|
info: &EnumVariantInfo,
|
|
3336
3406
|
call: &ast::FnCall,
|
|
3337
|
-
|
|
3407
|
+
_expr: &ast::Expr,
|
|
3338
3408
|
body: &mut Vec<u8>,
|
|
3339
3409
|
ctx: &LocalCtx,
|
|
3340
3410
|
state: &mut ModuleState,
|
|
@@ -3345,78 +3415,45 @@ fn compileVariantConstruction(
|
|
|
3345
3415
|
call.name, info.field_types.len(), call.args.len()
|
|
3346
3416
|
));
|
|
3347
3417
|
}
|
|
3418
|
+
let variant_type_idx = *ctx.gc_types.variant_type_idx.get(&call.name)
|
|
3419
|
+
.ok_or_else(|| format!("codegen: variant '{}' missing from the GC type registry", call.name))?;
|
|
3348
3420
|
if info.field_types.is_empty() {
|
|
3421
|
+
// Pre-allocated singleton (this migration plan's Decision 2) — not a fresh
|
|
3422
|
+
// struct.new per reference.
|
|
3423
|
+
let global_idx = *ctx.singleton_globals.get(&call.name)
|
|
3424
|
+
.ok_or_else(|| format!("codegen: payload-free variant '{}' has no singleton global", call.name))?;
|
|
3349
|
-
Instruction::
|
|
3425
|
+
Instruction::GlobalGet(global_idx).encode(body);
|
|
3350
3426
|
return Ok(());
|
|
3351
3427
|
}
|
|
3352
3428
|
|
|
3429
|
+
// struct.new needs every field value pushed, in order, immediately before the
|
|
3353
|
-
|
|
3430
|
+
// single construction instruction — positional args already match field
|
|
3354
|
-
|
|
3431
|
+
// declaration order (unlike named class-field construction), so no reordering
|
|
3355
|
-
|
|
3432
|
+
// is needed here.
|
|
3356
|
-
.classcall_scratch
|
|
3357
|
-
|
|
3433
|
+
for arg in &call.args {
|
|
3358
|
-
.ok_or_else(|| "internal codegen error: missing variant-call scratch slot".to_string())?;
|
|
3359
|
-
let scratch_local = ctx.classcall_scratch_base + scratch_idx;
|
|
3360
|
-
|
|
3361
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3362
|
-
Instruction::LocalSet(scratch_local).encode(body);
|
|
3363
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3364
|
-
Instruction::I32Const(size).encode(body);
|
|
3365
|
-
Instruction::I32Add.encode(body);
|
|
3366
|
-
Instruction::GlobalSet(ctx.bump_global).encode(body);
|
|
3367
|
-
|
|
3368
|
-
Instruction::LocalGet(scratch_local).encode(body);
|
|
3369
|
-
Instruction::I32Const(info.tag).encode(body);
|
|
3370
|
-
Instruction::I32Store(MemArg { offset: 0, align: 2, memory_index: 0 }).encode(body);
|
|
3371
|
-
|
|
3372
|
-
for (i, (arg, field_ty)) in call.args.iter().zip(info.field_types.iter()).enumerate() {
|
|
3373
3434
|
let arg_expr = match arg {
|
|
3374
3435
|
ast::Arg::Positional(e) => e,
|
|
3375
3436
|
ast::Arg::Keyword { value, .. } => value,
|
|
3376
3437
|
ast::Arg::Pair { value, .. } => value,
|
|
3377
3438
|
};
|
|
3378
|
-
Instruction::LocalGet(scratch_local).encode(body);
|
|
3379
3439
|
compileExpr(arg_expr, body, ctx, state)?;
|
|
3380
|
-
let offset = ((i + 1) as u64) * 8;
|
|
3381
|
-
match plumTypeToValtype(field_ty) {
|
|
3382
|
-
ValType::I64 => Instruction::I64Store(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
3383
|
-
ValType::F64 => Instruction::F64Store(MemArg { offset, align: 3, memory_index: 0 }).encode(body),
|
|
3384
|
-
_ => Instruction::I32Store(MemArg { offset, align: 2, memory_index: 0 }).encode(body),
|
|
3385
|
-
};
|
|
3386
3440
|
}
|
|
3387
|
-
Instruction::
|
|
3441
|
+
Instruction::StructNew(variant_type_idx).encode(body);
|
|
3388
3442
|
Ok(())
|
|
3389
3443
|
}
|
|
3390
3444
|
|
|
3391
|
-
/// Emits a width-appropriate store for a heap slot: `[addr, value]` -> memory.
|
|
3392
|
-
fn emitStore(vt: ValType, offset: u64, body: &mut Vec<u8>) {
|
|
3393
|
-
match vt {
|
|
3394
|
-
ValType::I64 => Instruction::I64Store(MemArg { offset, align: 3, memory_index: 0 }),
|
|
3395
|
-
ValType::F64 => Instruction::F64Store(MemArg { offset, align: 3, memory_index: 0 }),
|
|
3396
|
-
_ => Instruction::I32Store(MemArg { offset, align: 2, memory_index: 0 }),
|
|
3397
|
-
}.encode(body);
|
|
3398
|
-
}
|
|
3399
|
-
|
|
3400
|
-
/// Emits a width-appropriate load for a heap slot: `[addr]` -> value.
|
|
3401
|
-
fn emitLoad(vt: ValType, offset: u64, body: &mut Vec<u8>) {
|
|
3402
|
-
match vt {
|
|
3403
|
-
ValType::I64 => Instruction::I64Load(MemArg { offset, align: 3, memory_index: 0 }),
|
|
3404
|
-
ValType::F64 => Instruction::F64Load(MemArg { offset, align: 3, memory_index: 0 }),
|
|
3405
|
-
_ => Instruction::I32Load(MemArg { offset, align: 2, memory_index: 0 }),
|
|
3406
|
-
}.encode(body);
|
|
3407
|
-
}
|
|
3408
|
-
|
|
3409
|
-
/// Emits a static (compile-time-known)
|
|
3445
|
+
/// Emits a static (compile-time-known) string literal as a fresh passive data
|
|
3410
|
-
/// segment, pushing
|
|
3446
|
+
/// segment, pushing a `Str` array built from it via `array.new_data`. `Str`'s wasm-gc
|
|
3447
|
+
/// representation is a plain `array<i8>` (see Decision 4 of the wasm-gc migration
|
|
3448
|
+
/// plan) — no length prefix needed, unlike the old bump-allocator layout.
|
|
3411
3449
|
fn compileStaticString(text: &str, body: &mut Vec<u8>, state: &mut ModuleState) {
|
|
3412
3450
|
let bytes = text.as_bytes();
|
|
3413
|
-
let offset = state.next_string_offset;
|
|
3414
|
-
let mut data = Vec::with_capacity(4 + bytes.len());
|
|
3415
|
-
data.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
|
|
3416
|
-
data.extend_from_slice(bytes);
|
|
3417
|
-
|
|
3451
|
+
let data_index = state.passive_segments.len() as u32;
|
|
3418
|
-
state.
|
|
3452
|
+
state.passive_segments.push(bytes.to_vec());
|
|
3453
|
+
let str_type_idx = withGcTypes(|r| r.str_type_idx);
|
|
3454
|
+
Instruction::I32Const(0).encode(body);
|
|
3419
|
-
Instruction::I32Const(
|
|
3455
|
+
Instruction::I32Const(bytes.len() as i32).encode(body);
|
|
3456
|
+
Instruction::ArrayNewData { array_type_index: str_type_idx, array_data_index: data_index }.encode(body);
|
|
3420
3457
|
}
|
|
3421
3458
|
|
|
3422
3459
|
/// Lowers a string literal that contains at least one `{expr}` interpolation.
|
|
@@ -3447,8 +3484,9 @@ fn compileInterpolatedString(
|
|
|
3447
3484
|
Instruction::Call(ctx.int_to_string_func).encode(body);
|
|
3448
3485
|
}
|
|
3449
3486
|
PlumType::TBool => {
|
|
3487
|
+
let str_ref = withGcTypes(|r| gcRef(r.str_type_idx));
|
|
3450
|
-
|
|
3488
|
+
compileBoolConditionAsI32(expr, body, ctx, state)?;
|
|
3451
|
-
Instruction::If(BlockType::Result(
|
|
3489
|
+
Instruction::If(BlockType::Result(str_ref)).encode(body);
|
|
3452
3490
|
compileStaticString("True", body, state);
|
|
3453
3491
|
Instruction::Else.encode(body);
|
|
3454
3492
|
compileStaticString("False", body, state);
|
|
@@ -3474,9 +3512,10 @@ fn compileInterpolatedString(
|
|
|
3474
3512
|
Ok(())
|
|
3475
3513
|
}
|
|
3476
3514
|
|
|
3477
|
-
/// Compiles a closure *literal* at its construction site. Snapshots each captured
|
|
3515
|
+
/// Compiles a closure *literal* at its construction site. Snapshots each captured
|
|
3478
|
-
/// variable's CURRENT value into a fresh
|
|
3516
|
+
/// free variable's CURRENT value into a fresh env struct (`struct.new`), then wraps
|
|
3517
|
+
/// it with the closure's table index into the shared `{table_idx, env}` closure
|
|
3479
|
-
/// struct
|
|
3518
|
+
/// struct and leaves its ref on the stack.
|
|
3480
3519
|
fn compileClosureLiteral(
|
|
3481
3520
|
cl: &ast::Closure,
|
|
3482
3521
|
body: &mut Vec<u8>,
|
|
@@ -3488,58 +3527,29 @@ fn compileClosureLiteral(
|
|
|
3488
3527
|
.closures
|
|
3489
3528
|
.get(&key)
|
|
3490
3529
|
.ok_or_else(|| "internal codegen error: closure literal was not discovered by the discovery pre-pass".to_string())?;
|
|
3491
|
-
|
|
3530
|
+
|
|
3492
|
-
.closure_scratch
|
|
3493
|
-
.get(&key)
|
|
3494
|
-
.ok_or_else(|| "internal codegen error: missing closure scratch slot".to_string())?;
|
|
3495
|
-
let env_scratch = ctx.closure_scratch_base + scratch_off;
|
|
3496
|
-
let closure_scratch = env_scratch + 1;
|
|
3497
|
-
|
|
3498
|
-
//
|
|
3531
|
+
// Build the closure struct directly on the stack: push table_idx, then build the
|
|
3499
|
-
|
|
3532
|
+
// env struct (snapshotting each captured free variable's current value from the
|
|
3500
|
-
|
|
3533
|
+
// enclosing function's local), then wrap both into the shared closure struct.
|
|
3501
|
-
Instruction::LocalSet(env_scratch).encode(body);
|
|
3502
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3503
|
-
Instruction::I32Const(
|
|
3534
|
+
Instruction::I32Const(info.table_idx as i32).encode(body);
|
|
3504
|
-
Instruction::I32Add.encode(body);
|
|
3505
|
-
Instruction::GlobalSet(ctx.bump_global).encode(body);
|
|
3506
|
-
|
|
3507
|
-
// Snapshot each free variable's current value from the enclosing function's local.
|
|
3508
|
-
for (
|
|
3535
|
+
for (name, _) in &info.free_vars {
|
|
3509
3536
|
let local_idx = *ctx
|
|
3510
3537
|
.locals
|
|
3511
3538
|
.get(name)
|
|
3512
3539
|
.ok_or_else(|| format!("codegen: captured variable '{}' is not a local in the enclosing scope", name))?;
|
|
3513
|
-
Instruction::LocalGet(env_scratch).encode(body);
|
|
3514
3540
|
Instruction::LocalGet(local_idx).encode(body);
|
|
3515
|
-
emitStore(plumTypeToValtype(ty), (i as u64) * 8, body);
|
|
3516
3541
|
}
|
|
3517
|
-
|
|
3518
|
-
// Bump-allocate the 2-word closure struct.
|
|
3519
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3520
|
-
Instruction::LocalSet(closure_scratch).encode(body);
|
|
3521
|
-
Instruction::GlobalGet(ctx.bump_global).encode(body);
|
|
3522
|
-
Instruction::I32Const(16).encode(body);
|
|
3523
|
-
Instruction::I32Add.encode(body);
|
|
3524
|
-
Instruction::GlobalSet(ctx.bump_global).encode(body);
|
|
3525
|
-
|
|
3526
|
-
// table_index @ offset 0
|
|
3527
|
-
Instruction::LocalGet(closure_scratch).encode(body);
|
|
3528
|
-
Instruction::
|
|
3542
|
+
Instruction::StructNew(info.env_type_idx).encode(body);
|
|
3529
|
-
Instruction::I32Store(MemArg { offset: 0, align: 2, memory_index: 0 }).encode(body);
|
|
3530
|
-
// env_pointer @ offset 8
|
|
3531
|
-
Instruction::
|
|
3543
|
+
Instruction::StructNew(ctx.gc_types.closure_type_idx).encode(body);
|
|
3532
|
-
Instruction::LocalGet(env_scratch).encode(body);
|
|
3533
|
-
Instruction::I32Store(MemArg { offset: 8, align: 2, memory_index: 0 }).encode(body);
|
|
3534
|
-
|
|
3535
|
-
// The closure value is its struct pointer.
|
|
3536
|
-
Instruction::LocalGet(closure_scratch).encode(body);
|
|
3537
3544
|
Ok(())
|
|
3538
3545
|
}
|
|
3539
3546
|
|
|
3540
|
-
/// Compiles a call to a closure-typed local via `call_indirect`.
|
|
3547
|
+
/// Compiles a call to a closure-typed local via `call_indirect`. The local is
|
|
3548
|
+
/// statically typed `anyref` (closures share that placeholder type — see
|
|
3549
|
+
/// `plumTypeToValtype`), so every read of its fields `ref.cast`s down to the shared
|
|
3550
|
+
/// concrete closure struct type first. Stack order matches the closure function's
|
|
3541
|
-
///
|
|
3551
|
+
/// signature `(env, ...args)`: push the env, then each argument, then the table
|
|
3542
|
-
///
|
|
3552
|
+
/// index (the `call_indirect` operand).
|
|
3543
3553
|
fn compileClosureCall(
|
|
3544
3554
|
call: &ast::FnCall,
|
|
3545
3555
|
body: &mut Vec<u8>,
|
|
@@ -3550,6 +3560,7 @@ fn compileClosureCall(
|
|
|
3550
3560
|
.locals
|
|
3551
3561
|
.get(&call.name)
|
|
3552
3562
|
.ok_or_else(|| format!("codegen: closure '{}' is not a local", call.name))?;
|
|
3563
|
+
let closure_type_idx = ctx.gc_types.closure_type_idx;
|
|
3553
3564
|
|
|
3554
3565
|
// Prefer the exact signature recorded when this local was assigned a closure
|
|
3555
3566
|
// *literal* (see `closure_local_sigs`) — it's already correct. Otherwise (e.g.
|
|
@@ -3562,7 +3573,7 @@ fn compileClosureCall(
|
|
|
3562
3573
|
PlumType::TFun(p, r) => (p, *r),
|
|
3563
3574
|
other => return Err(format!("codegen: '{}' is not callable (type {:?})", call.name, other)),
|
|
3564
3575
|
};
|
|
3565
|
-
let mut sig_params = vec![ValType::
|
|
3576
|
+
let mut sig_params = vec![ValType::Ref(RefType::ANYREF)]; // env pointer
|
|
3566
3577
|
for p in ¶m_ptypes {
|
|
3567
3578
|
sig_params.push(plumTypeToValtype(p));
|
|
3568
3579
|
}
|
|
@@ -3578,16 +3589,18 @@ fn compileClosureCall(
|
|
|
3578
3589
|
.get(&sig_key)
|
|
3579
3590
|
.ok_or_else(|| format!("internal codegen error: no call_indirect type for closure '{}'", call.name))?;
|
|
3580
3591
|
|
|
3581
|
-
// env
|
|
3592
|
+
// env (closure struct field 1)
|
|
3582
3593
|
Instruction::LocalGet(closure_local).encode(body);
|
|
3594
|
+
Instruction::RefCastNonNull(HeapType::Concrete(closure_type_idx)).encode(body);
|
|
3583
|
-
Instruction::
|
|
3595
|
+
Instruction::StructGet { struct_type_index: closure_type_idx, field_index: 1 }.encode(body);
|
|
3584
3596
|
// real arguments
|
|
3585
3597
|
for arg in &call.args {
|
|
3586
3598
|
compileExpr(argExprOf(arg), body, ctx, state)?;
|
|
3587
3599
|
}
|
|
3588
|
-
// table index (struct
|
|
3600
|
+
// table index (closure struct field 0) — the call_indirect operand
|
|
3589
3601
|
Instruction::LocalGet(closure_local).encode(body);
|
|
3602
|
+
Instruction::RefCastNonNull(HeapType::Concrete(closure_type_idx)).encode(body);
|
|
3590
|
-
Instruction::
|
|
3603
|
+
Instruction::StructGet { struct_type_index: closure_type_idx, field_index: 0 }.encode(body);
|
|
3591
3604
|
Instruction::CallIndirect { type_index, table_index: 0 }.encode(body);
|
|
3592
3605
|
Ok(())
|
|
3593
3606
|
}
|
|
@@ -3617,12 +3630,9 @@ fn compileClosureBody(
|
|
|
3617
3630
|
cctx: checkCtxOf(&ctx.classes, &ctx.methods, &ctx.enum_variants, &ctx.enum_params),
|
|
3618
3631
|
named: Vec::new(),
|
|
3619
3632
|
named_set: Default::default(),
|
|
3620
|
-
classcall_scratch: HashMap::new(),
|
|
3621
3633
|
match_scratch: HashMap::new(),
|
|
3622
|
-
next_classcall_slot: 0,
|
|
3623
|
-
closure_scratch: HashMap::new(),
|
|
3624
|
-
next_closure_slot: 0,
|
|
3625
3634
|
nested_class_scratch: HashMap::new(),
|
|
3635
|
+
nested_class_scratch_types: Vec::new(),
|
|
3626
3636
|
next_nested_class_slot: 0,
|
|
3627
3637
|
variadic_for_scratch: HashMap::new(),
|
|
3628
3638
|
next_variadic_for_slot: 0,
|
|
@@ -3654,13 +3664,6 @@ fn compileClosureBody(
|
|
|
3654
3664
|
idx += 1;
|
|
3655
3665
|
}
|
|
3656
3666
|
|
|
3657
|
-
let classcall_scratch_base = idx;
|
|
3658
|
-
let classcall_count = collector.classcall_scratch.values().copied().max().map(|m| m + 1).unwrap_or(0);
|
|
3659
|
-
for _ in 0..classcall_count {
|
|
3660
|
-
groups.push(ValType::I32);
|
|
3661
|
-
idx += 1;
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
3667
|
let match_scratch_base = idx;
|
|
3665
3668
|
let mut match_scratch_index: HashMap<usize, u32> = HashMap::new();
|
|
3666
3669
|
for (ptr, types) in collector.match_scratch.iter() {
|
|
@@ -3672,9 +3675,14 @@ fn compileClosureBody(
|
|
|
3672
3675
|
}
|
|
3673
3676
|
|
|
3674
3677
|
let nested_class_scratch_base = idx;
|
|
3678
|
+
// Each slot is declared with its OWN concrete variant ref type (not a uniform
|
|
3679
|
+
// placeholder) — `struct.get` on a constructor-pattern match requires the local
|
|
3675
|
-
|
|
3680
|
+
// holding the narrowed (`ref.cast`) value to be statically typed as that exact
|
|
3681
|
+
// variant, and different slots very likely narrow to different variants.
|
|
3676
|
-
for
|
|
3682
|
+
for vname in &collector.nested_class_scratch_types {
|
|
3683
|
+
let variant_idx = withGcTypes(|r| *r.variant_type_idx.get(vname)
|
|
3684
|
+
.unwrap_or_else(|| panic!("internal codegen error: variant '{}' missing from the GC type registry", vname)));
|
|
3677
|
-
groups.push(
|
|
3685
|
+
groups.push(gcRef(variant_idx));
|
|
3678
3686
|
idx += 1;
|
|
3679
3687
|
}
|
|
3680
3688
|
|
|
@@ -3686,12 +3694,6 @@ fn compileClosureBody(
|
|
|
3686
3694
|
idx += 2;
|
|
3687
3695
|
}
|
|
3688
3696
|
|
|
3689
|
-
let closure_scratch_base = idx;
|
|
3690
|
-
let closure_scratch_count = collector.closure_scratch.values().copied().max().map(|m| m + 2).unwrap_or(0);
|
|
3691
|
-
for _ in 0..closure_scratch_count {
|
|
3692
|
-
groups.push(ValType::I32);
|
|
3693
|
-
}
|
|
3694
|
-
|
|
3695
3697
|
if groups.is_empty() {
|
|
3696
3698
|
body.push(0);
|
|
3697
3699
|
} else {
|
|
@@ -3702,22 +3704,20 @@ fn compileClosureBody(
|
|
|
3702
3704
|
}
|
|
3703
3705
|
}
|
|
3704
3706
|
|
|
3705
|
-
// Restore each captured free variable from the env struct (local 0
|
|
3707
|
+
// Restore each captured free variable from the env struct (local 0, statically
|
|
3708
|
+
// `anyref` — `ref.cast` down to THIS closure's own concrete env type) at entry.
|
|
3706
|
-
for (i, (name,
|
|
3709
|
+
for (i, (name, _)) in info.free_vars.iter().enumerate() {
|
|
3707
3710
|
let local_idx = *locals.get(name).expect("free var local was assigned above");
|
|
3708
3711
|
Instruction::LocalGet(0).encode(&mut body); // env pointer
|
|
3709
|
-
|
|
3712
|
+
Instruction::RefCastNonNull(HeapType::Concrete(info.env_type_idx)).encode(&mut body);
|
|
3713
|
+
Instruction::StructGet { struct_type_index: info.env_type_idx, field_index: i as u32 }.encode(&mut body);
|
|
3710
3714
|
Instruction::LocalSet(local_idx).encode(&mut body);
|
|
3711
3715
|
}
|
|
3712
3716
|
|
|
3713
3717
|
let local_ctx = LocalCtx {
|
|
3714
3718
|
locals,
|
|
3715
|
-
classcall_scratch_base,
|
|
3716
|
-
classcall_scratch: collector.classcall_scratch,
|
|
3717
3719
|
match_scratch_base,
|
|
3718
3720
|
match_scratch_index,
|
|
3719
|
-
closure_scratch_base,
|
|
3720
|
-
closure_scratch: collector.closure_scratch,
|
|
3721
3721
|
nested_class_scratch_base,
|
|
3722
3722
|
nested_class_scratch: collector.nested_class_scratch,
|
|
3723
3723
|
variadic_for_scratch_base,
|
|
@@ -3733,9 +3733,10 @@ fn compileClosureBody(
|
|
|
3733
3733
|
methods: &ctx.methods,
|
|
3734
3734
|
enum_variants: &ctx.enum_variants,
|
|
3735
3735
|
enum_params: &ctx.enum_params,
|
|
3736
|
+
gc_types: &ctx.gc_types,
|
|
3737
|
+
singleton_globals: &ctx.singleton_globals,
|
|
3736
3738
|
type_env: RefCell::new(base_env),
|
|
3737
3739
|
closure_local_sigs: RefCell::new(HashMap::new()),
|
|
3738
|
-
bump_global: ctx.bump_global,
|
|
3739
3740
|
};
|
|
3740
3741
|
|
|
3741
3742
|
compileBlockAsFnBody(&cl.body, &mut body, &local_ctx, state, info.ret_vt)?;
|
plum-wasm-codegen/tests/codegen_tests.rs
CHANGED
|
@@ -362,24 +362,34 @@ fn runMainF64(bytes: &[u8]) -> f64 {
|
|
|
362
362
|
main.call(&mut store, ()).expect("main should not trap")
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
-
/// Runs a `() -> Str`-returning `main`, reading the
|
|
365
|
+
/// Runs a `() -> Str`-returning `main`, reading the returned `array<i8>` GC value
|
|
366
|
+
/// back out byte-by-byte via wasmtime's host-side GC ref API (`Str` has no length
|
|
367
|
+
/// prefix of its own now — `array.len` is native, see Decision 4 of the wasm-gc
|
|
368
|
+
/// migration plan) — untyped `Func::call` is used because `main`'s wasm return type
|
|
366
|
-
///
|
|
369
|
+
/// is a concrete `(ref $Str)`, not one `get_typed_func` can name directly.
|
|
367
370
|
fn runMainStr(bytes: &[u8]) -> String {
|
|
368
371
|
let engine = gcEngine();
|
|
369
372
|
let module = wasmtime::Module::new(&engine, bytes).expect("module should be loadable");
|
|
370
373
|
let mut store = wasmtime::Store::new(&engine, ());
|
|
371
374
|
let instance = wasmtime::Instance::new(&mut store, &module, &[]).expect("module should instantiate");
|
|
372
|
-
let main = instance
|
|
373
|
-
.get_typed_func::<(), i32>(&mut store, "main")
|
|
374
|
-
.expect("main should have signature () -> i32");
|
|
375
|
-
let ptr = main.call(&mut store, ()).expect("main should not trap") as u32 as usize;
|
|
376
|
-
let
|
|
375
|
+
let main = instance.get_func(&mut store, "main").expect("module should export main");
|
|
377
|
-
let mut
|
|
376
|
+
let mut results = [wasmtime::Val::null_any_ref()];
|
|
378
|
-
|
|
377
|
+
main.call(&mut store, &[], &mut results).expect("main should not trap");
|
|
378
|
+
let any_ref = match &results[0] {
|
|
379
|
+
wasmtime::Val::AnyRef(Some(r)) => *r,
|
|
380
|
+
other => panic!("main should return a non-null anyref (Str), got {:?}", other),
|
|
381
|
+
};
|
|
382
|
+
let array = any_ref.unwrap_array(&store).expect("Str's returned anyref should be a GC array");
|
|
383
|
+
let len = array.len(&store).expect("array.len should succeed");
|
|
379
|
-
let
|
|
384
|
+
let mut bytes_out = Vec::with_capacity(len as usize);
|
|
380
|
-
|
|
385
|
+
for i in 0..len {
|
|
381
|
-
|
|
386
|
+
let byte = match array.get(&mut store, i).expect("array.get should succeed") {
|
|
387
|
+
wasmtime::Val::I32(b) => b as u8,
|
|
388
|
+
other => panic!("Str array element should be i32, got {:?}", other),
|
|
389
|
+
};
|
|
390
|
+
bytes_out.push(byte);
|
|
391
|
+
}
|
|
382
|
-
String::from_utf8(
|
|
392
|
+
String::from_utf8(bytes_out).expect("string bytes should be valid utf8")
|
|
383
393
|
}
|
|
384
394
|
|
|
385
395
|
#[test]
|