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.
- package/README.md +174 -403
- package/package.json +16 -19
- package/src/{codegen → backend/js}/expressions.js +265 -77
- package/src/{codegen → backend/js}/index.js +120 -89
- package/src/{codegen → backend/js}/runtime.js +102 -0
- package/src/{codegen → backend/js}/source-map.js +32 -0
- package/src/{codegen → backend/js}/statements.js +139 -64
- package/src/backend/js/stdlib/bits.js +32 -0
- package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
- package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
- package/src/{codegen → backend/js}/stdlib/index.js +6 -0
- package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
- package/src/{codegen → backend/js}/stdlib/path.js +2 -2
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
- package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +58 -0
- package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
- package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
- package/src/{codegen → backend/js}/stdlib/time.js +3 -3
- package/src/{codegen → backend/js}/templ.js +3 -3
- package/src/backend/wasm/boundary.js +1798 -0
- package/src/backend/wasm/emit-builtins.js +1103 -0
- package/src/backend/wasm/emit-exprs.js +1908 -0
- package/src/backend/wasm/emit-maps.js +1863 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +3006 -0
- package/src/backend/wasm/emit-stmts.js +2472 -0
- package/src/backend/wasm/emit.js +1376 -0
- package/src/backend/wasm/encode.js +937 -0
- package/src/backend/wasm/glue.js +144 -0
- package/src/backend/wasm/index.js +2790 -0
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +227 -0
- package/src/backend/wasm/types.js +544 -0
- package/src/backend/wasm/wat.js +302 -0
- package/src/cli-core.js +323 -75
- package/src/compiler.js +447 -14
- package/src/dev-server.js +56 -15
- package/src/index.js +99 -120
- package/src/lexer.js +18 -0
- package/src/lower/boxing.js +35 -0
- package/src/lower/captures.js +172 -0
- package/src/lower/embedding.js +30 -0
- package/src/lower/escape.js +164 -0
- package/src/lower/functions.js +302 -0
- package/src/lower/globals.js +316 -0
- package/src/lower/index.js +118 -0
- package/src/lower/ownership.js +137 -0
- package/src/lower/range.js +67 -0
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +2 -0
- package/src/parser/index.js +8 -3
- package/src/resolver.js +3 -1
- package/src/test-runner.js +94 -20
- package/src/typechecker/assignability.js +42 -1
- package/src/typechecker/expressions.js +182 -3
- package/src/typechecker/index.js +261 -5
- package/src/typechecker/resolve.js +35 -6
- package/src/typechecker/stdlib/core.js +34 -19
- package/src/typechecker/stdlib/extended.js +18 -3
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +259 -6
- package/src/vendor.js +2 -2
- package/src/codegen/stdlib/strconv.js +0 -33
- /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
- /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
|
+
}
|