gofront 1.3.4 → 1.3.6
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/package.json +1 -1
- package/src/cli-core.js +118 -13
- package/src/codegen/expressions.js +408 -89
- package/src/codegen/index.js +213 -29
- package/src/codegen/runtime.js +22 -1
- package/src/codegen/statements.js +170 -45
- package/src/codegen/stdlib/builder.js +7 -2
- package/src/codegen/stdlib/fmt.js +2 -2
- package/src/codegen/stdlib/io.js +1 -1
- package/src/dev-server.js +22 -10
- package/src/index.js +8 -2
- package/src/parser/declarations.js +2 -1
- package/src/pwa.js +3 -1
- package/src/typechecker/assignability.js +50 -4
- package/src/typechecker/expressions.js +13 -1
- package/src/typechecker/index.js +71 -31
- package/src/typechecker/resolve.js +2 -0
- package/src/typechecker/statements.js +10 -1
- package/src/typechecker/stdlib/web.js +54 -26
- package/src/typechecker/types.js +3 -2
- package/src/vendor.js +12 -7
package/src/typechecker/index.js
CHANGED
|
@@ -140,26 +140,7 @@ export class TypeChecker {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
check(program) {
|
|
143
|
-
this.
|
|
144
|
-
// Pass 1: collect type declarations
|
|
145
|
-
for (const decl of program.decls) {
|
|
146
|
-
if (decl.kind === "TypeDecl") this.collectType(decl);
|
|
147
|
-
}
|
|
148
|
-
// Pass 2: collect function / method signatures
|
|
149
|
-
for (const decl of program.decls) {
|
|
150
|
-
if (decl.kind === "FuncDecl" || decl.kind === "MethodDecl") {
|
|
151
|
-
this.collectFunc(decl);
|
|
152
|
-
} else if (decl.kind === "TemplDecl") {
|
|
153
|
-
this._collectTemplDecl(decl);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// Pass 2.1: Promote embedded methods
|
|
157
|
-
this._promoteEmbeddedMethods();
|
|
158
|
-
// Pass 3: check bodies
|
|
159
|
-
for (const decl of program.decls) {
|
|
160
|
-
this.checkTopDecl(decl, this.globals);
|
|
161
|
-
}
|
|
162
|
-
return this.errors;
|
|
143
|
+
return this.checkAll([program]);
|
|
163
144
|
}
|
|
164
145
|
|
|
165
146
|
// Pre-declare a package-level var so other files can reference its name
|
|
@@ -196,11 +177,63 @@ export class TypeChecker {
|
|
|
196
177
|
|
|
197
178
|
_collectTypesPass(programs) {
|
|
198
179
|
for (const p of programs) {
|
|
199
|
-
|
|
200
|
-
|
|
180
|
+
for (const d of p.decls) {
|
|
181
|
+
if (d.kind === "TypeDecl" && !d.isAlias && !this.types.has(d.name)) {
|
|
182
|
+
const placeholder = { kind: "named", name: d.name, underlying: null };
|
|
183
|
+
this.types.set(d.name, placeholder);
|
|
184
|
+
this.globals.define(d.name, placeholder);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const all = [];
|
|
189
|
+
for (const p of programs) {
|
|
190
|
+
for (const d of p.decls) if (d.kind === "TypeDecl") all.push({ p, d });
|
|
191
|
+
}
|
|
192
|
+
// Collect in dependency order so aliases, generics, embedded and by-value
|
|
193
|
+
// field types declared later (or in other files) are ready when needed.
|
|
194
|
+
let pending = all;
|
|
195
|
+
while (pending.length > 0) {
|
|
196
|
+
const pendingNames = new Map(pending.map((x) => [x.d.name, x.d]));
|
|
197
|
+
const ready = pending.filter(
|
|
198
|
+
(x) => !this._typeDeclBlocked(x.d, pendingNames),
|
|
199
|
+
);
|
|
200
|
+
const batch = ready.length > 0 ? ready : pending;
|
|
201
|
+
for (const { p, d } of batch) {
|
|
202
|
+
this._setCurrentFile(p);
|
|
203
|
+
this.collectType(d);
|
|
204
|
+
}
|
|
205
|
+
pending =
|
|
206
|
+
ready.length > 0 ? pending.filter((x) => !ready.includes(x)) : [];
|
|
201
207
|
}
|
|
202
208
|
}
|
|
203
209
|
|
|
210
|
+
_typeDeclBlocked(decl, pendingNames) {
|
|
211
|
+
const visit = (node, indirect) => {
|
|
212
|
+
if (!node || typeof node !== "object") return false;
|
|
213
|
+
if (Array.isArray(node)) return node.some((n) => visit(n, indirect));
|
|
214
|
+
if (
|
|
215
|
+
(node.kind === "TypeName" || node.kind === "GenericTypeName") &&
|
|
216
|
+
node.name !== decl.name
|
|
217
|
+
) {
|
|
218
|
+
const dep = pendingNames.get(node.name);
|
|
219
|
+
if (dep && (!indirect || dep.isAlias || dep.typeParams)) return true;
|
|
220
|
+
}
|
|
221
|
+
const nextIndirect =
|
|
222
|
+
indirect ||
|
|
223
|
+
[
|
|
224
|
+
"PointerType",
|
|
225
|
+
"SliceType",
|
|
226
|
+
"MapType",
|
|
227
|
+
"FuncType",
|
|
228
|
+
"ChanType",
|
|
229
|
+
].includes(node.kind);
|
|
230
|
+
return Object.keys(node).some(
|
|
231
|
+
(k) => !k.startsWith("_") && visit(node[k], nextIndirect),
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
return visit(decl.type, false);
|
|
235
|
+
}
|
|
236
|
+
|
|
204
237
|
_collectFuncsPass(programs) {
|
|
205
238
|
for (const p of programs) {
|
|
206
239
|
this._setCurrentFile(p);
|
|
@@ -274,14 +307,15 @@ export class TypeChecker {
|
|
|
274
307
|
return t;
|
|
275
308
|
});
|
|
276
309
|
const underlying = this.resolveTypeNode(decl.type, typeScope);
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
310
|
+
const existing = this.types.get(decl.name);
|
|
311
|
+
const named =
|
|
312
|
+
existing?.kind === "named"
|
|
313
|
+
? existing
|
|
314
|
+
: { kind: "named", name: decl.name };
|
|
315
|
+
named.underlying = underlying;
|
|
316
|
+
named._generic = {
|
|
317
|
+
typeParams: typeParamTypes,
|
|
318
|
+
declNode: decl,
|
|
285
319
|
};
|
|
286
320
|
if (underlying.kind === "struct") {
|
|
287
321
|
underlying.name = decl.name;
|
|
@@ -298,7 +332,12 @@ export class TypeChecker {
|
|
|
298
332
|
this.globals.define(decl.name, underlying);
|
|
299
333
|
return;
|
|
300
334
|
}
|
|
301
|
-
const
|
|
335
|
+
const existing = this.types.get(decl.name);
|
|
336
|
+
const named =
|
|
337
|
+
existing?.kind === "named"
|
|
338
|
+
? existing
|
|
339
|
+
: { kind: "named", name: decl.name };
|
|
340
|
+
named.underlying = underlying;
|
|
302
341
|
if (underlying.kind === "struct") {
|
|
303
342
|
underlying.name = decl.name;
|
|
304
343
|
underlying.methods = new Map();
|
|
@@ -394,6 +433,7 @@ export class TypeChecker {
|
|
|
394
433
|
}
|
|
395
434
|
|
|
396
435
|
_attachMethod(decl, funcType) {
|
|
436
|
+
funcType._ptrRecv = Boolean(decl.recvPointer);
|
|
397
437
|
const recvNamedType = this.types.get(decl.recvType.name);
|
|
398
438
|
if (recvNamedType?._generic)
|
|
399
439
|
this._attachGenericMethod(recvNamedType, decl, funcType);
|
|
@@ -411,6 +411,8 @@ export const resolveMethods = {
|
|
|
411
411
|
_fieldTypeInterface(base, baseType, field, node) {
|
|
412
412
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
413
413
|
if (base.fields?.has(field)) return base.fields.get(field);
|
|
414
|
+
if (base._excluded?.has(field))
|
|
415
|
+
return this.err(`No member '${field}' on ${typeStr(baseType)}`, node);
|
|
414
416
|
if (base._isOpen) return ANY;
|
|
415
417
|
return this.err(`No method '${field}' on ${typeStr(baseType)}`, node);
|
|
416
418
|
},
|
|
@@ -148,8 +148,17 @@ export const statementCheckMethods = {
|
|
|
148
148
|
for (let i = 0; i < lhsTypes.length; i++) {
|
|
149
149
|
if (stmt.lhs[i]?.kind === "Ident" && stmt.lhs[i]?.name === "_") continue;
|
|
150
150
|
const r = rhsFlat[i] ?? ANY;
|
|
151
|
-
if (
|
|
151
|
+
if (stmt.rhs.length === lhsTypes.length && lhsTypes[i] && r)
|
|
152
|
+
this._markIfaceBox(
|
|
153
|
+
this.resolveType(lhsTypes[i]),
|
|
154
|
+
this.resolveType(r),
|
|
155
|
+
stmt.rhs[i],
|
|
156
|
+
);
|
|
157
|
+
if (!isAny(lhsTypes[i]) && !isAny(r)) {
|
|
158
|
+
this._skipIfaceMark = true;
|
|
152
159
|
this.assertAssignable(lhsTypes[i], r, stmt.lhs[i]);
|
|
160
|
+
this._skipIfaceMark = false;
|
|
161
|
+
}
|
|
153
162
|
}
|
|
154
163
|
},
|
|
155
164
|
|
|
@@ -23,6 +23,25 @@ const func = (params, returns = [VOID], variadic = false) => ({
|
|
|
23
23
|
variadic,
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// Last parameter is optional (modelled as variadic).
|
|
27
|
+
const optLast = (params, returns = [VOID]) => func(params, returns, true);
|
|
28
|
+
|
|
29
|
+
const WEBGL2_ONLY_CONSTANTS = new Set([
|
|
30
|
+
"UNIFORM_BUFFER",
|
|
31
|
+
"HALF_FLOAT",
|
|
32
|
+
"RGBA8",
|
|
33
|
+
"RGBA32F",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const WEBGL2_ONLY_METHODS = new Set([
|
|
37
|
+
"VertexAttribIPointer",
|
|
38
|
+
"DrawArraysInstanced",
|
|
39
|
+
"DrawElementsInstanced",
|
|
40
|
+
"CreateVertexArray",
|
|
41
|
+
"BindVertexArray",
|
|
42
|
+
"DeleteVertexArray",
|
|
43
|
+
]);
|
|
44
|
+
|
|
26
45
|
const WEBGL_CONSTANTS_LIST = {
|
|
27
46
|
ARRAY_BUFFER: 0x8892,
|
|
28
47
|
ELEMENT_ARRAY_BUFFER: 0x8893,
|
|
@@ -113,7 +132,7 @@ function buildArrayBufferType() {
|
|
|
113
132
|
methods,
|
|
114
133
|
};
|
|
115
134
|
named.underlying = iface;
|
|
116
|
-
methods.set("slice",
|
|
135
|
+
methods.set("slice", optLast([INT, INT], [named]));
|
|
117
136
|
return named;
|
|
118
137
|
}
|
|
119
138
|
|
|
@@ -126,20 +145,20 @@ function buildDataViewType(arrayBufferType) {
|
|
|
126
145
|
const methods = new Map([
|
|
127
146
|
["getInt8", func([INT], [INT8])],
|
|
128
147
|
["getUint8", func([INT], [UINT8])],
|
|
129
|
-
["getInt16",
|
|
130
|
-
["getUint16",
|
|
131
|
-
["getInt32",
|
|
132
|
-
["getUint32",
|
|
133
|
-
["getFloat32",
|
|
134
|
-
["getFloat64",
|
|
148
|
+
["getInt16", optLast([INT, BOOL], [INT16])],
|
|
149
|
+
["getUint16", optLast([INT, BOOL], [UINT16])],
|
|
150
|
+
["getInt32", optLast([INT, BOOL], [INT32])],
|
|
151
|
+
["getUint32", optLast([INT, BOOL], [UINT32])],
|
|
152
|
+
["getFloat32", optLast([INT, BOOL], [FLOAT32])],
|
|
153
|
+
["getFloat64", optLast([INT, BOOL], [FLOAT64])],
|
|
135
154
|
["setInt8", func([INT, INT8])],
|
|
136
155
|
["setUint8", func([INT, UINT8])],
|
|
137
|
-
["setInt16",
|
|
138
|
-
["setUint16",
|
|
139
|
-
["setInt32",
|
|
140
|
-
["setUint32",
|
|
141
|
-
["setFloat32",
|
|
142
|
-
["setFloat64",
|
|
156
|
+
["setInt16", optLast([INT, INT16, BOOL])],
|
|
157
|
+
["setUint16", optLast([INT, UINT16, BOOL])],
|
|
158
|
+
["setInt32", optLast([INT, INT32, BOOL])],
|
|
159
|
+
["setUint32", optLast([INT, UINT32, BOOL])],
|
|
160
|
+
["setFloat32", optLast([INT, FLOAT32, BOOL])],
|
|
161
|
+
["setFloat64", optLast([INT, FLOAT64, BOOL])],
|
|
143
162
|
]);
|
|
144
163
|
const named = { kind: "named", name: "DataView", underlying: null };
|
|
145
164
|
named.underlying = { kind: "interface", name: "DataView", fields, methods };
|
|
@@ -164,15 +183,16 @@ function buildTypedArrayType(name, elemType, arrayBufferType) {
|
|
|
164
183
|
_isTypedArray: true,
|
|
165
184
|
};
|
|
166
185
|
named.underlying = iface;
|
|
167
|
-
methods.set("subarray",
|
|
168
|
-
methods.set("slice",
|
|
169
|
-
methods.set("set",
|
|
186
|
+
methods.set("subarray", optLast([INT, INT], [named]));
|
|
187
|
+
methods.set("slice", optLast([INT, INT], [named]));
|
|
188
|
+
methods.set("set", optLast([ANY, INT], [VOID]));
|
|
170
189
|
return named;
|
|
171
190
|
}
|
|
172
191
|
|
|
173
|
-
function createWebGLMethods() {
|
|
192
|
+
function createWebGLMethods(isWebGL2) {
|
|
174
193
|
const methods = new Map();
|
|
175
194
|
const add = (name, fn) => {
|
|
195
|
+
if (!isWebGL2 && WEBGL2_ONLY_METHODS.has(name)) return;
|
|
176
196
|
methods.set(name, fn);
|
|
177
197
|
const camel = name[0].toLowerCase() + name.slice(1);
|
|
178
198
|
if (camel !== name) methods.set(camel, fn);
|
|
@@ -296,7 +316,7 @@ function createWebGPUMethods() {
|
|
|
296
316
|
addDev("CreateShaderModule", func([ANY], [ANY]));
|
|
297
317
|
addDev("CreateRenderPipeline", func([ANY], [ANY]));
|
|
298
318
|
addDev("CreateComputePipeline", func([ANY], [ANY]));
|
|
299
|
-
addDev("CreateCommandEncoder",
|
|
319
|
+
addDev("CreateCommandEncoder", optLast([ANY], [ANY]));
|
|
300
320
|
addDev("CreateBindGroup", func([ANY], [ANY]));
|
|
301
321
|
addDev("CreateBindGroupLayout", func([ANY], [ANY]));
|
|
302
322
|
addDev("CreatePipelineLayout", func([ANY], [ANY]));
|
|
@@ -307,7 +327,7 @@ function createWebGPUMethods() {
|
|
|
307
327
|
const camel = name[0].toLowerCase() + name.slice(1);
|
|
308
328
|
if (camel !== name) adapterMethods.set(camel, fn);
|
|
309
329
|
};
|
|
310
|
-
addAdapt("RequestDevice",
|
|
330
|
+
addAdapt("RequestDevice", optLast([ANY], [ANY]));
|
|
311
331
|
|
|
312
332
|
return { queueMethods, deviceMethods, adapterMethods };
|
|
313
333
|
}
|
|
@@ -339,21 +359,29 @@ export function setupWebGlobals(globals, types) {
|
|
|
339
359
|
}
|
|
340
360
|
|
|
341
361
|
// WebGL constants map
|
|
342
|
-
const
|
|
362
|
+
const constFields2 = new Map();
|
|
343
363
|
for (const [k] of Object.entries(WEBGL_CONSTANTS_LIST)) {
|
|
344
|
-
|
|
364
|
+
constFields2.set(k, INT);
|
|
365
|
+
}
|
|
366
|
+
const constFields1 = new Map(
|
|
367
|
+
[...constFields2].filter(([k]) => !WEBGL2_ONLY_CONSTANTS.has(k)),
|
|
368
|
+
);
|
|
369
|
+
const webgl2Only = new Set(WEBGL2_ONLY_CONSTANTS);
|
|
370
|
+
for (const m of WEBGL2_ONLY_METHODS) {
|
|
371
|
+
webgl2Only.add(m);
|
|
372
|
+
webgl2Only.add(m[0].toLowerCase() + m.slice(1));
|
|
345
373
|
}
|
|
346
374
|
|
|
347
|
-
const webglMethods = createWebGLMethods();
|
|
348
375
|
const webglContext = {
|
|
349
376
|
kind: "named",
|
|
350
377
|
name: "WebGLRenderingContext",
|
|
351
378
|
underlying: {
|
|
352
379
|
kind: "interface",
|
|
353
380
|
name: "WebGLRenderingContext",
|
|
354
|
-
fields:
|
|
355
|
-
methods:
|
|
381
|
+
fields: constFields1,
|
|
382
|
+
methods: createWebGLMethods(false),
|
|
356
383
|
_isOpen: true,
|
|
384
|
+
_excluded: webgl2Only,
|
|
357
385
|
},
|
|
358
386
|
};
|
|
359
387
|
const webgl2Context = {
|
|
@@ -362,8 +390,8 @@ export function setupWebGlobals(globals, types) {
|
|
|
362
390
|
underlying: {
|
|
363
391
|
kind: "interface",
|
|
364
392
|
name: "WebGL2RenderingContext",
|
|
365
|
-
fields:
|
|
366
|
-
methods:
|
|
393
|
+
fields: constFields2,
|
|
394
|
+
methods: createWebGLMethods(true),
|
|
367
395
|
_isOpen: true,
|
|
368
396
|
},
|
|
369
397
|
};
|
package/src/typechecker/types.js
CHANGED
|
@@ -55,7 +55,7 @@ export const UINT16 = { kind: "basic", name: "uint16" };
|
|
|
55
55
|
export const UINT32 = { kind: "basic", name: "uint32" };
|
|
56
56
|
export const UINT64 = { kind: "basic", name: "uint64" };
|
|
57
57
|
export const UINTPTR = { kind: "basic", name: "uintptr" };
|
|
58
|
-
export const RUNE =
|
|
58
|
+
export const RUNE = INT32;
|
|
59
59
|
export const FLOAT32 = { kind: "basic", name: "float32" };
|
|
60
60
|
export const FLOAT64 = { kind: "basic", name: "float64" };
|
|
61
61
|
export const STRING = { kind: "basic", name: "string" };
|
|
@@ -149,9 +149,9 @@ export const isString = makeBasicPredicate("string");
|
|
|
149
149
|
export const isBool = makeBasicPredicate("bool");
|
|
150
150
|
|
|
151
151
|
// ── TypedArray mappings ──────────────────────────────────────
|
|
152
|
+
// []float64 deliberately stays a plain Array for JSON / JS-library interop.
|
|
152
153
|
export const TYPED_ARRAY_CONSTRUCTORS = {
|
|
153
154
|
float32: "Float32Array",
|
|
154
|
-
float64: "Float64Array",
|
|
155
155
|
uint8: "Uint8Array",
|
|
156
156
|
byte: "Uint8Array",
|
|
157
157
|
int8: "Int8Array",
|
|
@@ -159,6 +159,7 @@ export const TYPED_ARRAY_CONSTRUCTORS = {
|
|
|
159
159
|
int16: "Int16Array",
|
|
160
160
|
uint32: "Uint32Array",
|
|
161
161
|
int32: "Int32Array",
|
|
162
|
+
rune: "Int32Array",
|
|
162
163
|
};
|
|
163
164
|
|
|
164
165
|
export function typedArrayConstructorForElem(elemType) {
|
package/src/vendor.js
CHANGED
|
@@ -227,9 +227,12 @@ export async function loadNodePolyfillsPlugin(projectRoot, customPlugin) {
|
|
|
227
227
|
if (!info) return null;
|
|
228
228
|
try {
|
|
229
229
|
const mod = await import(pathToFileURL(info.path).href);
|
|
230
|
-
const fn =
|
|
231
|
-
mod.nodePolyfills
|
|
232
|
-
|
|
230
|
+
const fn = [
|
|
231
|
+
mod.nodePolyfills,
|
|
232
|
+
mod.default?.nodePolyfills,
|
|
233
|
+
mod.default,
|
|
234
|
+
].find((f) => typeof f === "function");
|
|
235
|
+
return fn ? fn() : null;
|
|
233
236
|
} catch {
|
|
234
237
|
return null;
|
|
235
238
|
}
|
|
@@ -339,10 +342,12 @@ export async function bundleVendor(projectDir = ".", options = {}) {
|
|
|
339
342
|
options.nodePolyfillsPlugin ??
|
|
340
343
|
bundlerInfo.nodePolyfillsPlugin ??
|
|
341
344
|
(options.nodePolyfills === false ? null : undefined);
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
345
|
+
// The polyfill plugin is a Rolldown plugin; esbuild cannot use it.
|
|
346
|
+
const supportsPlugins =
|
|
347
|
+
typeof bundlerInfo.bundle === "function" || bundlerInfo.name === "rolldown";
|
|
348
|
+
const polyfillPlugin = supportsPlugins
|
|
349
|
+
? await loadNodePolyfillsPlugin(projectRoot, customPlugin)
|
|
350
|
+
: null;
|
|
346
351
|
|
|
347
352
|
if (typeof bundlerInfo.bundle === "function") {
|
|
348
353
|
try {
|