gofront 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +174 -403
  2. package/package.json +16 -19
  3. package/src/{codegen → backend/js}/expressions.js +265 -77
  4. package/src/{codegen → backend/js}/index.js +120 -89
  5. package/src/{codegen → backend/js}/runtime.js +102 -0
  6. package/src/{codegen → backend/js}/source-map.js +32 -0
  7. package/src/{codegen → backend/js}/statements.js +139 -64
  8. package/src/backend/js/stdlib/bits.js +32 -0
  9. package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
  10. package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
  11. package/src/{codegen → backend/js}/stdlib/index.js +6 -0
  12. package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
  13. package/src/{codegen → backend/js}/stdlib/path.js +2 -2
  14. package/src/backend/js/stdlib/shared.js +17 -0
  15. package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
  16. package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
  17. package/src/backend/js/stdlib/strconv.js +58 -0
  18. package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
  19. package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
  20. package/src/{codegen → backend/js}/stdlib/time.js +3 -3
  21. package/src/{codegen → backend/js}/templ.js +3 -3
  22. package/src/backend/wasm/boundary.js +1798 -0
  23. package/src/backend/wasm/emit-builtins.js +1103 -0
  24. package/src/backend/wasm/emit-exprs.js +1908 -0
  25. package/src/backend/wasm/emit-maps.js +1863 -0
  26. package/src/backend/wasm/emit-shared.js +519 -0
  27. package/src/backend/wasm/emit-stdlib.js +3006 -0
  28. package/src/backend/wasm/emit-stmts.js +2472 -0
  29. package/src/backend/wasm/emit.js +1376 -0
  30. package/src/backend/wasm/encode.js +937 -0
  31. package/src/backend/wasm/glue.js +144 -0
  32. package/src/backend/wasm/index.js +2790 -0
  33. package/src/backend/wasm/monomorph.js +711 -0
  34. package/src/backend/wasm/optimize.js +227 -0
  35. package/src/backend/wasm/types.js +544 -0
  36. package/src/backend/wasm/wat.js +302 -0
  37. package/src/cli-core.js +323 -75
  38. package/src/compiler.js +447 -14
  39. package/src/dev-server.js +56 -15
  40. package/src/index.js +99 -120
  41. package/src/lexer.js +18 -0
  42. package/src/lower/boxing.js +35 -0
  43. package/src/lower/captures.js +172 -0
  44. package/src/lower/embedding.js +30 -0
  45. package/src/lower/escape.js +164 -0
  46. package/src/lower/functions.js +302 -0
  47. package/src/lower/globals.js +316 -0
  48. package/src/lower/index.js +118 -0
  49. package/src/lower/ownership.js +137 -0
  50. package/src/lower/range.js +67 -0
  51. package/src/lower/walk.js +25 -0
  52. package/src/minifier.js +2 -0
  53. package/src/parser/index.js +8 -3
  54. package/src/resolver.js +3 -1
  55. package/src/test-runner.js +94 -20
  56. package/src/typechecker/assignability.js +42 -1
  57. package/src/typechecker/expressions.js +182 -3
  58. package/src/typechecker/index.js +261 -5
  59. package/src/typechecker/resolve.js +35 -6
  60. package/src/typechecker/stdlib/core.js +34 -19
  61. package/src/typechecker/stdlib/extended.js +18 -3
  62. package/src/typechecker/stdlib/shared.js +53 -0
  63. package/src/typechecker/stdlib.js +2 -0
  64. package/src/typechecker/types.js +259 -6
  65. package/src/vendor.js +2 -2
  66. package/src/codegen/stdlib/strconv.js +0 -33
  67. /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
  68. /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
  69. /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
  70. /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
  71. /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
  72. /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
  73. /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
  74. /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
  75. /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
  76. /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
  77. /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
