gofront 1.7.0 → 1.8.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 +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Installed as a mixin on TypeChecker.prototype.
|
|
3
3
|
|
|
4
4
|
import { parseJsonTag } from "../parser/struct-tag.js";
|
|
5
|
+
import { foldConstExpr } from "./const-fold.js";
|
|
6
|
+
import { EXCLUDED_STDLIB_MEMBERS } from "./stdlib/excluded.js";
|
|
5
7
|
import {
|
|
6
8
|
ANY,
|
|
7
9
|
BASIC_TYPES,
|
|
8
10
|
COMPARABLE,
|
|
9
11
|
defaultType,
|
|
12
|
+
displayTypeName,
|
|
10
13
|
isAny,
|
|
11
14
|
isTypedArraySlice,
|
|
12
15
|
Scope,
|
|
@@ -20,6 +23,73 @@ import {
|
|
|
20
23
|
/** @typedef {import('./index.js').TypeChecker} TypeChecker */
|
|
21
24
|
|
|
22
25
|
// Dispatch table for substituteType — keyed by type.kind.
|
|
26
|
+
// Nesting depth of in-progress instances of one generic type before the
|
|
27
|
+
// instantiation is reported as a cycle.
|
|
28
|
+
const MAX_INSTANTIATION_DEPTH = 32;
|
|
29
|
+
|
|
30
|
+
// Two imported packages may both declare `Pt`, so the cache key of an
|
|
31
|
+
// instance tells declarations apart by identity instead of by display name.
|
|
32
|
+
const declIds = new WeakMap();
|
|
33
|
+
let nextDeclId = 0;
|
|
34
|
+
function declId(t) {
|
|
35
|
+
if (!declIds.has(t)) declIds.set(t, ++nextDeclId);
|
|
36
|
+
return declIds.get(t);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Go infers T from the "largest" untyped constant kind (int < rune < float <
|
|
40
|
+
// complex), and a typed argument beats any untyped one.
|
|
41
|
+
const UNTYPED_RANK = { int: 0, int32: 1, float64: 2, complex128: 3 };
|
|
42
|
+
function untypedWins(prev, next) {
|
|
43
|
+
if (prev.kind !== "untyped") return false;
|
|
44
|
+
if (next.kind !== "untyped") return true;
|
|
45
|
+
return (UNTYPED_RANK[next.base] ?? 0) > (UNTYPED_RANK[prev.base] ?? 0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Node count of a type, so a request that keeps growing can be told from one
|
|
49
|
+
// that merely repeats.
|
|
50
|
+
function typeSize(t) {
|
|
51
|
+
switch (t?.kind) {
|
|
52
|
+
case "named":
|
|
53
|
+
return 1 + (t.typeArgs ?? []).reduce((n, a) => n + typeSize(a), 0);
|
|
54
|
+
case "pointer":
|
|
55
|
+
return 1 + typeSize(t.base);
|
|
56
|
+
case "slice":
|
|
57
|
+
case "array":
|
|
58
|
+
return 1 + typeSize(t.elem);
|
|
59
|
+
case "map":
|
|
60
|
+
return 1 + typeSize(t.key) + typeSize(t.value);
|
|
61
|
+
case "func":
|
|
62
|
+
return (
|
|
63
|
+
1 +
|
|
64
|
+
[...(t.params ?? []), ...(t.returns ?? [])].reduce(
|
|
65
|
+
(n, a) => n + typeSize(a),
|
|
66
|
+
0,
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
default:
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function instanceKey(t) {
|
|
75
|
+
switch (t?.kind) {
|
|
76
|
+
case "named":
|
|
77
|
+
return t.typeArgs?.length
|
|
78
|
+
? `${typeStr(t)}`
|
|
79
|
+
: `${typeStr(t)}#${declId(t)}`;
|
|
80
|
+
case "pointer":
|
|
81
|
+
return `*${instanceKey(t.base)}`;
|
|
82
|
+
case "slice":
|
|
83
|
+
return `[]${instanceKey(t.elem)}`;
|
|
84
|
+
case "array":
|
|
85
|
+
return `[${t.size ?? "..."}]${instanceKey(t.elem)}`;
|
|
86
|
+
case "map":
|
|
87
|
+
return `map[${instanceKey(t.key)}]${instanceKey(t.value)}`;
|
|
88
|
+
default:
|
|
89
|
+
return typeStr(t);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
23
93
|
const SUBSTITUTE_DISPATCH = {
|
|
24
94
|
basic: (_s, type) => type,
|
|
25
95
|
untyped: (_s, type) => type,
|
|
@@ -63,6 +133,7 @@ const SUBSTITUTE_DISPATCH = {
|
|
|
63
133
|
fields,
|
|
64
134
|
methods,
|
|
65
135
|
_embeds: type._embeds,
|
|
136
|
+
_structFields: type._structFields,
|
|
66
137
|
};
|
|
67
138
|
},
|
|
68
139
|
interface: (s, type, map) => {
|
|
@@ -70,11 +141,24 @@ const SUBSTITUTE_DISPATCH = {
|
|
|
70
141
|
for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
|
|
71
142
|
return { kind: "interface", name: type.name, methods };
|
|
72
143
|
},
|
|
73
|
-
named: (s, type, map) =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
underlying
|
|
77
|
-
|
|
144
|
+
named: (s, type, map) => {
|
|
145
|
+
// An instance of a generic type: re-instantiate with the substituted
|
|
146
|
+
// arguments (through the instance cache) rather than copying its
|
|
147
|
+
// possibly cyclic underlying type.
|
|
148
|
+
if (type._genericOf)
|
|
149
|
+
return s.instantiateGenericWithArgs(
|
|
150
|
+
type._genericOf,
|
|
151
|
+
type.typeArgs.map((t) => s.substituteType(t, map)),
|
|
152
|
+
);
|
|
153
|
+
return {
|
|
154
|
+
kind: "named",
|
|
155
|
+
name: type.name,
|
|
156
|
+
...(type.typeArgs && {
|
|
157
|
+
typeArgs: type.typeArgs.map((t) => s.substituteType(t, map)),
|
|
158
|
+
}),
|
|
159
|
+
underlying: s.substituteType(type.underlying, map),
|
|
160
|
+
};
|
|
161
|
+
},
|
|
78
162
|
};
|
|
79
163
|
|
|
80
164
|
/** @type {ThisType<TypeChecker>} */
|
|
@@ -88,40 +172,27 @@ export const resolveMethods = {
|
|
|
88
172
|
if (scope) {
|
|
89
173
|
const fromScope = scope.lookup(node.name);
|
|
90
174
|
if (fromScope?.kind === "typeParam") return fromScope;
|
|
175
|
+
if (fromScope?.kind === "aliasArg") return fromScope.type;
|
|
91
176
|
}
|
|
92
177
|
const named = this.types.get(node.name);
|
|
93
178
|
if (named) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
179
|
+
const wasmErr = this._checkWasmStdlibTypeMember(node);
|
|
180
|
+
if (wasmErr) return wasmErr;
|
|
181
|
+
this._useQualifiedTypeName(node, named);
|
|
97
182
|
return named;
|
|
98
183
|
}
|
|
184
|
+
if (EXCLUDED_STDLIB_MEMBERS.has(node.name)) {
|
|
185
|
+
return this.err(
|
|
186
|
+
`${node.name} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(node.name)}`,
|
|
187
|
+
node,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
99
190
|
return this.err(`Unknown type '${node.name}'`, node);
|
|
100
191
|
}
|
|
101
192
|
case "GenericTypeName":
|
|
102
193
|
case "InstantiationExpr":
|
|
103
|
-
case "IndexExpr":
|
|
104
|
-
|
|
105
|
-
node.name ?? (node.expr?.kind === "Ident" ? node.expr.name : null);
|
|
106
|
-
const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
|
|
107
|
-
const base = name
|
|
108
|
-
? this.types.get(name)
|
|
109
|
-
: node.expr
|
|
110
|
-
? this.resolveTypeNode(node.expr, scope)
|
|
111
|
-
: null;
|
|
112
|
-
if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
|
|
113
|
-
if (base.kind === "named" && base._generic) {
|
|
114
|
-
const res = this.instantiateGenericType(
|
|
115
|
-
base._generic,
|
|
116
|
-
typeArgs,
|
|
117
|
-
scope,
|
|
118
|
-
);
|
|
119
|
-
node._typeArgs = res.typeArgs;
|
|
120
|
-
node._genericType = base;
|
|
121
|
-
return res;
|
|
122
|
-
}
|
|
123
|
-
return this.err(`Type '${name ?? "?"}' is not generic`, node);
|
|
124
|
-
}
|
|
194
|
+
case "IndexExpr":
|
|
195
|
+
return this._resolveGenericTypeName(node, scope);
|
|
125
196
|
case "TypeParam": {
|
|
126
197
|
const constraint = node.constraint
|
|
127
198
|
? this.resolveTypeNode(node.constraint, scope)
|
|
@@ -157,11 +228,14 @@ export const resolveMethods = {
|
|
|
157
228
|
// SelectorExpr; resolve it like a qualified TypeName.
|
|
158
229
|
if (node.expr?.kind === "Ident" && typeof node.field === "string") {
|
|
159
230
|
const qualified = `${node.expr.name}.${node.field}`;
|
|
160
|
-
if (this.types.has(qualified))
|
|
161
|
-
|
|
231
|
+
if (this.types.has(qualified)) {
|
|
232
|
+
const resolved = this.resolveTypeNode(
|
|
162
233
|
{ kind: "TypeName", name: qualified },
|
|
163
234
|
scope,
|
|
164
235
|
);
|
|
236
|
+
this._useQualifiedTypeName(node, resolved);
|
|
237
|
+
return resolved;
|
|
238
|
+
}
|
|
165
239
|
}
|
|
166
240
|
return ANY;
|
|
167
241
|
case "ArrayType":
|
|
@@ -178,24 +252,116 @@ export const resolveMethods = {
|
|
|
178
252
|
}
|
|
179
253
|
},
|
|
180
254
|
|
|
255
|
+
_checkWasmStdlibTypeMember(node) {
|
|
256
|
+
if (this.target !== "wasm" && this.target !== "both") return null;
|
|
257
|
+
const dot = node.name.indexOf(".");
|
|
258
|
+
if (dot === -1) return null;
|
|
259
|
+
const pkg = node.name.slice(0, dot);
|
|
260
|
+
if (pkg !== "time") return null;
|
|
261
|
+
const field = node.name.slice(dot + 1);
|
|
262
|
+
const allowed = WASM_STDLIB_MEMBERS.get(pkg);
|
|
263
|
+
if (allowed && !allowed.has(field)) {
|
|
264
|
+
this.recordBlocker("unsupported stdlib member", node.name);
|
|
265
|
+
return this.err(
|
|
266
|
+
`'${node.name}' is not yet available in wasm packages`,
|
|
267
|
+
node,
|
|
268
|
+
"move this call to a JS package, or use a supported alternative",
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// `pkg.T` in a type position counts as using the import; a Go-source
|
|
275
|
+
// stdlib type is emitted under its mangled name.
|
|
276
|
+
_useQualifiedTypeName(node, named) {
|
|
277
|
+
const name =
|
|
278
|
+
node.name ?? (node.expr ? `${node.expr.name}.${node.field}` : "");
|
|
279
|
+
const dot = name.indexOf(".");
|
|
280
|
+
if (dot <= 0) return;
|
|
281
|
+
this.globals.lookup(name.slice(0, dot));
|
|
282
|
+
if (named.name?.includes("$$")) node._stdName = named.name;
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
// `[N]T` with a named constant (or constant expression) length folds to a number.
|
|
286
|
+
_foldArrayLen(size, scope) {
|
|
287
|
+
const resolve = (name, depth = 0) => {
|
|
288
|
+
const e = depth < 16 ? scope.lookupConstExpr(name) : null;
|
|
289
|
+
return e ? foldConstExpr(e, (n) => resolve(n, depth + 1)) : undefined;
|
|
290
|
+
};
|
|
291
|
+
try {
|
|
292
|
+
const lit = foldConstExpr(size, resolve);
|
|
293
|
+
const n = lit ? Number(lit.value) : Number.NaN;
|
|
294
|
+
return Number.isInteger(n) && n >= 0 ? n : size;
|
|
295
|
+
} catch {
|
|
296
|
+
return size;
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
|
|
181
300
|
_resolveArrayTypeNode(node, scope) {
|
|
301
|
+
// Fold in place so backends that read the type node see a literal length.
|
|
302
|
+
if (!node.inferLen && node.size?.value === undefined) {
|
|
303
|
+
const n = this._foldArrayLen(node.size, scope);
|
|
304
|
+
if (typeof n === "number")
|
|
305
|
+
node.size = {
|
|
306
|
+
kind: "BasicLit",
|
|
307
|
+
litKind: "INT",
|
|
308
|
+
value: String(n),
|
|
309
|
+
_line: node.size._line,
|
|
310
|
+
_col: node.size._col,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
182
313
|
return {
|
|
183
314
|
kind: "array",
|
|
184
315
|
size: node.inferLen
|
|
185
316
|
? null
|
|
186
317
|
: node.size?.value !== undefined
|
|
187
318
|
? Number(node.size.value)
|
|
188
|
-
: node.size,
|
|
319
|
+
: this._foldArrayLen(node.size, scope),
|
|
189
320
|
elem: this.resolveTypeNode(node.elem, scope),
|
|
190
321
|
};
|
|
191
322
|
},
|
|
192
323
|
|
|
324
|
+
// `G[X]`: instantiate a generic named type, or expand a generic alias.
|
|
325
|
+
_resolveGenericTypeName(node, scope) {
|
|
326
|
+
const sel = node.expr?.kind === "SelectorExpr" ? node.expr : null;
|
|
327
|
+
const qualified =
|
|
328
|
+
sel?.expr?.kind === "Ident" ? `${sel.expr.name}.${sel.field}` : null;
|
|
329
|
+
const name =
|
|
330
|
+
node.name ??
|
|
331
|
+
(node.expr?.kind === "Ident" ? node.expr.name : null) ??
|
|
332
|
+
qualified;
|
|
333
|
+
const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
|
|
334
|
+
const base = name
|
|
335
|
+
? this.types.get(name)
|
|
336
|
+
: node.expr
|
|
337
|
+
? this.resolveTypeNode(node.expr, scope)
|
|
338
|
+
: null;
|
|
339
|
+
if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
|
|
340
|
+
if (qualified) this.globals.lookup(sel.expr.name);
|
|
341
|
+
else if (name)
|
|
342
|
+
this._useQualifiedTypeName(node.name ? node : node.expr, base);
|
|
343
|
+
if (base.kind === "named" && base._generic?.isAlias) {
|
|
344
|
+
const aliasArgs = typeArgs.map((n) => this.resolveTypeNode(n, scope));
|
|
345
|
+
node._typeArgs = aliasArgs;
|
|
346
|
+
return this._instantiateGenericAlias(base._generic, aliasArgs);
|
|
347
|
+
}
|
|
348
|
+
if (base.kind === "named" && base._generic) {
|
|
349
|
+
const res = this.instantiateGenericType(base._generic, typeArgs, scope);
|
|
350
|
+
node._typeArgs = res.typeArgs;
|
|
351
|
+
node._genericType = base;
|
|
352
|
+
return res;
|
|
353
|
+
}
|
|
354
|
+
return this.err(`Type '${name ?? "?"}' is not generic`, node);
|
|
355
|
+
},
|
|
356
|
+
|
|
193
357
|
_resolveFuncTypeNode(node, scope) {
|
|
194
358
|
const params = node.params.map((p) => this.resolveTypeNode(p.type, scope));
|
|
195
359
|
const returns = node.returnType
|
|
196
360
|
? [this.resolveTypeNode(node.returnType, scope)]
|
|
197
361
|
: [VOID];
|
|
198
|
-
|
|
362
|
+
const fn = { kind: "func", params, returns };
|
|
363
|
+
if (node.params.at(-1)?.variadic) fn.variadic = true;
|
|
364
|
+
return fn;
|
|
199
365
|
},
|
|
200
366
|
|
|
201
367
|
resolveTypeNodeName(node, scope) {
|
|
@@ -223,6 +389,7 @@ export const resolveMethods = {
|
|
|
223
389
|
if (f.embedded) {
|
|
224
390
|
const inner = ft.kind === "pointer" ? ft.base : ft;
|
|
225
391
|
const embedName = (
|
|
392
|
+
f.embeddedName ??
|
|
226
393
|
f.astType?.name ??
|
|
227
394
|
f.type?.name ??
|
|
228
395
|
f.type?.base?.name ??
|
|
@@ -262,6 +429,9 @@ export const resolveMethods = {
|
|
|
262
429
|
if (isVariadic) mType.variadic = true;
|
|
263
430
|
methods.set(m.name, mType);
|
|
264
431
|
}
|
|
432
|
+
// Embedded interfaces that carry a type set (`interface{ Integer }`):
|
|
433
|
+
// kept so type-parameter operator checks can follow them.
|
|
434
|
+
const typeSetEmbeds = [];
|
|
265
435
|
if (node.embeds) {
|
|
266
436
|
for (const embed of node.embeds) {
|
|
267
437
|
const resolved = this.resolveTypeNode(embed, scope);
|
|
@@ -271,6 +441,8 @@ export const resolveMethods = {
|
|
|
271
441
|
for (const [mName, mType] of base.methods) {
|
|
272
442
|
if (!methods.has(mName)) methods.set(mName, mType);
|
|
273
443
|
}
|
|
444
|
+
if (base.unionConstraint || base._typeSetEmbeds)
|
|
445
|
+
typeSetEmbeds.push(base);
|
|
274
446
|
} else if (!isAny(resolved)) {
|
|
275
447
|
this.err(
|
|
276
448
|
`cannot embed non-interface type ${typeStr(resolved)}`,
|
|
@@ -281,6 +453,7 @@ export const resolveMethods = {
|
|
|
281
453
|
}
|
|
282
454
|
const result = { kind: "interface", methods };
|
|
283
455
|
if (node.unionConstraint) result.unionConstraint = node.unionConstraint;
|
|
456
|
+
if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
|
|
284
457
|
return result;
|
|
285
458
|
},
|
|
286
459
|
|
|
@@ -301,36 +474,127 @@ export const resolveMethods = {
|
|
|
301
474
|
},
|
|
302
475
|
|
|
303
476
|
instantiateGenericType(generic, typeArgNodes, scope) {
|
|
304
|
-
const map = new Map();
|
|
305
477
|
const typeArgs = typeArgNodes.map((n) => this.resolveTypeNode(n, scope));
|
|
478
|
+
return this.instantiateGenericWithArgs(generic, typeArgs);
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
// One instance per type-argument list. The instance is cached before its
|
|
482
|
+
// fields are resolved, so a field that names the instance again (`Next
|
|
483
|
+
// *List[T]`, `Children []Tree[T]`, or a cycle through another generic type)
|
|
484
|
+
// reaches the same object instead of recursing forever.
|
|
485
|
+
instantiateGenericWithArgs(generic, typeArgs) {
|
|
486
|
+
const key = typeArgs.map(instanceKey).join(",");
|
|
487
|
+
generic._instances ??= new Map();
|
|
488
|
+
const cached = generic._instances.get(key);
|
|
489
|
+
if (cached) {
|
|
490
|
+
this._syncInstanceMethods(generic, cached);
|
|
491
|
+
return cached;
|
|
492
|
+
}
|
|
493
|
+
// `type T[X any] struct{ n *T[[]X] }` asks for T[[]X], then T[[][]X], and
|
|
494
|
+
// so on: an instance still being built that keeps asking for a larger
|
|
495
|
+
// one never settles.
|
|
496
|
+
generic._building ??= [];
|
|
497
|
+
generic._buildSizes ??= [];
|
|
498
|
+
const building = generic._building;
|
|
499
|
+
const size = typeArgs.reduce((n, a) => n + typeSize(a), 0);
|
|
500
|
+
// A request for a larger instance while one is being built never
|
|
501
|
+
// settles (`T[X]` containing `T[[]X]`), and doubling forms such as
|
|
502
|
+
// `T[Pair[X, X]]` blow up long before a depth limit is reached.
|
|
503
|
+
const top = generic._buildSizes[generic._buildSizes.length - 1];
|
|
504
|
+
if (
|
|
505
|
+
building.length >= MAX_INSTANTIATION_DEPTH ||
|
|
506
|
+
(top !== undefined && size > top)
|
|
507
|
+
) {
|
|
508
|
+
if (!generic._cycleReported) {
|
|
509
|
+
generic._cycleReported = true;
|
|
510
|
+
this.err(
|
|
511
|
+
`instantiation cycle: ${generic.declNode.name} instantiates itself with ever larger type arguments`,
|
|
512
|
+
generic.declNode,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
return ANY;
|
|
516
|
+
}
|
|
517
|
+
const map = new Map();
|
|
306
518
|
for (let i = 0; i < generic.typeParams.length; i++)
|
|
307
519
|
map.set(generic.typeParams[i].name, typeArgs[i] ?? ANY);
|
|
520
|
+
const named = {
|
|
521
|
+
kind: "named",
|
|
522
|
+
name: generic.declNode.name,
|
|
523
|
+
typeArgs,
|
|
524
|
+
underlying: null,
|
|
525
|
+
_genericOf: generic,
|
|
526
|
+
};
|
|
527
|
+
generic._instances.set(key, named);
|
|
308
528
|
// Resolve the declaration with its type params in scope (as typeParams,
|
|
309
529
|
// never as value symbols), then substitute the concrete arguments.
|
|
310
530
|
const typeScope = new Scope(this.globals);
|
|
311
531
|
for (const tp of generic.typeParams) typeScope.define(tp.name, tp);
|
|
312
|
-
|
|
313
|
-
|
|
532
|
+
building.push(key);
|
|
533
|
+
generic._buildSizes.push(size);
|
|
534
|
+
let instantiated;
|
|
535
|
+
try {
|
|
536
|
+
const underlying = this.resolveTypeNode(generic.declNode.type, typeScope);
|
|
537
|
+
instantiated = this.substituteType(underlying, map);
|
|
538
|
+
} finally {
|
|
539
|
+
building.pop();
|
|
540
|
+
generic._buildSizes.pop();
|
|
541
|
+
}
|
|
314
542
|
if (instantiated.kind === "struct") {
|
|
315
543
|
instantiated.name = generic.declNode.name;
|
|
316
544
|
instantiated.typeArgs = typeArgs;
|
|
317
545
|
if (!instantiated.methods) instantiated.methods = new Map();
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
546
|
+
}
|
|
547
|
+
named.underlying = instantiated;
|
|
548
|
+
named.methods = instantiated.methods;
|
|
549
|
+
named._instMap = map;
|
|
550
|
+
this._syncInstanceMethods(generic, named);
|
|
551
|
+
return named;
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
// Methods are collected after types, so an instance created earlier picks
|
|
555
|
+
// up the methods declared since.
|
|
556
|
+
_syncInstanceMethods(generic, named) {
|
|
557
|
+
const methods = named.methods;
|
|
558
|
+
if (!methods || !named._instMap || named._syncing) return;
|
|
559
|
+
// A method signature that names the instance reaches this again.
|
|
560
|
+
named._syncing = true;
|
|
561
|
+
for (const [mName, mType] of generic.methods ?? [])
|
|
562
|
+
if (!methods.has(mName))
|
|
563
|
+
methods.set(mName, this.substituteType(mType, named._instMap));
|
|
564
|
+
// Methods promoted from embedded structs (`Outer[T]` embedding `Base`).
|
|
565
|
+
this._promoteEmbeddedInto(named.underlying);
|
|
566
|
+
named._syncing = false;
|
|
567
|
+
},
|
|
568
|
+
|
|
569
|
+
// A[X] for `type A[P any] = T`: resolve T with each P bound to the
|
|
570
|
+
// concrete X, so A[int] is exactly T[int] (generic named types inside T
|
|
571
|
+
// are instantiated normally and keep their identity).
|
|
572
|
+
_instantiateGenericAlias(generic, typeArgs) {
|
|
573
|
+
const { typeParams, declNode } = generic;
|
|
574
|
+
if (typeArgs.length !== typeParams.length) {
|
|
575
|
+
return this.err(
|
|
576
|
+
`got ${typeArgs.length} type arguments but ${declNode.name} has ${typeParams.length} type parameters`,
|
|
577
|
+
declNode,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
const aliasScope = new Scope(this.globals);
|
|
581
|
+
for (let i = 0; i < typeParams.length; i++) {
|
|
582
|
+
const tp = typeParams[i];
|
|
583
|
+
if (tp.constraint) {
|
|
584
|
+
const constraint = this.resolveTypeNode(tp.constraint, this.globals);
|
|
585
|
+
this.checkConstraint(typeArgs[i], constraint, declNode);
|
|
321
586
|
}
|
|
587
|
+
aliasScope.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
|
|
322
588
|
}
|
|
323
|
-
return
|
|
324
|
-
kind: "named",
|
|
325
|
-
name: generic.declNode.name,
|
|
326
|
-
typeArgs,
|
|
327
|
-
underlying: instantiated,
|
|
328
|
-
methods: instantiated.methods,
|
|
329
|
-
};
|
|
589
|
+
return this.resolveTypeNode(declNode.type, aliasScope);
|
|
330
590
|
},
|
|
331
591
|
|
|
332
|
-
|
|
592
|
+
// `explicit` seeds the leading type arguments of a partial instantiation
|
|
593
|
+
// (`F[int](x)`); the rest are inferred from the call arguments.
|
|
594
|
+
inferTypeArgs(genericType, argTypes, explicit = []) {
|
|
333
595
|
const map = new Map();
|
|
596
|
+
for (let i = 0; i < explicit.length; i++)
|
|
597
|
+
map.set(genericType.typeParams[i].name, explicit[i]);
|
|
334
598
|
const params = genericType.underlying.params;
|
|
335
599
|
for (let i = 0; i < params.length && i < argTypes.length; i++)
|
|
336
600
|
this._inferFromPair(params[i], argTypes[i], map);
|
|
@@ -364,14 +628,36 @@ export const resolveMethods = {
|
|
|
364
628
|
}
|
|
365
629
|
if (paramType.kind === "func" && argType.kind === "func")
|
|
366
630
|
this._inferFromFuncPair(paramType, argType, map);
|
|
631
|
+
if (paramType.kind === "tuple" && argType.kind === "tuple") {
|
|
632
|
+
for (
|
|
633
|
+
let i = 0;
|
|
634
|
+
i < paramType.types.length && i < argType.types.length;
|
|
635
|
+
i++
|
|
636
|
+
)
|
|
637
|
+
this._inferFromPair(paramType.types[i], argType.types[i], map);
|
|
638
|
+
}
|
|
367
639
|
},
|
|
368
640
|
|
|
369
641
|
_inferFromPair(paramType, argType, map) {
|
|
370
642
|
if (!paramType || !argType) return;
|
|
371
643
|
if (paramType.kind === "typeParam") {
|
|
372
|
-
|
|
644
|
+
const prev = map.get(paramType.name);
|
|
645
|
+
if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
|
|
373
646
|
return;
|
|
374
647
|
}
|
|
648
|
+
if (paramType.kind === "named" && paramType.typeArgs) {
|
|
649
|
+
// `iter.Seq[E]` against `iter.Seq[int]`: unify the type arguments;
|
|
650
|
+
// against any other type, its underlying type.
|
|
651
|
+
if (argType.kind === "named" && argType.name === paramType.name) {
|
|
652
|
+
const args = argType.typeArgs ?? [];
|
|
653
|
+
for (let i = 0; i < paramType.typeArgs.length; i++)
|
|
654
|
+
this._inferFromPair(paramType.typeArgs[i], args[i], map);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
paramType = paramType.underlying;
|
|
658
|
+
}
|
|
659
|
+
if (argType.kind === "named" && argType.underlying)
|
|
660
|
+
argType = argType.underlying;
|
|
375
661
|
this._inferFromStructuredPair(paramType, argType, map);
|
|
376
662
|
},
|
|
377
663
|
|
|
@@ -422,11 +708,37 @@ export const resolveMethods = {
|
|
|
422
708
|
(!t.underlying || !t.methods)
|
|
423
709
|
) {
|
|
424
710
|
const found = this.types.get(t.name);
|
|
425
|
-
|
|
711
|
+
// Two imported packages can both declare `Pt`; the bare name only
|
|
712
|
+
// reaches the last one registered, so a declaration that came from
|
|
713
|
+
// an import's own type table stays itself.
|
|
714
|
+
if (found && !(t.underlying && this._ambiguousImported?.has(t)))
|
|
715
|
+
return found;
|
|
426
716
|
}
|
|
717
|
+
if (t.kind === "named" && t.typeArgs?.length)
|
|
718
|
+
return this._withGenericMethods(t);
|
|
427
719
|
return t;
|
|
428
720
|
},
|
|
429
721
|
|
|
722
|
+
// A substituted generic instance (e.g. Stack[int] from an inferred call)
|
|
723
|
+
// drops the methods map; re-derive it from the generic declaration.
|
|
724
|
+
_withGenericMethods(t) {
|
|
725
|
+
const decl = this.types.get(t.name);
|
|
726
|
+
const generic = decl?._generic;
|
|
727
|
+
// Struct generics only: codegen has no receiver support for generic
|
|
728
|
+
// slice/map/func named types, so those keep the baseline diagnostics.
|
|
729
|
+
if (this.resolveType(decl?.underlying)?.kind !== "struct") return t;
|
|
730
|
+
if (!generic?.methods || generic.methods.size === 0) return t;
|
|
731
|
+
if (t.methods && generic.methods.size <= t.methods.size) return t;
|
|
732
|
+
const map = new Map();
|
|
733
|
+
for (let i = 0; i < generic.typeParams.length; i++)
|
|
734
|
+
map.set(generic.typeParams[i].name, t.typeArgs[i] ?? ANY);
|
|
735
|
+
const methods = new Map(t.methods);
|
|
736
|
+
for (const [name, mType] of generic.methods)
|
|
737
|
+
if (!methods.has(name))
|
|
738
|
+
methods.set(name, this.substituteType(mType, map));
|
|
739
|
+
return { ...t, methods };
|
|
740
|
+
},
|
|
741
|
+
|
|
430
742
|
// ── Field type lookup ─────────────────────────────────────────────
|
|
431
743
|
|
|
432
744
|
fieldType(baseType, field, node) {
|
|
@@ -490,12 +802,54 @@ export const resolveMethods = {
|
|
|
490
802
|
return base.fields.get(field);
|
|
491
803
|
}
|
|
492
804
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
805
|
+
const typeName = baseType?.name || base.name;
|
|
806
|
+
if (typeName) {
|
|
807
|
+
const dName = displayTypeName(typeName);
|
|
808
|
+
const shortName = typeName.includes("$$")
|
|
809
|
+
? typeName.split("$$")[0]
|
|
810
|
+
: typeName.includes(".")
|
|
811
|
+
? typeName.split(".").pop()
|
|
812
|
+
: typeName;
|
|
813
|
+
for (const key of [
|
|
814
|
+
`${dName}.${field}`,
|
|
815
|
+
`${typeName}.${field}`,
|
|
816
|
+
`${shortName}.${field}`,
|
|
817
|
+
]) {
|
|
818
|
+
if (EXCLUDED_STDLIB_MEMBERS.has(key)) {
|
|
819
|
+
return this.err(
|
|
820
|
+
`${dName}.${field} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(key)}`,
|
|
821
|
+
node,
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
493
826
|
return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
|
|
494
827
|
},
|
|
495
828
|
|
|
496
829
|
_fieldTypeInterface(base, baseType, field, node) {
|
|
497
830
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
498
831
|
if (base.fields?.has(field)) return base.fields.get(field);
|
|
832
|
+
const typeName = baseType?.name || base.name;
|
|
833
|
+
if (typeName) {
|
|
834
|
+
const dName = displayTypeName(typeName);
|
|
835
|
+
const shortName = typeName.includes("$$")
|
|
836
|
+
? typeName.split("$$")[0]
|
|
837
|
+
: typeName.includes(".")
|
|
838
|
+
? typeName.split(".").pop()
|
|
839
|
+
: typeName;
|
|
840
|
+
for (const key of [
|
|
841
|
+
`${dName}.${field}`,
|
|
842
|
+
`${typeName}.${field}`,
|
|
843
|
+
`${shortName}.${field}`,
|
|
844
|
+
]) {
|
|
845
|
+
if (EXCLUDED_STDLIB_MEMBERS.has(key)) {
|
|
846
|
+
return this.err(
|
|
847
|
+
`${dName}.${field} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(key)}`,
|
|
848
|
+
node,
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
499
853
|
if (base._excluded?.has(field))
|
|
500
854
|
return this.err(`No member '${field}' on ${typeStr(baseType)}`, node);
|
|
501
855
|
if (base._isOpen) return ANY;
|
|
@@ -543,6 +897,12 @@ export const resolveMethods = {
|
|
|
543
897
|
`cannot refer to unexported name ${base.name}.${field}`,
|
|
544
898
|
node,
|
|
545
899
|
);
|
|
900
|
+
// Go-source stdlib member: backends emit the mangled top-level name.
|
|
901
|
+
const mangled = base._mangle?.get(field);
|
|
902
|
+
if (mangled) {
|
|
903
|
+
node._stdName = mangled;
|
|
904
|
+
return base.members[field];
|
|
905
|
+
}
|
|
546
906
|
if (field in base.members) {
|
|
547
907
|
if (
|
|
548
908
|
(this.target === "wasm" || this.target === "both") &&
|
|
@@ -561,6 +921,12 @@ export const resolveMethods = {
|
|
|
561
921
|
}
|
|
562
922
|
return base.members[field];
|
|
563
923
|
}
|
|
924
|
+
const excluded = EXCLUDED_STDLIB_MEMBERS.get(`${base.name}.${field}`);
|
|
925
|
+
if (excluded)
|
|
926
|
+
return this.err(
|
|
927
|
+
`${base.name}.${field} is not available: ${excluded}`,
|
|
928
|
+
node,
|
|
929
|
+
);
|
|
564
930
|
return this.err(`No member '${field}' in namespace ${base.name}`, node);
|
|
565
931
|
},
|
|
566
932
|
|
|
@@ -575,7 +941,16 @@ function isExported(name) {
|
|
|
575
941
|
return code >= 65 && code <= 90;
|
|
576
942
|
}
|
|
577
943
|
|
|
578
|
-
function _processJsonField(
|
|
944
|
+
function _processJsonField(
|
|
945
|
+
f,
|
|
946
|
+
depth,
|
|
947
|
+
path,
|
|
948
|
+
visited,
|
|
949
|
+
queue,
|
|
950
|
+
candidates,
|
|
951
|
+
index,
|
|
952
|
+
pos,
|
|
953
|
+
) {
|
|
579
954
|
const tagInfo = parseJsonTag(f.tag);
|
|
580
955
|
if (tagInfo.ignored) return;
|
|
581
956
|
|
|
@@ -602,6 +977,7 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
|
602
977
|
depth,
|
|
603
978
|
tagged: true,
|
|
604
979
|
isEmbedded: true,
|
|
980
|
+
index: [...index, pos],
|
|
605
981
|
path: [...path, { name: embedName, isPtr, typeName: embedName }],
|
|
606
982
|
});
|
|
607
983
|
}
|
|
@@ -612,6 +988,7 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
|
612
988
|
queue.push({
|
|
613
989
|
struct: embedStruct,
|
|
614
990
|
depth: depth + 1,
|
|
991
|
+
index: [...index, pos],
|
|
615
992
|
path: [...path, { name: embedName, isPtr, typeName: embedName }],
|
|
616
993
|
visited: nextVisited,
|
|
617
994
|
});
|
|
@@ -620,9 +997,10 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
|
620
997
|
return;
|
|
621
998
|
}
|
|
622
999
|
|
|
623
|
-
for (const name of f.names) {
|
|
1000
|
+
for (const [i, name] of f.names.entries()) {
|
|
624
1001
|
if (!isExported(name)) continue;
|
|
625
1002
|
candidates.push({
|
|
1003
|
+
index: [...index, pos + i],
|
|
626
1004
|
jsonKey: tagInfo.name !== null ? tagInfo.name : name,
|
|
627
1005
|
goName: name,
|
|
628
1006
|
type: f.type,
|
|
@@ -658,10 +1036,18 @@ function _resolveCandidateCollisions(candidates) {
|
|
|
658
1036
|
}
|
|
659
1037
|
}
|
|
660
1038
|
|
|
661
|
-
|
|
1039
|
+
// Go orders the fields of a struct by their index sequence, so a promoted
|
|
1040
|
+
// field sits where its embedded struct is declared.
|
|
1041
|
+
winningFields.sort((a, b) => compareIndex(a.index, b.index));
|
|
662
1042
|
return winningFields;
|
|
663
1043
|
}
|
|
664
1044
|
|
|
1045
|
+
function compareIndex(a, b) {
|
|
1046
|
+
for (let i = 0; i < a.length && i < b.length; i++)
|
|
1047
|
+
if (a[i] !== b[i]) return a[i] - b[i];
|
|
1048
|
+
return a.length - b.length;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
665
1051
|
export function resolveJsonFields(structType) {
|
|
666
1052
|
if (!structType) return [];
|
|
667
1053
|
const underlying =
|
|
@@ -670,16 +1056,25 @@ export function resolveJsonFields(structType) {
|
|
|
670
1056
|
if (underlying._jsonFields) return underlying._jsonFields;
|
|
671
1057
|
|
|
672
1058
|
const queue = [
|
|
673
|
-
{
|
|
1059
|
+
{
|
|
1060
|
+
struct: underlying,
|
|
1061
|
+
depth: 0,
|
|
1062
|
+
path: [],
|
|
1063
|
+
index: [],
|
|
1064
|
+
visited: new Set([underlying]),
|
|
1065
|
+
},
|
|
674
1066
|
];
|
|
675
1067
|
const candidates = [];
|
|
676
1068
|
|
|
677
1069
|
while (queue.length > 0) {
|
|
678
|
-
const { struct: curr, depth, path, visited } = queue.shift();
|
|
1070
|
+
const { struct: curr, depth, path, index, visited } = queue.shift();
|
|
679
1071
|
if (curr?.kind !== "struct") continue;
|
|
680
1072
|
|
|
1073
|
+
// Each name of a `A, B int` field is its own field, as in Go.
|
|
1074
|
+
let pos = 0;
|
|
681
1075
|
for (const f of curr._structFields || []) {
|
|
682
|
-
_processJsonField(f, depth, path, visited, queue, candidates);
|
|
1076
|
+
_processJsonField(f, depth, path, visited, queue, candidates, index, pos);
|
|
1077
|
+
pos += f.embedded ? 1 : (f.names?.length ?? 1);
|
|
683
1078
|
}
|
|
684
1079
|
}
|
|
685
1080
|
|