gofront 1.8.0 → 1.9.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 +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Source-like text of an expression for diagnostics ("cannot use y (variable
|
|
2
|
+
// of type float64) as int value"). Only the common shapes are printed in full;
|
|
3
|
+
// anything else falls back to "expression".
|
|
4
|
+
|
|
5
|
+
function litText(node) {
|
|
6
|
+
switch (node.litKind) {
|
|
7
|
+
case "STRING":
|
|
8
|
+
return JSON.stringify(node.value);
|
|
9
|
+
case "NIL":
|
|
10
|
+
return "nil";
|
|
11
|
+
default:
|
|
12
|
+
return String(node.value);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const PRINTERS = {
|
|
17
|
+
Ident: (n) => n.name,
|
|
18
|
+
BasicLit: litText,
|
|
19
|
+
ParenExpr: (n) => `(${exprText(n.expr)})`,
|
|
20
|
+
SelectorExpr: (n) => `${exprText(n.expr)}.${n.field}`,
|
|
21
|
+
UnaryExpr: (n) => `${n.op}${exprText(n.operand)}`,
|
|
22
|
+
BinaryExpr: (n) => `${exprText(n.left)} ${n.op} ${exprText(n.right)}`,
|
|
23
|
+
CallExpr: (n) =>
|
|
24
|
+
`${exprText(n.func)}(${(n.args ?? []).map(exprText).join(", ")})`,
|
|
25
|
+
IndexExpr: (n) => `${exprText(n.expr)}[${exprText(n.index)}]`,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function exprText(node) {
|
|
29
|
+
const print = node && PRINTERS[node.kind];
|
|
30
|
+
return print ? print(node) : "expression";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// "variable of type float64", "untyped int constant", "value of type float64".
|
|
34
|
+
export function operandKind(node, typeName, isUntypedConst) {
|
|
35
|
+
if (isUntypedConst)
|
|
36
|
+
return `untyped ${typeName === "float64" ? "float" : typeName} constant`;
|
|
37
|
+
return node?.kind === "Ident"
|
|
38
|
+
? `variable of type ${typeName}`
|
|
39
|
+
: `value of type ${typeName}`;
|
|
40
|
+
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ANY,
|
|
9
9
|
BASIC_TYPES,
|
|
10
10
|
COMPARABLE,
|
|
11
|
+
DECLARED_ANY,
|
|
11
12
|
defaultType,
|
|
12
13
|
displayTypeName,
|
|
13
14
|
isAny,
|
|
@@ -71,6 +72,30 @@ function typeSize(t) {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
// Whether `t` mentions one of the type parameters `params` below its root.
|
|
76
|
+
function nestsTypeParam(t, params) {
|
|
77
|
+
const mentions = (u) => {
|
|
78
|
+
switch (u?.kind) {
|
|
79
|
+
case "typeParam":
|
|
80
|
+
return params.includes(u);
|
|
81
|
+
case "pointer":
|
|
82
|
+
return mentions(u.base);
|
|
83
|
+
case "slice":
|
|
84
|
+
case "array":
|
|
85
|
+
return mentions(u.elem);
|
|
86
|
+
case "map":
|
|
87
|
+
return mentions(u.key) || mentions(u.value);
|
|
88
|
+
case "func":
|
|
89
|
+
return [...(u.params ?? []), ...(u.returns ?? [])].some(mentions);
|
|
90
|
+
case "named":
|
|
91
|
+
return (u.typeArgs ?? []).some(mentions);
|
|
92
|
+
default:
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return t?.kind !== "typeParam" && mentions(t);
|
|
97
|
+
}
|
|
98
|
+
|
|
74
99
|
function instanceKey(t) {
|
|
75
100
|
switch (t?.kind) {
|
|
76
101
|
case "named":
|
|
@@ -133,13 +158,34 @@ const SUBSTITUTE_DISPATCH = {
|
|
|
133
158
|
fields,
|
|
134
159
|
methods,
|
|
135
160
|
_embeds: type._embeds,
|
|
136
|
-
|
|
161
|
+
// The JSON codecs read the field types from here.
|
|
162
|
+
_structFields: type._structFields?.map((f) => ({
|
|
163
|
+
...f,
|
|
164
|
+
type: s.substituteType(f.type, map),
|
|
165
|
+
})),
|
|
137
166
|
};
|
|
138
167
|
},
|
|
139
168
|
interface: (s, type, map) => {
|
|
140
169
|
const methods = new Map();
|
|
141
170
|
for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
|
|
142
|
-
|
|
171
|
+
const out = { kind: "interface", name: type.name, methods };
|
|
172
|
+
// The type set follows the substitution (`interface{ ~[]E }` with E=int).
|
|
173
|
+
if (type.unionConstraint)
|
|
174
|
+
out.unionConstraint = {
|
|
175
|
+
...type.unionConstraint,
|
|
176
|
+
terms: type.unionConstraint.terms.map((term) => ({
|
|
177
|
+
...term,
|
|
178
|
+
resolved: s.substituteType(
|
|
179
|
+
term.resolved ?? s.resolveTypeNode(term.type, type._unionScope),
|
|
180
|
+
map,
|
|
181
|
+
),
|
|
182
|
+
})),
|
|
183
|
+
};
|
|
184
|
+
if (type._typeSetEmbeds)
|
|
185
|
+
out._typeSetEmbeds = type._typeSetEmbeds.map((e) =>
|
|
186
|
+
s.substituteType(e, map),
|
|
187
|
+
);
|
|
188
|
+
return out;
|
|
143
189
|
},
|
|
144
190
|
named: (s, type, map) => {
|
|
145
191
|
// An instance of a generic type: re-instantiate with the substituted
|
|
@@ -167,7 +213,8 @@ export const resolveMethods = {
|
|
|
167
213
|
if (!node) return ANY;
|
|
168
214
|
switch (node.kind) {
|
|
169
215
|
case "TypeName": {
|
|
170
|
-
if (BASIC_TYPES[node.name])
|
|
216
|
+
if (BASIC_TYPES[node.name])
|
|
217
|
+
return node.name === "any" ? DECLARED_ANY : BASIC_TYPES[node.name];
|
|
171
218
|
if (node.name === "comparable") return COMPARABLE;
|
|
172
219
|
if (scope) {
|
|
173
220
|
const fromScope = scope.lookup(node.name);
|
|
@@ -176,11 +223,11 @@ export const resolveMethods = {
|
|
|
176
223
|
}
|
|
177
224
|
const named = this.types.get(node.name);
|
|
178
225
|
if (named) {
|
|
179
|
-
const wasmErr = this._checkWasmStdlibTypeMember(node);
|
|
180
|
-
if (wasmErr) return wasmErr;
|
|
181
226
|
this._useQualifiedTypeName(node, named);
|
|
182
227
|
return named;
|
|
183
228
|
}
|
|
229
|
+
const jsOnly = this._jsOnlyMember(node.name);
|
|
230
|
+
if (jsOnly) return this._errJsOnly(jsOnly, node);
|
|
184
231
|
if (EXCLUDED_STDLIB_MEMBERS.has(node.name)) {
|
|
185
232
|
return this.err(
|
|
186
233
|
`${node.name} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(node.name)}`,
|
|
@@ -218,6 +265,13 @@ export const resolveMethods = {
|
|
|
218
265
|
return this._resolveStructType(node, scope);
|
|
219
266
|
case "InterfaceType":
|
|
220
267
|
return this._resolveInterfaceType(node, scope);
|
|
268
|
+
case "UnaryExpr":
|
|
269
|
+
// `new(*T)`: the parser reads the pointer type as a dereference.
|
|
270
|
+
if (node.op !== "*") return ANY;
|
|
271
|
+
return {
|
|
272
|
+
kind: "pointer",
|
|
273
|
+
base: this.resolveTypeNode(node.operand, scope),
|
|
274
|
+
};
|
|
221
275
|
case "Ident":
|
|
222
276
|
return this.resolveTypeNode(
|
|
223
277
|
{ kind: "TypeName", name: node.name },
|
|
@@ -252,23 +306,23 @@ export const resolveMethods = {
|
|
|
252
306
|
}
|
|
253
307
|
},
|
|
254
308
|
|
|
255
|
-
|
|
309
|
+
// `pkg.Name` when `pkg` is a Go-source package whose `js` files declare
|
|
310
|
+
// Name and this package is wasm or both (those files are not loaded).
|
|
311
|
+
_jsOnlyMember(qualified) {
|
|
256
312
|
if (this.target !== "wasm" && this.target !== "both") return null;
|
|
257
|
-
const dot =
|
|
258
|
-
if (dot
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
return null;
|
|
313
|
+
const dot = qualified.indexOf(".");
|
|
314
|
+
if (dot < 0) return null;
|
|
315
|
+
const ns = this.globals.lookup(qualified.slice(0, dot));
|
|
316
|
+
return ns?._jsOnly?.has(qualified.slice(dot + 1)) ? qualified : null;
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
_errJsOnly(qualified, node) {
|
|
320
|
+
this.recordBlocker("unsupported stdlib member", qualified);
|
|
321
|
+
return this.err(
|
|
322
|
+
`'${qualified}' is only available in js packages`,
|
|
323
|
+
node,
|
|
324
|
+
"move this call to a JS package",
|
|
325
|
+
);
|
|
272
326
|
},
|
|
273
327
|
|
|
274
328
|
// `pkg.T` in a type position counts as using the import; a Go-source
|
|
@@ -291,10 +345,25 @@ export const resolveMethods = {
|
|
|
291
345
|
try {
|
|
292
346
|
const lit = foldConstExpr(size, resolve);
|
|
293
347
|
const n = lit ? Number(lit.value) : Number.NaN;
|
|
294
|
-
|
|
348
|
+
if (Number.isInteger(n) && n >= 0) return n;
|
|
295
349
|
} catch {
|
|
296
|
-
|
|
350
|
+
// fall through to the checker's evaluator
|
|
297
351
|
}
|
|
352
|
+
// `[math.MaxInt8]T`: a package constant the AST folder cannot see.
|
|
353
|
+
const q = this._constIntValue(size, scope);
|
|
354
|
+
if (q === null || !Number.isInteger(q) || q < 0) return size;
|
|
355
|
+
this._useQualifiedConsts(size);
|
|
356
|
+
return q;
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
// `pkg.K` inside a folded array length counts as using the import.
|
|
360
|
+
_useQualifiedConsts(node) {
|
|
361
|
+
if (!node || typeof node !== "object") return;
|
|
362
|
+
if (node.kind === "SelectorExpr" && node.expr?.kind === "Ident")
|
|
363
|
+
this.globals.lookup(node.expr.name);
|
|
364
|
+
for (const key of ["expr", "left", "right", "operand"])
|
|
365
|
+
this._useQualifiedConsts(node[key]);
|
|
366
|
+
for (const arg of node.args ?? []) this._useQualifiedConsts(arg);
|
|
298
367
|
},
|
|
299
368
|
|
|
300
369
|
_resolveArrayTypeNode(node, scope) {
|
|
@@ -337,8 +406,11 @@ export const resolveMethods = {
|
|
|
337
406
|
? this.resolveTypeNode(node.expr, scope)
|
|
338
407
|
: null;
|
|
339
408
|
if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
|
|
340
|
-
if (qualified)
|
|
341
|
-
|
|
409
|
+
if (qualified) {
|
|
410
|
+
this.globals.lookup(sel.expr.name);
|
|
411
|
+
// `iter.Seq[int](f)`: the selector names the mangled stdlib type.
|
|
412
|
+
this._useQualifiedTypeName(sel, base);
|
|
413
|
+
} else if (name)
|
|
342
414
|
this._useQualifiedTypeName(node.name ? node : node.expr, base);
|
|
343
415
|
if (base.kind === "named" && base._generic?.isAlias) {
|
|
344
416
|
const aliasArgs = typeArgs.map((n) => this.resolveTypeNode(n, scope));
|
|
@@ -452,7 +524,10 @@ export const resolveMethods = {
|
|
|
452
524
|
}
|
|
453
525
|
}
|
|
454
526
|
const result = { kind: "interface", methods };
|
|
455
|
-
if (node.unionConstraint)
|
|
527
|
+
if (node.unionConstraint) {
|
|
528
|
+
result.unionConstraint = node.unionConstraint;
|
|
529
|
+
result._unionScope = scope;
|
|
530
|
+
}
|
|
456
531
|
if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
|
|
457
532
|
return result;
|
|
458
533
|
},
|
|
@@ -501,8 +576,15 @@ export const resolveMethods = {
|
|
|
501
576
|
// settles (`T[X]` containing `T[[]X]`), and doubling forms such as
|
|
502
577
|
// `T[Pair[X, X]]` blow up long before a depth limit is reached.
|
|
503
578
|
const top = generic._buildSizes[generic._buildSizes.length - 1];
|
|
579
|
+
// Inside a method of `G[T]`, `G[[]T]` makes every instance ask for a
|
|
580
|
+
// larger one, even if the method is never called.
|
|
581
|
+
const growsReceiver =
|
|
582
|
+
this._recvGeneric === generic &&
|
|
583
|
+
typeArgs.some((a) => nestsTypeParam(a, generic.typeParams));
|
|
504
584
|
if (
|
|
585
|
+
growsReceiver ||
|
|
505
586
|
building.length >= MAX_INSTANTIATION_DEPTH ||
|
|
587
|
+
(generic._syncDepth ?? 0) >= MAX_INSTANTIATION_DEPTH ||
|
|
506
588
|
(top !== undefined && size > top)
|
|
507
589
|
) {
|
|
508
590
|
if (!generic._cycleReported) {
|
|
@@ -557,13 +639,20 @@ export const resolveMethods = {
|
|
|
557
639
|
const methods = named.methods;
|
|
558
640
|
if (!methods || !named._instMap || named._syncing) return;
|
|
559
641
|
// A method signature that names the instance reaches this again.
|
|
642
|
+
// `func (s Stack[T]) Wrap() Stack[[]T]` makes every instance's methods
|
|
643
|
+
// ask for a larger instance: count the nesting to diagnose it.
|
|
560
644
|
named._syncing = true;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
645
|
+
generic._syncDepth = (generic._syncDepth ?? 0) + 1;
|
|
646
|
+
try {
|
|
647
|
+
for (const [mName, mType] of generic.methods ?? [])
|
|
648
|
+
if (!methods.has(mName))
|
|
649
|
+
methods.set(mName, this.substituteType(mType, named._instMap));
|
|
650
|
+
// Methods promoted from embedded structs (`Outer[T]` embedding `Base`).
|
|
651
|
+
this._promoteEmbeddedInto(named.underlying);
|
|
652
|
+
} finally {
|
|
653
|
+
generic._syncDepth--;
|
|
654
|
+
named._syncing = false;
|
|
655
|
+
}
|
|
567
656
|
},
|
|
568
657
|
|
|
569
658
|
// A[X] for `type A[P any] = T`: resolve T with each P bound to the
|
|
@@ -591,19 +680,53 @@ export const resolveMethods = {
|
|
|
591
680
|
|
|
592
681
|
// `explicit` seeds the leading type arguments of a partial instantiation
|
|
593
682
|
// (`F[int](x)`); the rest are inferred from the call arguments.
|
|
594
|
-
|
|
683
|
+
// `scope` resolves the type parameters of the calling function, so an
|
|
684
|
+
// argument of type `S ~[]E` unifies through its core type.
|
|
685
|
+
inferTypeArgs(genericType, argTypes, explicit = [], scope = null) {
|
|
595
686
|
const map = new Map();
|
|
687
|
+
const saved = this._inferScope;
|
|
688
|
+
this._inferScope = scope;
|
|
689
|
+
try {
|
|
690
|
+
return this._inferTypeArgs(genericType, argTypes, explicit, map);
|
|
691
|
+
} finally {
|
|
692
|
+
this._inferScope = saved;
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
|
|
696
|
+
_inferTypeArgs(genericType, argTypes, explicit, map) {
|
|
596
697
|
for (let i = 0; i < explicit.length; i++)
|
|
597
698
|
map.set(genericType.typeParams[i].name, explicit[i]);
|
|
598
699
|
const params = genericType.underlying.params;
|
|
599
700
|
for (let i = 0; i < params.length && i < argTypes.length; i++)
|
|
600
701
|
this._inferFromPair(params[i], argTypes[i], map);
|
|
702
|
+
this._inferFromCoreTypes(genericType.typeParams, map);
|
|
601
703
|
for (const tp of genericType.typeParams) {
|
|
602
704
|
if (!map.has(tp.name)) return null;
|
|
603
705
|
}
|
|
604
706
|
return genericType.typeParams.map((tp) => defaultType(map.get(tp.name)));
|
|
605
707
|
},
|
|
606
708
|
|
|
709
|
+
// Constraint type inference: a type parameter `S` constrained by `~[]E`
|
|
710
|
+
// whose argument is known unifies `E` with that argument's element type.
|
|
711
|
+
_inferFromCoreTypes(typeParams, map) {
|
|
712
|
+
const scope = new Scope(this.globals);
|
|
713
|
+
for (const tp of typeParams) scope.define(tp.name, tp);
|
|
714
|
+
for (let round = 0; round < typeParams.length; round++) {
|
|
715
|
+
const before = map.size;
|
|
716
|
+
for (const tp of typeParams) {
|
|
717
|
+
const arg = map.get(tp.name);
|
|
718
|
+
if (!arg || !tp.constraint) continue;
|
|
719
|
+
const terms = this._constraintTerms(tp.constraint, new Set(), scope);
|
|
720
|
+
if (terms?.length !== 1) continue;
|
|
721
|
+
const argUnder =
|
|
722
|
+
this._coreOfTypeParam(arg) ??
|
|
723
|
+
this._deepUnderlying(this.resolveType(arg));
|
|
724
|
+
if (argUnder) this._inferFromPair(terms[0], argUnder, map);
|
|
725
|
+
}
|
|
726
|
+
if (map.size === before) break;
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
|
|
607
730
|
_inferFromFuncPair(paramType, argType, map) {
|
|
608
731
|
for (
|
|
609
732
|
let i = 0;
|
|
@@ -622,6 +745,8 @@ export const resolveMethods = {
|
|
|
622
745
|
_inferFromStructuredPair(paramType, argType, map) {
|
|
623
746
|
if (paramType.kind === "slice" && argType.kind === "slice")
|
|
624
747
|
this._inferFromPair(paramType.elem, argType.elem, map);
|
|
748
|
+
if (paramType.kind === "pointer" && argType.kind === "pointer")
|
|
749
|
+
this._inferFromPair(paramType.base, argType.base, map);
|
|
625
750
|
if (paramType.kind === "map" && argType.kind === "map") {
|
|
626
751
|
this._inferFromPair(paramType.key, argType.key, map);
|
|
627
752
|
this._inferFromPair(paramType.value, argType.value, map);
|
|
@@ -638,8 +763,19 @@ export const resolveMethods = {
|
|
|
638
763
|
}
|
|
639
764
|
},
|
|
640
765
|
|
|
766
|
+
// An argument whose type is a type parameter of the calling function
|
|
767
|
+
// (`S ~[]E`) unifies through its core type: that type, or null.
|
|
768
|
+
// `paramType` (optional) is the parameter it is matched against; a bare
|
|
769
|
+
// type parameter takes the argument as it is.
|
|
770
|
+
_coreOfTypeParam(argType, paramType = null) {
|
|
771
|
+
if (argType.kind !== "typeParam" || !this._inferScope) return null;
|
|
772
|
+
if (paramType?.kind === "typeParam") return null;
|
|
773
|
+
return this._coreType(argType, this._inferScope);
|
|
774
|
+
},
|
|
775
|
+
|
|
641
776
|
_inferFromPair(paramType, argType, map) {
|
|
642
777
|
if (!paramType || !argType) return;
|
|
778
|
+
argType = this._coreOfTypeParam(argType, paramType) ?? argType;
|
|
643
779
|
if (paramType.kind === "typeParam") {
|
|
644
780
|
const prev = map.get(paramType.name);
|
|
645
781
|
if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
|
|
@@ -664,7 +800,8 @@ export const resolveMethods = {
|
|
|
664
800
|
_checkUnionConstraint(typeArg, constraint, node) {
|
|
665
801
|
for (const term of constraint.terms) {
|
|
666
802
|
if (term.approx) return;
|
|
667
|
-
const termType =
|
|
803
|
+
const termType =
|
|
804
|
+
term.resolved ?? this.resolveTypeNode(term.type, this.globals);
|
|
668
805
|
if (typeStr(typeArg) === typeStr(termType)) return;
|
|
669
806
|
}
|
|
670
807
|
this.err(`type ${typeStr(typeArg)} does not satisfy constraint`, node);
|
|
@@ -758,7 +895,7 @@ export const resolveMethods = {
|
|
|
758
895
|
return (
|
|
759
896
|
baseType.kind === "pointer" &&
|
|
760
897
|
field !== "value" &&
|
|
761
|
-
baseType.base?.kind === "named"
|
|
898
|
+
(baseType.base?.kind === "named" || baseType.base?.kind === "struct")
|
|
762
899
|
);
|
|
763
900
|
},
|
|
764
901
|
|
|
@@ -891,16 +1028,35 @@ export const resolveMethods = {
|
|
|
891
1028
|
return ANY;
|
|
892
1029
|
},
|
|
893
1030
|
|
|
894
|
-
|
|
1031
|
+
// An error for `field` when the namespace cannot offer it to this package:
|
|
1032
|
+
// an unexported name, or a member of a `js` file in a wasm or both package.
|
|
1033
|
+
_namespaceMemberError(base, field, node) {
|
|
895
1034
|
if (base._gofront && field.length > 0 && field[0] >= "a" && field[0] <= "z")
|
|
896
1035
|
return this.err(
|
|
897
1036
|
`cannot refer to unexported name ${base.name}.${field}`,
|
|
898
1037
|
node,
|
|
899
1038
|
);
|
|
1039
|
+
const wasm = this.target === "wasm" || this.target === "both";
|
|
1040
|
+
return wasm && base._jsOnly?.has(field)
|
|
1041
|
+
? this._errJsOnly(`${base.name}.${field}`, node)
|
|
1042
|
+
: null;
|
|
1043
|
+
},
|
|
1044
|
+
|
|
1045
|
+
_fieldTypeNamespace(base, field, node) {
|
|
1046
|
+
const bad = this._namespaceMemberError(base, field, node);
|
|
1047
|
+
if (bad) return bad;
|
|
900
1048
|
// Go-source stdlib member: backends emit the mangled top-level name.
|
|
901
1049
|
const mangled = base._mangle?.get(field);
|
|
902
1050
|
if (mangled) {
|
|
903
1051
|
node._stdName = mangled;
|
|
1052
|
+
// Lazy stdlib bodies: referencing a member checks what it reaches.
|
|
1053
|
+
const failure = base._reach?.(mangled);
|
|
1054
|
+
if (failure)
|
|
1055
|
+
this.err(
|
|
1056
|
+
`internal stdlib error in ${base._path}; please report`,
|
|
1057
|
+
node,
|
|
1058
|
+
failure,
|
|
1059
|
+
);
|
|
904
1060
|
return base.members[field];
|
|
905
1061
|
}
|
|
906
1062
|
if (field in base.members) {
|
|
@@ -15,15 +15,20 @@ const CHECK_STMT_DELEGATE = {
|
|
|
15
15
|
BranchStmt: "_checkBranchStmt",
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
import { exprText, operandKind } from "./operand-text.js";
|
|
18
19
|
import {
|
|
19
20
|
ANY,
|
|
20
21
|
BOOL,
|
|
21
22
|
defaultType,
|
|
23
|
+
is64BitInt,
|
|
22
24
|
isAny,
|
|
23
25
|
isBool,
|
|
26
|
+
isNil,
|
|
27
|
+
isUntyped,
|
|
24
28
|
isVoid,
|
|
25
29
|
iteratorYieldParams,
|
|
26
30
|
Scope,
|
|
31
|
+
typeStr,
|
|
27
32
|
} from "./types.js";
|
|
28
33
|
|
|
29
34
|
/** @typedef {import('./index.js').TypeChecker} TypeChecker */
|
|
@@ -67,9 +72,9 @@ export const statementCheckMethods = {
|
|
|
67
72
|
|
|
68
73
|
_checkDefineCommaOkAssert(stmt, rhs, scope) {
|
|
69
74
|
if (!stmt.rhs[0]?._commaOk) return false;
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
75
|
+
this._declareDefineVars(stmt, [rhs[0], BOOL], scope);
|
|
76
|
+
if (stmt.lhs.every((e) => e._redecl || (e.name ?? e) === "_"))
|
|
77
|
+
this.err("no new variables on left side of :=", stmt.lhs[0]);
|
|
73
78
|
stmt._rhsTypes = [rhs[0], BOOL];
|
|
74
79
|
return true;
|
|
75
80
|
},
|
|
@@ -97,10 +102,10 @@ export const statementCheckMethods = {
|
|
|
97
102
|
)
|
|
98
103
|
return false;
|
|
99
104
|
const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
|
|
100
|
-
const resolvedBase =
|
|
101
|
-
mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
|
|
105
|
+
const resolvedBase = this._underlyingOrCore(mapBaseType, scope);
|
|
102
106
|
if (resolvedBase?.kind !== "map") return false;
|
|
103
107
|
const keyType = this.checkExpr(stmt.rhs[0].index, scope);
|
|
108
|
+
this.assertAssignable(resolvedBase.key, keyType, stmt.rhs[0].index);
|
|
104
109
|
this._markIfaceKey(resolvedBase, stmt.rhs[0].index, keyType);
|
|
105
110
|
stmt._commaOkMap = true;
|
|
106
111
|
stmt.rhs[0]._mapValueType = resolvedBase.value;
|
|
@@ -129,7 +134,8 @@ export const statementCheckMethods = {
|
|
|
129
134
|
if (this._checkDefineCommaOkAssert(stmt, rhs, scope)) return;
|
|
130
135
|
if (
|
|
131
136
|
stmt.rhs[0]?.kind !== "RangeExpr" &&
|
|
132
|
-
stmt.lhs.length !== rhsFlat.length
|
|
137
|
+
stmt.lhs.length !== rhsFlat.length &&
|
|
138
|
+
!rhsFlat.some((t) => t?._tainted)
|
|
133
139
|
) {
|
|
134
140
|
const vars =
|
|
135
141
|
stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
|
|
@@ -156,10 +162,10 @@ export const statementCheckMethods = {
|
|
|
156
162
|
)
|
|
157
163
|
return null;
|
|
158
164
|
const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
|
|
159
|
-
const resolvedBase =
|
|
160
|
-
mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
|
|
165
|
+
const resolvedBase = this._underlyingOrCore(mapBaseType, scope);
|
|
161
166
|
if (resolvedBase?.kind !== "map") return null;
|
|
162
167
|
const keyType = this.checkExpr(stmt.rhs[0].index, scope);
|
|
168
|
+
this.assertAssignable(resolvedBase.key, keyType, stmt.rhs[0].index);
|
|
163
169
|
this._markIfaceKey(resolvedBase, stmt.rhs[0].index, keyType);
|
|
164
170
|
stmt._commaOkMap = true;
|
|
165
171
|
stmt.rhs[0]._mapValueType = resolvedBase.value;
|
|
@@ -211,7 +217,13 @@ export const statementCheckMethods = {
|
|
|
211
217
|
continue;
|
|
212
218
|
if (!isAny(lhsTypes[i]) && !isAny(r)) {
|
|
213
219
|
this._skipIfaceMark = true;
|
|
214
|
-
this.assertAssignable(
|
|
220
|
+
this.assertAssignable(
|
|
221
|
+
lhsTypes[i],
|
|
222
|
+
r,
|
|
223
|
+
stmt.lhs[i],
|
|
224
|
+
"assignment",
|
|
225
|
+
stmt.rhs.length === lhsTypes.length ? stmt.rhs[i] : undefined,
|
|
226
|
+
);
|
|
215
227
|
this._skipIfaceMark = false;
|
|
216
228
|
}
|
|
217
229
|
}
|
|
@@ -231,7 +243,8 @@ export const statementCheckMethods = {
|
|
|
231
243
|
if (
|
|
232
244
|
!stmt.rhs[0]?._commaOk &&
|
|
233
245
|
stmt.rhs[0]?.kind !== "RangeExpr" &&
|
|
234
|
-
stmt.lhs.length !== rhsFlat.length
|
|
246
|
+
stmt.lhs.length !== rhsFlat.length &&
|
|
247
|
+
!rhsFlat.some((t) => t?._tainted)
|
|
235
248
|
) {
|
|
236
249
|
const vars =
|
|
237
250
|
stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
|
|
@@ -321,6 +334,8 @@ export const statementCheckMethods = {
|
|
|
321
334
|
this.err("For condition must be bool", stmt);
|
|
322
335
|
} else {
|
|
323
336
|
const ct = this.checkExpr(stmt.cond.expr, inner);
|
|
337
|
+
const core = ct?.kind === "typeParam" ? this._coreType(ct, inner) : null;
|
|
338
|
+
if (core) stmt.cond.expr._coreType = core;
|
|
324
339
|
const info = iteratorYieldParams(ct);
|
|
325
340
|
if (info) {
|
|
326
341
|
stmt.cond._isIterator = true;
|
|
@@ -359,10 +374,17 @@ export const statementCheckMethods = {
|
|
|
359
374
|
const inner = new Scope(scope);
|
|
360
375
|
if (stmt.init) this.checkStmt(stmt.init, inner, returnType);
|
|
361
376
|
if (stmt.tag) this.checkExpr(stmt.tag, inner);
|
|
377
|
+
const tagIs64 = stmt.tag && is64BitInt(stmt.tag._type);
|
|
362
378
|
this._switchDepth++;
|
|
363
379
|
for (const c of stmt.cases) {
|
|
364
380
|
const caseScope = new Scope(inner);
|
|
365
|
-
if (c.list)
|
|
381
|
+
if (c.list) {
|
|
382
|
+
for (const e of c.list) {
|
|
383
|
+
this.checkExpr(e, caseScope);
|
|
384
|
+
this._checkCaseValue(stmt.tag, e);
|
|
385
|
+
if (tagIs64 && e._type?.kind === "untyped") e._is64Bit = true;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
366
388
|
for (const s of c.stmts) this.checkStmt(s, caseScope, returnType);
|
|
367
389
|
this._reportUnused(caseScope, stmt);
|
|
368
390
|
}
|
|
@@ -370,6 +392,76 @@ export const statementCheckMethods = {
|
|
|
370
392
|
this._reportUnused(inner, stmt);
|
|
371
393
|
},
|
|
372
394
|
|
|
395
|
+
// `case e:` compares `tag == e`, so `e` must convert to the tag's type
|
|
396
|
+
// (or the other way round for typed values).
|
|
397
|
+
_checkCaseValue(tag, e) {
|
|
398
|
+
const caseType = this.resolveType(e._type);
|
|
399
|
+
if (!tag) {
|
|
400
|
+
this._checkTaglessCase(e, caseType);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
let tagType = this.resolveType(tag._type);
|
|
404
|
+
// `switch "foo" {` compares as the constant's default type.
|
|
405
|
+
if (isUntyped(tagType) && !isNil(tagType) && tagType.base !== "bool")
|
|
406
|
+
tagType = defaultType(tagType);
|
|
407
|
+
if (this._caseValueUnchecked(tagType, caseType)) return;
|
|
408
|
+
if (isUntyped(caseType)) {
|
|
409
|
+
if (this._isUntypedAssignable(tagType, caseType)) return;
|
|
410
|
+
this.err(
|
|
411
|
+
`cannot convert ${exprText(e)} (${operandKind(e, caseType.base, true)}) to type ${typeStr(tagType)}`,
|
|
412
|
+
e,
|
|
413
|
+
);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (
|
|
417
|
+
this._assignableQuietly(tagType, caseType) ||
|
|
418
|
+
this._assignableQuietly(caseType, tagType)
|
|
419
|
+
)
|
|
420
|
+
return;
|
|
421
|
+
this.err(
|
|
422
|
+
`invalid case ${exprText(e)} in switch on ${exprText(tag)} (mismatched types ${typeStr(caseType)} and ${typeStr(tagType)})`,
|
|
423
|
+
e,
|
|
424
|
+
);
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
// A tagless `switch` is `switch true`, so every case must be boolean.
|
|
428
|
+
_checkTaglessCase(e, caseType) {
|
|
429
|
+
if (!caseType || caseType._tainted || caseType.kind === "typeParam") return;
|
|
430
|
+
if (isAny(caseType) || isNil(caseType) || isBool(caseType)) return;
|
|
431
|
+
if (isUntyped(caseType) && caseType.base === "bool") return;
|
|
432
|
+
const under = this.resolveType(caseType.underlying ?? caseType);
|
|
433
|
+
if (isBool(under)) return;
|
|
434
|
+
this.err(
|
|
435
|
+
`invalid case ${exprText(e)} in switch (mismatched types ${typeStr(caseType)} and bool)`,
|
|
436
|
+
e,
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
// Operands whose comparison the checker cannot (or need not) judge: unknown
|
|
441
|
+
// or recovered types, `any`/interface tags, nil cases, type parameters.
|
|
442
|
+
_caseValueUnchecked(tagType, caseType) {
|
|
443
|
+
if (!tagType || !caseType || tagType._tainted || caseType._tainted)
|
|
444
|
+
return true;
|
|
445
|
+
if (isAny(tagType) || isAny(caseType) || isNil(caseType)) return true;
|
|
446
|
+
if (isUntyped(tagType) || tagType.kind === "typeParam") return true;
|
|
447
|
+
return (
|
|
448
|
+
this.resolveType(tagType.underlying ?? tagType)?.kind === "interface"
|
|
449
|
+
);
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
// Whether assertAssignable(target, source) reports nothing.
|
|
453
|
+
_assignableQuietly(target, source) {
|
|
454
|
+
const saved = this.errors;
|
|
455
|
+
const savedSkip = this._skipIfaceMark;
|
|
456
|
+
this.errors = [];
|
|
457
|
+
this._skipIfaceMark = true;
|
|
458
|
+
this.assertAssignable(target, source, null);
|
|
459
|
+
const ok = this.errors.length === 0;
|
|
460
|
+
this.errors = saved;
|
|
461
|
+
this._skipIfaceMark = savedSkip;
|
|
462
|
+
return ok;
|
|
463
|
+
},
|
|
464
|
+
|
|
373
465
|
// A case naming an interface (literal or declared) records the resolved
|
|
374
466
|
// type so backends test its method set.
|
|
375
467
|
_markInterfaceCaseType(t, scope) {
|
|
@@ -383,6 +475,23 @@ export const statementCheckMethods = {
|
|
|
383
475
|
if (u?.kind === "interface") t._type = resolved;
|
|
384
476
|
},
|
|
385
477
|
|
|
478
|
+
// Records the resolved type of a composite type node used as an assertion
|
|
479
|
+
// or type-switch target (`[]int`, `map[string]any`, `*int`, `iter.Seq[int]`)
|
|
480
|
+
// so the backend can match it against the dynamic type of an interface value.
|
|
481
|
+
_markDynamicType(node, resolved) {
|
|
482
|
+
const kind = node?.kind;
|
|
483
|
+
if (
|
|
484
|
+
kind === "SliceType" ||
|
|
485
|
+
kind === "ArrayType" ||
|
|
486
|
+
kind === "MapType" ||
|
|
487
|
+
kind === "PointerType" ||
|
|
488
|
+
kind === "FuncType" ||
|
|
489
|
+
kind === "StructType" ||
|
|
490
|
+
kind === "GenericTypeName"
|
|
491
|
+
)
|
|
492
|
+
node._assertType = resolved;
|
|
493
|
+
},
|
|
494
|
+
|
|
386
495
|
_checkTypeSwitchStmt(stmt, scope, returnType) {
|
|
387
496
|
const inner = new Scope(scope);
|
|
388
497
|
this.checkExpr(stmt.expr, inner);
|
|
@@ -396,6 +505,8 @@ export const statementCheckMethods = {
|
|
|
396
505
|
const base = t?.kind === "PointerType" ? t.base : t;
|
|
397
506
|
if (base?.kind === "GenericTypeName") this.resolveTypeNode(t, inner);
|
|
398
507
|
this._markInterfaceCaseType(t, inner);
|
|
508
|
+
if (t && t.kind !== "TypeName" && t.kind !== "InterfaceType")
|
|
509
|
+
this._markDynamicType(t, this.resolveTypeNode(t, inner));
|
|
399
510
|
}
|
|
400
511
|
if (stmt.assign) {
|
|
401
512
|
// Bind the variable to the single case type, or any for multi/default.
|