@@ -0,0 +1,711 @@
1
+ // src/backend/wasm/monomorph.js
2
+ // Monomorphises generic functions, types, and methods for the WebAssembly (WasmGC) target.
3
+ //
4
+ // In GoFront, the JS backend leaves generics erased because JS is dynamically typed.
5
+ // For WebAssembly, WasmGC requires concrete struct layouts and typed function signatures.
6
+ // This pass specializes all generic templates reachable from code into concrete Go declarations
7
+ // (e.g. Identity$int, Box$int, Stack$int.Push) and re-typechecks the monomorphised AST.
8
+
9
+ import { TypeChecker } from "../../typechecker/index.js";
10
+
11
+ /**
12
+ * Returns true if the AST contains any generic declarations.
13
+ */
14
+ export function hasGenerics(programs) {
15
+ const progs = Array.isArray(programs) ? programs : [programs];
16
+ for (const p of progs) {
17
+ for (const d of p?.decls ?? []) {
18
+ if (d.kind === "FuncDecl" && d.typeParams && d.typeParams.length > 0)
19
+ return true;
20
+ if (d.kind === "TypeDecl" && d.typeParams && d.typeParams.length > 0)
21
+ return true;
22
+ if (
23
+ d.kind === "MethodDecl" &&
24
+ (d.recvType?.typeArgs?.length > 0 ||
25
+ d.recvType?.kind === "GenericTypeName")
26
+ )
27
+ return true;
28
+ }
29
+ }
30
+ return false;
31
+ }
32
+
33
+ /**
34
+ * Returns a stable identifier-safe string key for any Go type or AST type node.
35
+ */
36
+ function typeKey(t) {
37
+ if (!t) return "any";
38
+ if (typeof t === "string") return t;
39
+ if (t.kind === "basic") return t.name;
40
+ if (t.kind === "untyped") return t.base ?? "int";
41
+ if (t.kind === "TypeName" || t.kind === "Ident") return t.name;
42
+ if (t.kind === "GenericTypeName") {
43
+ const args = (t.typeArgs ?? []).map(typeKey).join("$");
44
+ return args ? `${t.name}$${args}` : t.name;
45
+ }
46
+ if (t.kind === "named") {
47
+ if (t.typeArgs && t.typeArgs.length > 0) {
48
+ const args = t.typeArgs.map(typeKey).join("$");
49
+ return `${t.name}$${args}`;
50
+ }
51
+ return t.name;
52
+ }
53
+ if (t.kind === "pointer" || t.kind === "PointerType") {
54
+ return `ptr_${typeKey(t.base ?? t.expr ?? t.operand)}`;
55
+ }
56
+ if (t.kind === "StarExpr") {
57
+ return `ptr_${typeKey(t.expr ?? t.operand)}`;
58
+ }
59
+ if (t.kind === "slice" || t.kind === "SliceType") {
60
+ return `slice_${typeKey(t.elem)}`;
61
+ }
62
+ if (t.kind === "array" || t.kind === "ArrayType") {
63
+ const len = t.len ?? t.size ?? "";
64
+ return `arr_${len}_${typeKey(t.elem)}`;
65
+ }
66
+ if (t.kind === "map" || t.kind === "MapType") {
67
+ return `map_${typeKey(t.key)}_${typeKey(t.value)}`;
68
+ }
69
+ if (t.kind === "func" || t.kind === "FuncType") {
70
+ const params = (t.params ?? []).map(typeKey).join("_");
71
+ const rets = (t.returns ?? []).map(typeKey).join("_");
72
+ return `fn_${params}_ret_${rets}`;
73
+ }
74
+ if (t.name) return t.name;
75
+ return "val";
76
+ }
77
+
78
+ /**
79
+ * Converts a Go type representation or AST type node into a concrete AST type node.
80
+ */
81
+ function typeToTypeNode(t) {
82
+ if (!t) return { kind: "TypeName", name: "any" };
83
+ if (typeof t === "string") return { kind: "TypeName", name: t };
84
+ if (
85
+ t.kind === "TypeName" ||
86
+ t.kind === "GenericTypeName" ||
87
+ t.kind === "PointerType" ||
88
+ t.kind === "SliceType" ||
89
+ t.kind === "ArrayType" ||
90
+ t.kind === "MapType" ||
91
+ t.kind === "FuncType"
92
+ ) {
93
+ return cloneAst(t);
94
+ }
95
+ if (t.kind === "basic") return { kind: "TypeName", name: t.name };
96
+ if (t.kind === "untyped") return { kind: "TypeName", name: t.base ?? "int" };
97
+ if (t.kind === "named") {
98
+ if (t.typeArgs && t.typeArgs.length > 0) {
99
+ return {
100
+ kind: "TypeName",
101
+ name: `${t.name}$${t.typeArgs.map(typeKey).join("$")}`,
102
+ };
103
+ }
104
+ return { kind: "TypeName", name: t.name };
105
+ }
106
+ if (t.kind === "pointer") {
107
+ return { kind: "PointerType", base: typeToTypeNode(t.base) };
108
+ }
109
+ if (t.kind === "slice") {
110
+ return { kind: "SliceType", elem: typeToTypeNode(t.elem) };
111
+ }
112
+ if (t.kind === "array") {
113
+ return {
114
+ kind: "ArrayType",
115
+ len: t.len ?? t.size ?? 0,
116
+ elem: typeToTypeNode(t.elem),
117
+ };
118
+ }
119
+ if (t.kind === "map") {
120
+ return {
121
+ kind: "MapType",
122
+ key: typeToTypeNode(t.key),
123
+ value: typeToTypeNode(t.value),
124
+ };
125
+ }
126
+ if (t.kind === "typeParam") return { kind: "TypeName", name: t.name };
127
+ return { kind: "TypeName", name: t.name ?? "any" };
128
+ }
129
+
130
+ function getSpecializedName(baseName, typeArgs) {
131
+ if (!typeArgs || typeArgs.length === 0) return baseName;
132
+ const keys = typeArgs.map(typeKey).join("$");
133
+ return `${baseName}$${keys}`;
134
+ }
135
+
136
+ function cloneAst(node) {
137
+ if (!node || typeof node !== "object") return node;
138
+ if (Array.isArray(node)) return node.map(cloneAst);
139
+ const copy = {};
140
+ for (const [k, v] of Object.entries(node)) {
141
+ copy[k] = cloneAst(v);
142
+ }
143
+ return copy;
144
+ }
145
+
146
+ /**
147
+ * Replaces references to type parameters in AST nodes with concrete types.
148
+ */
149
+ function substituteTypesInAst(
150
+ node,
151
+ typeSubstMap,
152
+ onGenericUsage,
153
+ genericTypes = null,
154
+ genericFuncs = null,
155
+ ) {
156
+ if (!node || typeof node !== "object") return node;
157
+
158
+ if (Array.isArray(node)) {
159
+ for (let i = 0; i < node.length; i++) {
160
+ node[i] = substituteTypesInAst(
161
+ node[i],
162
+ typeSubstMap,
163
+ onGenericUsage,
164
+ genericTypes,
165
+ genericFuncs,
166
+ );
167
+ }
168
+ return node;
169
+ }
170
+
171
+ // 1. Plain TypeName reference: if it matches a type parameter name, replace with concrete type node
172
+ if (node.kind === "TypeName" && typeSubstMap.has(node.name)) {
173
+ const replacement = cloneAst(typeSubstMap.get(node.name));
174
+ return replacement;
175
+ }
176
+
177
+ // 2. GenericTypeName: e.g. Box[T] or Box[int]
178
+ if (node.kind === "GenericTypeName") {
179
+ if (node.typeArgs) {
180
+ node.typeArgs = node.typeArgs.map((ta) =>
181
+ substituteTypesInAst(
182
+ ta,
183
+ typeSubstMap,
184
+ onGenericUsage,
185
+ genericTypes,
186
+ genericFuncs,
187
+ ),
188
+ );
189
+ }
190
+ const sName = getSpecializedName(node.name, node.typeArgs);
191
+ if (onGenericUsage) {
192
+ onGenericUsage({
193
+ kind: "type",
194
+ genericName: node.name,
195
+ typeArgs: node.typeArgs,
196
+ specializedName: sName,
197
+ });
198
+ }
199
+ return { kind: "TypeName", name: sName };
200
+ }
201
+
202
+ // 3. InstantiationExpr: e.g. Identity[T] or Stack[int]
203
+ if (node.kind === "InstantiationExpr") {
204
+ if (node.typeArgs) {
205
+ node.typeArgs = node.typeArgs.map((ta) =>
206
+ substituteTypesInAst(
207
+ ta,
208
+ typeSubstMap,
209
+ onGenericUsage,
210
+ genericTypes,
211
+ genericFuncs,
212
+ ),
213
+ );
214
+ }
215
+ const baseName = node.expr?.name;
216
+ if (baseName) {
217
+ const sName = getSpecializedName(baseName, node.typeArgs);
218
+ const isType = genericTypes ? genericTypes.has(baseName) : false;
219
+ if (onGenericUsage) {
220
+ onGenericUsage({
221
+ kind: isType ? "type" : "func",
222
+ genericName: baseName,
223
+ typeArgs: node.typeArgs,
224
+ specializedName: sName,
225
+ });
226
+ }
227
+ return isType
228
+ ? { kind: "TypeName", name: sName }
229
+ : { kind: "Ident", name: sName };
230
+ }
231
+ }
232
+
233
+ // 3b. CompositeLit
234
+ if (node.kind === "CompositeLit") {
235
+ const targetProp = node.typeExpr ? "typeExpr" : node.type ? "type" : null;
236
+ if (targetProp && node[targetProp]) {
237
+ node[targetProp] = substituteTypesInAst(
238
+ node[targetProp],
239
+ typeSubstMap,
240
+ onGenericUsage,
241
+ genericTypes,
242
+ genericFuncs,
243
+ );
244
+ }
245
+ }
246
+
247
+ // 4. CallExpr
248
+ if (node.kind === "CallExpr") {
249
+ if (node.func?.kind === "Ident") {
250
+ let typeArgs = node._typeArgs ?? node.func._typeArgs;
251
+ if (typeArgs) {
252
+ typeArgs = typeArgs.map((t) => {
253
+ const name = t?.name ?? (t?.kind === "typeParam" ? t.name : null);
254
+ if (name && typeSubstMap.has(name)) {
255
+ return typeSubstMap.get(name);
256
+ }
257
+ return t;
258
+ });
259
+ const baseName = node.func.name;
260
+ const sName = getSpecializedName(baseName, typeArgs);
261
+ if (onGenericUsage) {
262
+ onGenericUsage({
263
+ kind: "func",
264
+ genericName: baseName,
265
+ typeArgs,
266
+ specializedName: sName,
267
+ });
268
+ }
269
+ node.func.name = sName;
270
+ }
271
+ }
272
+ }
273
+
274
+ // Recurse child properties
275
+ for (const key of Object.keys(node)) {
276
+ if (key.startsWith("_")) continue;
277
+ node[key] = substituteTypesInAst(
278
+ node[key],
279
+ typeSubstMap,
280
+ onGenericUsage,
281
+ genericTypes,
282
+ genericFuncs,
283
+ );
284
+ }
285
+
286
+ return node;
287
+ }
288
+
289
+ /**
290
+ * Scans an AST tree for any generic type or function instantiations.
291
+ */
292
+ function collectGenericUsages(node, genericFuncs, genericTypes, onUsage) {
293
+ if (!node || typeof node !== "object") return;
294
+
295
+ if (Array.isArray(node)) {
296
+ for (const item of node)
297
+ collectGenericUsages(item, genericFuncs, genericTypes, onUsage);
298
+ return;
299
+ }
300
+
301
+ if (node.kind === "GenericTypeName" && genericTypes.has(node.name)) {
302
+ const typeArgs = node._typeArgs ?? node.typeArgs;
303
+ if (typeArgs && typeArgs.length > 0) {
304
+ const sName = getSpecializedName(node.name, typeArgs);
305
+ onUsage({
306
+ kind: "type",
307
+ genericName: node.name,
308
+ typeArgs,
309
+ specializedName: sName,
310
+ });
311
+ }
312
+ } else if (
313
+ node.kind === "InstantiationExpr" &&
314
+ (genericFuncs.has(node.expr?.name) || genericTypes.has(node.expr?.name))
315
+ ) {
316
+ const typeArgs = node._typeArgs ?? node.typeArgs;
317
+ if (typeArgs && typeArgs.length > 0) {
318
+ const isType = genericTypes.has(node.expr.name);
319
+ const sName = getSpecializedName(node.expr.name, typeArgs);
320
+ onUsage({
321
+ kind: isType ? "type" : "func",
322
+ genericName: node.expr.name,
323
+ typeArgs,
324
+ specializedName: sName,
325
+ });
326
+ }
327
+ } else if (node.kind === "CompositeLit") {
328
+ const tNode = node.typeExpr ?? node.type;
329
+ if (tNode) collectGenericUsages(tNode, genericFuncs, genericTypes, onUsage);
330
+ } else if (node.kind === "CallExpr") {
331
+ if (node.func?.kind === "Ident" && genericFuncs.has(node.func.name)) {
332
+ const typeArgs = node._typeArgs ?? node.func._typeArgs;
333
+ if (typeArgs && typeArgs.length > 0) {
334
+ const sName = getSpecializedName(node.func.name, typeArgs);
335
+ onUsage({
336
+ kind: "func",
337
+ genericName: node.func.name,
338
+ typeArgs,
339
+ specializedName: sName,
340
+ });
341
+ }
342
+ } else if (
343
+ node.func?.kind === "InstantiationExpr" &&
344
+ genericFuncs.has(node.func.expr?.name)
345
+ ) {
346
+ const typeArgs = node.func._typeArgs ?? node.func.typeArgs;
347
+ if (typeArgs && typeArgs.length > 0) {
348
+ const sName = getSpecializedName(node.func.expr.name, typeArgs);
349
+ onUsage({
350
+ kind: "func",
351
+ genericName: node.func.expr.name,
352
+ typeArgs,
353
+ specializedName: sName,
354
+ });
355
+ }
356
+ } else if (
357
+ node.func?.kind === "SelectorExpr" &&
358
+ genericFuncs.has(node.func.field)
359
+ ) {
360
+ const typeArgs = node._typeArgs ?? node.func._typeArgs;
361
+ if (typeArgs && typeArgs.length > 0) {
362
+ const sName = getSpecializedName(node.func.field, typeArgs);
363
+ onUsage({
364
+ kind: "func",
365
+ genericName: node.func.field,
366
+ typeArgs,
367
+ specializedName: sName,
368
+ });
369
+ }
370
+ }
371
+ }
372
+
373
+ for (const key of Object.keys(node)) {
374
+ if (key.startsWith("_")) continue;
375
+ collectGenericUsages(node[key], genericFuncs, genericTypes, onUsage);
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Rewrites generic call sites and type expressions to reference their specialized names.
381
+ */
382
+ function rewriteGenericReferences(node, genericFuncs, genericTypes) {
383
+ if (!node || typeof node !== "object") return;
384
+
385
+ if (Array.isArray(node)) {
386
+ for (let i = 0; i < node.length; i++) {
387
+ const child = node[i];
388
+ if (child?.kind === "GenericTypeName" && genericTypes.has(child.name)) {
389
+ const typeArgs = child._typeArgs ?? child.typeArgs;
390
+ if (typeArgs && typeArgs.length > 0) {
391
+ const sName = getSpecializedName(child.name, typeArgs);
392
+ node[i] = { kind: "TypeName", name: sName };
393
+ continue;
394
+ }
395
+ }
396
+ if (
397
+ child?.kind === "InstantiationExpr" &&
398
+ (genericFuncs.has(child.expr?.name) ||
399
+ genericTypes.has(child.expr?.name))
400
+ ) {
401
+ const typeArgs = child._typeArgs ?? child.typeArgs;
402
+ if (typeArgs && typeArgs.length > 0) {
403
+ const isType = genericTypes.has(child.expr.name);
404
+ const sName = getSpecializedName(child.expr.name, typeArgs);
405
+ node[i] = isType
406
+ ? { kind: "TypeName", name: sName }
407
+ : { kind: "Ident", name: sName };
408
+ continue;
409
+ }
410
+ }
411
+ rewriteGenericReferences(child, genericFuncs, genericTypes);
412
+ }
413
+ return;
414
+ }
415
+
416
+ // Object children
417
+ if (node.kind === "CallExpr") {
418
+ if (node.func?.kind === "Ident" && genericFuncs.has(node.func.name)) {
419
+ const typeArgs = node._typeArgs ?? node.func._typeArgs;
420
+ if (typeArgs && typeArgs.length > 0) {
421
+ node.func.name = getSpecializedName(node.func.name, typeArgs);
422
+ }
423
+ } else if (
424
+ node.func?.kind === "InstantiationExpr" &&
425
+ genericFuncs.has(node.func.expr?.name)
426
+ ) {
427
+ const typeArgs = node.func._typeArgs ?? node.func.typeArgs;
428
+ if (typeArgs && typeArgs.length > 0) {
429
+ const sName = getSpecializedName(node.func.expr.name, typeArgs);
430
+ node.func = { kind: "Ident", name: sName };
431
+ }
432
+ } else if (
433
+ node.func?.kind === "SelectorExpr" &&
434
+ genericFuncs.has(node.func.field)
435
+ ) {
436
+ const typeArgs = node._typeArgs ?? node.func._typeArgs;
437
+ if (typeArgs && typeArgs.length > 0) {
438
+ node.func.field = getSpecializedName(node.func.field, typeArgs);
439
+ }
440
+ }
441
+ } else if (node.kind === "CompositeLit" && (node.typeExpr || node.type)) {
442
+ const targetProp = node.typeExpr ? "typeExpr" : "type";
443
+ const tNode = node[targetProp];
444
+ if (tNode?.kind === "GenericTypeName" && genericTypes.has(tNode.name)) {
445
+ const typeArgs = tNode._typeArgs ?? tNode.typeArgs;
446
+ if (typeArgs && typeArgs.length > 0) {
447
+ const sName = getSpecializedName(tNode.name, typeArgs);
448
+ node[targetProp] = { kind: "TypeName", name: sName };
449
+ }
450
+ } else if (
451
+ tNode?.kind === "InstantiationExpr" &&
452
+ genericTypes.has(tNode.expr?.name)
453
+ ) {
454
+ const typeArgs = tNode._typeArgs ?? tNode.typeArgs;
455
+ if (typeArgs && typeArgs.length > 0) {
456
+ const sName = getSpecializedName(tNode.expr.name, typeArgs);
457
+ node[targetProp] = { kind: "TypeName", name: sName };
458
+ }
459
+ }
460
+ }
461
+
462
+ for (const key of Object.keys(node)) {
463
+ if (key.startsWith("_")) continue;
464
+ const child = node[key];
465
+ if (child?.kind === "GenericTypeName" && genericTypes.has(child.name)) {
466
+ const typeArgs = child._typeArgs ?? child.typeArgs;
467
+ if (typeArgs && typeArgs.length > 0) {
468
+ node[key] = {
469
+ kind: "TypeName",
470
+ name: getSpecializedName(child.name, typeArgs),
471
+ };
472
+ continue;
473
+ }
474
+ }
475
+ if (
476
+ child?.kind === "InstantiationExpr" &&
477
+ (genericFuncs.has(child.expr?.name) || genericTypes.has(child.expr?.name))
478
+ ) {
479
+ const typeArgs = child._typeArgs ?? child.typeArgs;
480
+ if (typeArgs && typeArgs.length > 0) {
481
+ const isType = genericTypes.has(child.expr.name);
482
+ const sName = getSpecializedName(child.expr.name, typeArgs);
483
+ node[key] = isType
484
+ ? { kind: "TypeName", name: sName }
485
+ : { kind: "Ident", name: sName };
486
+ continue;
487
+ }
488
+ }
489
+ rewriteGenericReferences(child, genericFuncs, genericTypes);
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Main monomorphisation entry point.
495
+ * Given AST programs and typechecker, returns monomorphised programs and an updated TypeChecker.
496
+ */
497
+ export function monomorphise(programs, checker) {
498
+ const progs = Array.isArray(programs) ? programs : [programs];
499
+ if (!hasGenerics(progs)) {
500
+ return { programs: progs, checker };
501
+ }
502
+
503
+ // 1. Identify all generic definitions
504
+ const genericFuncs = new Map();
505
+ const genericTypes = new Map();
506
+ const genericMethods = new Map(); // recvBaseName -> Array<MethodDecl>
507
+
508
+ for (const p of progs) {
509
+ for (const d of p?.decls ?? []) {
510
+ if (d.kind === "FuncDecl" && d.typeParams && d.typeParams.length > 0) {
511
+ genericFuncs.set(d.name, d);
512
+ } else if (
513
+ d.kind === "TypeDecl" &&
514
+ d.typeParams &&
515
+ d.typeParams.length > 0
516
+ ) {
517
+ genericTypes.set(d.name, d);
518
+ } else if (d.kind === "MethodDecl") {
519
+ const recvName = d.recvType?.name;
520
+ if (
521
+ d.recvType?.typeArgs?.length > 0 ||
522
+ d.recvType?.kind === "GenericTypeName" ||
523
+ genericTypes.has(recvName)
524
+ ) {
525
+ if (!genericMethods.has(recvName)) genericMethods.set(recvName, []);
526
+ genericMethods.get(recvName).push(d);
527
+ }
528
+ }
529
+ }
530
+ }
531
+
532
+ const worklist = [];
533
+ const seen = new Set();
534
+
535
+ function enqueue(item) {
536
+ if (!item || seen.has(item.specializedName)) return;
537
+ worklist.push(item);
538
+ }
539
+
540
+ // 2. Collect initial usages in non-generic code
541
+ for (const p of progs) {
542
+ for (const d of p?.decls ?? []) {
543
+ const isGenericFunc =
544
+ d.kind === "FuncDecl" && d.typeParams && d.typeParams.length > 0;
545
+ const isGenericType =
546
+ d.kind === "TypeDecl" && d.typeParams && d.typeParams.length > 0;
547
+ const isGenericMethod =
548
+ d.kind === "MethodDecl" &&
549
+ (d.recvType?.typeArgs?.length > 0 ||
550
+ d.recvType?.kind === "GenericTypeName" ||
551
+ genericTypes.has(d.recvType?.name));
552
+
553
+ if (!isGenericFunc && !isGenericType && !isGenericMethod) {
554
+ collectGenericUsages(d, genericFuncs, genericTypes, enqueue);
555
+ }
556
+ }
557
+ }
558
+
559
+ const specializedFuncs = new Map();
560
+ const specializedTypes = new Map();
561
+ const specializedMethods = new Map();
562
+
563
+ // 3. Process worklist until all reachable specializations are emitted
564
+ let iterations = 0;
565
+ const MAX_ITERATIONS = 512;
566
+
567
+ while (worklist.length > 0) {
568
+ if (++iterations > MAX_ITERATIONS) {
569
+ throw new Error("Monomorphisation exceeded maximum iteration limit");
570
+ }
571
+ const item = worklist.shift();
572
+ if (seen.has(item.specializedName)) continue;
573
+ seen.add(item.specializedName);
574
+
575
+ if (item.kind === "type") {
576
+ const genDecl = genericTypes.get(item.genericName);
577
+ if (!genDecl) continue;
578
+
579
+ const typeSubstMap = new Map();
580
+ for (let i = 0; i < genDecl.typeParams.length; i++) {
581
+ const tp = genDecl.typeParams[i];
582
+ const concrete = item.typeArgs[i];
583
+ typeSubstMap.set(tp.name, typeToTypeNode(concrete));
584
+ }
585
+
586
+ const monoTypeDecl = cloneAst(genDecl);
587
+ monoTypeDecl.name = item.specializedName;
588
+ monoTypeDecl.typeParams = null;
589
+ substituteTypesInAst(
590
+ monoTypeDecl,
591
+ typeSubstMap,
592
+ enqueue,
593
+ genericTypes,
594
+ genericFuncs,
595
+ );
596
+ specializedTypes.set(item.specializedName, monoTypeDecl);
597
+
598
+ // Specialize all methods declared on this generic type
599
+ const methods = genericMethods.get(item.genericName) ?? [];
600
+ for (const m of methods) {
601
+ const monoMethod = cloneAst(m);
602
+ monoMethod.recvType = {
603
+ kind: "TypeName",
604
+ name: item.specializedName,
605
+ };
606
+ substituteTypesInAst(
607
+ monoMethod,
608
+ typeSubstMap,
609
+ enqueue,
610
+ genericTypes,
611
+ genericFuncs,
612
+ );
613
+ const mKey = `${item.specializedName}.${monoMethod.name}`;
614
+ specializedMethods.set(mKey, monoMethod);
615
+ }
616
+ } else if (item.kind === "func") {
617
+ const genDecl = genericFuncs.get(item.genericName);
618
+ if (!genDecl) continue;
619
+
620
+ const typeSubstMap = new Map();
621
+ for (let i = 0; i < genDecl.typeParams.length; i++) {
622
+ const tp = genDecl.typeParams[i];
623
+ const concrete = item.typeArgs[i];
624
+ typeSubstMap.set(tp.name, typeToTypeNode(concrete));
625
+ }
626
+
627
+ const monoFuncDecl = cloneAst(genDecl);
628
+ monoFuncDecl.name = item.specializedName;
629
+ monoFuncDecl.typeParams = null;
630
+ substituteTypesInAst(
631
+ monoFuncDecl,
632
+ typeSubstMap,
633
+ enqueue,
634
+ genericTypes,
635
+ genericFuncs,
636
+ );
637
+ specializedFuncs.set(item.specializedName, monoFuncDecl);
638
+ }
639
+ }
640
+
641
+ // 4. Rewrite all generic references across ASTs
642
+ for (const p of progs) {
643
+ for (const d of p?.decls ?? []) {
644
+ const isGenericFunc =
645
+ d.kind === "FuncDecl" && d.typeParams && d.typeParams.length > 0;
646
+ const isGenericType =
647
+ d.kind === "TypeDecl" && d.typeParams && d.typeParams.length > 0;
648
+ const isGenericMethod =
649
+ d.kind === "MethodDecl" &&
650
+ (d.recvType?.typeArgs?.length > 0 ||
651
+ d.recvType?.kind === "GenericTypeName" ||
652
+ genericTypes.has(d.recvType?.name));
653
+
654
+ if (!isGenericFunc && !isGenericType && !isGenericMethod) {
655
+ rewriteGenericReferences(d, genericFuncs, genericTypes);
656
+ }
657
+ }
658
+ }
659
+ for (const fn of specializedFuncs.values()) {
660
+ rewriteGenericReferences(fn, genericFuncs, genericTypes);
661
+ }
662
+ for (const t of specializedTypes.values()) {
663
+ rewriteGenericReferences(t, genericFuncs, genericTypes);
664
+ }
665
+ for (const m of specializedMethods.values()) {
666
+ rewriteGenericReferences(m, genericFuncs, genericTypes);
667
+ }
668
+
669
+ // 5. Update program declarations: remove generic templates, add specializations
670
+ for (const p of progs) {
671
+ p.decls = (p.decls ?? []).filter((d) => {
672
+ if (d.kind === "FuncDecl" && d.typeParams && d.typeParams.length > 0)
673
+ return false;
674
+ if (d.kind === "TypeDecl" && d.typeParams && d.typeParams.length > 0)
675
+ return false;
676
+ if (
677
+ d.kind === "MethodDecl" &&
678
+ (d.recvType?.typeArgs?.length > 0 ||
679
+ d.recvType?.kind === "GenericTypeName" ||
680
+ genericTypes.has(d.recvType?.name))
681
+ )
682
+ return false;
683
+ return true;
684
+ });
685
+ }
686
+
687
+ const targetProg = progs[0];
688
+ if (targetProg) {
689
+ targetProg.decls.push(...specializedTypes.values());
690
+ targetProg.decls.push(...specializedMethods.values());
691
+ targetProg.decls.push(...specializedFuncs.values());
692
+ }
693
+
694
+ // 6. Typecheck the monomorphised program to annotate all concrete types
695
+ const monoChecker = new TypeChecker();
696
+ monoChecker.target = "wasm";
697
+ monoChecker.pkgName = targetProg?.pkg?.name ?? "main";
698
+ if (checker?.types) {
699
+ for (const [name, type] of checker.types.entries()) {
700
+ if (!monoChecker.types.has(name)) {
701
+ monoChecker.types.set(name, type);
702
+ }
703
+ }
704
+ }
705
+
706
+ for (const p of progs) {
707
+ monoChecker.check(p);
708
+ }
709
+
710
+ return { programs: progs, checker: monoChecker };
711
+ }