gofront 1.6.1 → 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.
Files changed (132) hide show
  1. package/README.md +45 -49
  2. package/package.json +12 -8
  3. package/src/backend/js/expressions.js +1627 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +508 -102
  6. package/src/backend/js/runtime.js +1997 -106
  7. package/src/backend/js/statements.js +931 -87
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +7 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +19 -2
  14. package/src/backend/js/stdlib/json.js +1167 -0
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +34 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +446 -72
  29. package/src/backend/wasm/emit-builtins.js +320 -13
  30. package/src/backend/wasm/emit-exprs.js +1404 -199
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +518 -74
  34. package/src/backend/wasm/encode.js +128 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/optimize.js +4 -2
  41. package/src/backend/wasm/prescan.js +397 -0
  42. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  43. package/src/backend/wasm/stdlib/core.js +1574 -199
  44. package/src/backend/wasm/stdlib/index.js +3 -5
  45. package/src/backend/wasm/stdlib/io.js +1567 -150
  46. package/src/backend/wasm/stdlib/packages.js +2 -0
  47. package/src/backend/wasm/stdlib/simd.js +499 -0
  48. package/src/backend/wasm/stdlib/time.js +1041 -12
  49. package/src/backend/wasm/types.js +221 -15
  50. package/src/backend/wasm/variadic.js +93 -0
  51. package/src/backend/wasm/wat.js +84 -20
  52. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  53. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  54. package/src/{vendor.js → cli/vendor.js} +89 -15
  55. package/src/compiler.js +107 -269
  56. package/src/dts-parser.js +71 -14
  57. package/src/index.js +15 -169
  58. package/src/{lexer.js → lexer/lexer.js} +102 -55
  59. package/src/{tokens.js → lexer/tokens.js} +1 -0
  60. package/src/link/sourcemap.js +97 -0
  61. package/src/link/stdlib.js +515 -0
  62. package/src/link/wasm.js +294 -0
  63. package/src/lower/bounds.js +394 -0
  64. package/src/lower/boxing.js +1 -6
  65. package/src/lower/captures.js +176 -32
  66. package/src/lower/embedding.js +2 -1
  67. package/src/lower/index.js +26 -18
  68. package/src/lower/json-codecs.js +1398 -0
  69. package/src/lower/ownership.js +22 -1
  70. package/src/lower/range.js +86 -1
  71. package/src/lower/reachability.js +132 -0
  72. package/src/parser/declarations.js +58 -13
  73. package/src/parser/expressions.js +29 -7
  74. package/src/parser/index.js +2 -1
  75. package/src/parser/statements.js +37 -22
  76. package/src/parser/struct-tag.js +144 -0
  77. package/src/{templ-parser.js → parser/templ.js} +138 -15
  78. package/src/parser/types.js +130 -13
  79. package/src/resolver.js +22 -1
  80. package/src/typechecker/assignability.js +215 -11
  81. package/src/typechecker/const-fold.js +356 -0
  82. package/src/typechecker/constants.js +256 -0
  83. package/src/typechecker/expressions.js +960 -99
  84. package/src/typechecker/index.js +491 -72
  85. package/src/typechecker/init-order.js +330 -0
  86. package/src/typechecker/resolve.js +596 -52
  87. package/src/typechecker/statements.js +157 -17
  88. package/src/typechecker/stdlib/core.js +333 -55
  89. package/src/typechecker/stdlib/excluded.js +898 -0
  90. package/src/typechecker/stdlib/extended.js +359 -26
  91. package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
  92. package/src/typechecker/stdlib/simd.js +123 -0
  93. package/src/typechecker/termination.js +1 -1
  94. package/src/typechecker/types.js +165 -26
  95. package/stdlib/bytes/iter.go +192 -0
  96. package/stdlib/bytes/search.go +240 -0
  97. package/stdlib/bytes/split.go +218 -0
  98. package/stdlib/cmp/cmp.go +65 -0
  99. package/stdlib/encoding/json/errors.go +31 -0
  100. package/stdlib/encoding/json/indent.go +71 -0
  101. package/stdlib/encoding/json/number.go +12 -0
  102. package/stdlib/encoding/json/stream.go +173 -0
  103. package/stdlib/errors/overlay.go +12 -0
  104. package/stdlib/hash/hash.go +53 -0
  105. package/stdlib/internal/jsonrt/dec.go +738 -0
  106. package/stdlib/internal/jsonrt/enc.go +366 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +649 -0
  111. package/stdlib/io/io.go +346 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/iter.go +65 -0
  114. package/stdlib/math/extra.go +270 -0
  115. package/stdlib/slices/extra.go +73 -0
  116. package/stdlib/slices/iter.go +113 -0
  117. package/stdlib/sort/sort.go +561 -0
  118. package/stdlib/strconv/quote.go +341 -0
  119. package/stdlib/strings/extra.go +35 -0
  120. package/stdlib/strings/iter.go +122 -0
  121. package/stdlib/sync/extra.go +105 -0
  122. package/stdlib/unicode/unicode.go +112 -0
  123. package/stdlib/unicode/utf8/bytes.go +239 -0
  124. package/src/backend/wasm/const-fold.js +0 -205
  125. package/src/backend/wasm/emit-stdlib.js +0 -13
  126. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  127. /package/src/{colors.js → cli/colors.js} +0 -0
  128. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  129. /package/src/{minifier.js → cli/minifier.js} +0 -0
  130. /package/src/{project-config.js → cli/project-config.js} +0 -0
  131. /package/src/{pwa.js → cli/pwa.js} +0 -0
  132. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -1,11 +1,15 @@
1
1
  // Type resolution methods — resolveTypeNode, resolveType, fieldType, generics.
2
2
  // Installed as a mixin on TypeChecker.prototype.
3
3
 
4
+ import { parseJsonTag } from "../parser/struct-tag.js";
5
+ import { foldConstExpr } from "./const-fold.js";
6
+ import { EXCLUDED_STDLIB_MEMBERS } from "./stdlib/excluded.js";
4
7
  import {
5
8
  ANY,
6
9
  BASIC_TYPES,
7
10
  COMPARABLE,
8
11
  defaultType,
12
+ displayTypeName,
9
13
  isAny,
10
14
  isTypedArraySlice,
11
15
  Scope,
@@ -19,6 +23,73 @@ import {
19
23
  /** @typedef {import('./index.js').TypeChecker} TypeChecker */
20
24
 
21
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
+
22
93
  const SUBSTITUTE_DISPATCH = {
23
94
  basic: (_s, type) => type,
24
95
  untyped: (_s, type) => type,
@@ -62,6 +133,7 @@ const SUBSTITUTE_DISPATCH = {
62
133
  fields,
63
134
  methods,
64
135
  _embeds: type._embeds,
136
+ _structFields: type._structFields,
65
137
  };
66
138
  },
67
139
  interface: (s, type, map) => {
@@ -69,11 +141,24 @@ const SUBSTITUTE_DISPATCH = {
69
141
  for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
70
142
  return { kind: "interface", name: type.name, methods };
71
143
  },
72
- named: (s, type, map) => ({
73
- kind: "named",
74
- name: type.name,
75
- underlying: s.substituteType(type.underlying, map),
76
- }),
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
+ },
77
162
  };
78
163
 
79
164
  /** @type {ThisType<TypeChecker>} */
@@ -87,40 +172,27 @@ export const resolveMethods = {
87
172
  if (scope) {
88
173
  const fromScope = scope.lookup(node.name);
89
174
  if (fromScope?.kind === "typeParam") return fromScope;
175
+ if (fromScope?.kind === "aliasArg") return fromScope.type;
90
176
  }
91
177
  const named = this.types.get(node.name);
92
178
  if (named) {
93
- // `pkg.T` in a type position counts as using the import.
94
- const dot = node.name.indexOf(".");
95
- if (dot > 0) this.globals.lookup(node.name.slice(0, dot));
179
+ const wasmErr = this._checkWasmStdlibTypeMember(node);
180
+ if (wasmErr) return wasmErr;
181
+ this._useQualifiedTypeName(node, named);
96
182
  return named;
97
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
+ }
98
190
  return this.err(`Unknown type '${node.name}'`, node);
99
191
  }
100
192
  case "GenericTypeName":
101
193
  case "InstantiationExpr":
102
- case "IndexExpr": {
103
- const name =
104
- node.name ?? (node.expr?.kind === "Ident" ? node.expr.name : null);
105
- const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
106
- const base = name
107
- ? this.types.get(name)
108
- : node.expr
109
- ? this.resolveTypeNode(node.expr, scope)
110
- : null;
111
- if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
112
- if (base.kind === "named" && base._generic) {
113
- const res = this.instantiateGenericType(
114
- base._generic,
115
- typeArgs,
116
- scope,
117
- );
118
- node._typeArgs = res.typeArgs;
119
- node._genericType = base;
120
- return res;
121
- }
122
- return this.err(`Type '${name ?? "?"}' is not generic`, node);
123
- }
194
+ case "IndexExpr":
195
+ return this._resolveGenericTypeName(node, scope);
124
196
  case "TypeParam": {
125
197
  const constraint = node.constraint
126
198
  ? this.resolveTypeNode(node.constraint, scope)
@@ -156,11 +228,14 @@ export const resolveMethods = {
156
228
  // SelectorExpr; resolve it like a qualified TypeName.
157
229
  if (node.expr?.kind === "Ident" && typeof node.field === "string") {
158
230
  const qualified = `${node.expr.name}.${node.field}`;
159
- if (this.types.has(qualified))
160
- return this.resolveTypeNode(
231
+ if (this.types.has(qualified)) {
232
+ const resolved = this.resolveTypeNode(
161
233
  { kind: "TypeName", name: qualified },
162
234
  scope,
163
235
  );
236
+ this._useQualifiedTypeName(node, resolved);
237
+ return resolved;
238
+ }
164
239
  }
165
240
  return ANY;
166
241
  case "ArrayType":
@@ -177,24 +252,116 @@ export const resolveMethods = {
177
252
  }
178
253
  },
179
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
+
180
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
+ }
181
313
  return {
182
314
  kind: "array",
183
315
  size: node.inferLen
184
316
  ? null
185
317
  : node.size?.value !== undefined
186
318
  ? Number(node.size.value)
187
- : node.size,
319
+ : this._foldArrayLen(node.size, scope),
188
320
  elem: this.resolveTypeNode(node.elem, scope),
189
321
  };
190
322
  },
191
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
+
192
357
  _resolveFuncTypeNode(node, scope) {
193
358
  const params = node.params.map((p) => this.resolveTypeNode(p.type, scope));
194
359
  const returns = node.returnType
195
360
  ? [this.resolveTypeNode(node.returnType, scope)]
196
361
  : [VOID];
197
- return { kind: "func", params, returns };
362
+ const fn = { kind: "func", params, returns };
363
+ if (node.params.at(-1)?.variadic) fn.variadic = true;
364
+ return fn;
198
365
  },
199
366
 
200
367
  resolveTypeNodeName(node, scope) {
@@ -209,10 +376,29 @@ export const resolveMethods = {
209
376
  _resolveStructType(node, scope) {
210
377
  const fields = new Map();
211
378
  const embeds = [];
379
+ const structFields = [];
212
380
  for (const f of node.fields) {
213
381
  const ft = this.resolveTypeNode(f.type, scope);
382
+ structFields.push({
383
+ names: f.names,
384
+ type: ft,
385
+ tag: f.tag ?? null,
386
+ embedded: Boolean(f.embedded),
387
+ astType: f.type,
388
+ });
214
389
  if (f.embedded) {
215
- const base = ft.kind === "named" ? ft.underlying : ft;
390
+ const inner = ft.kind === "pointer" ? ft.base : ft;
391
+ const embedName = (
392
+ f.embeddedName ??
393
+ f.astType?.name ??
394
+ f.type?.name ??
395
+ f.type?.base?.name ??
396
+ (inner.kind === "named" ? inner.name : "")
397
+ )
398
+ .split(".")
399
+ .pop();
400
+ if (embedName) fields.set(embedName, ft);
401
+ const base = inner.kind === "named" ? inner.underlying : inner;
216
402
  if (base?.kind === "struct") {
217
403
  for (const [k, v] of base.fields.entries()) fields.set(k, v);
218
404
  }
@@ -221,7 +407,13 @@ export const resolveMethods = {
221
407
  for (const n of f.names) fields.set(n, ft);
222
408
  }
223
409
  }
224
- return { kind: "struct", fields, methods: new Map(), _embeds: embeds };
410
+ return {
411
+ kind: "struct",
412
+ fields,
413
+ methods: new Map(),
414
+ _embeds: embeds,
415
+ _structFields: structFields,
416
+ };
225
417
  },
226
418
 
227
419
  _resolveInterfaceType(node, scope) {
@@ -237,6 +429,9 @@ export const resolveMethods = {
237
429
  if (isVariadic) mType.variadic = true;
238
430
  methods.set(m.name, mType);
239
431
  }
432
+ // Embedded interfaces that carry a type set (`interface{ Integer }`):
433
+ // kept so type-parameter operator checks can follow them.
434
+ const typeSetEmbeds = [];
240
435
  if (node.embeds) {
241
436
  for (const embed of node.embeds) {
242
437
  const resolved = this.resolveTypeNode(embed, scope);
@@ -246,6 +441,8 @@ export const resolveMethods = {
246
441
  for (const [mName, mType] of base.methods) {
247
442
  if (!methods.has(mName)) methods.set(mName, mType);
248
443
  }
444
+ if (base.unionConstraint || base._typeSetEmbeds)
445
+ typeSetEmbeds.push(base);
249
446
  } else if (!isAny(resolved)) {
250
447
  this.err(
251
448
  `cannot embed non-interface type ${typeStr(resolved)}`,
@@ -256,6 +453,7 @@ export const resolveMethods = {
256
453
  }
257
454
  const result = { kind: "interface", methods };
258
455
  if (node.unionConstraint) result.unionConstraint = node.unionConstraint;
456
+ if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
259
457
  return result;
260
458
  },
261
459
 
@@ -276,36 +474,127 @@ export const resolveMethods = {
276
474
  },
277
475
 
278
476
  instantiateGenericType(generic, typeArgNodes, scope) {
279
- const map = new Map();
280
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();
281
518
  for (let i = 0; i < generic.typeParams.length; i++)
282
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);
283
528
  // Resolve the declaration with its type params in scope (as typeParams,
284
529
  // never as value symbols), then substitute the concrete arguments.
285
530
  const typeScope = new Scope(this.globals);
286
531
  for (const tp of generic.typeParams) typeScope.define(tp.name, tp);
287
- const underlying = this.resolveTypeNode(generic.declNode.type, typeScope);
288
- const instantiated = this.substituteType(underlying, map);
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
+ }
289
542
  if (instantiated.kind === "struct") {
290
543
  instantiated.name = generic.declNode.name;
291
544
  instantiated.typeArgs = typeArgs;
292
545
  if (!instantiated.methods) instantiated.methods = new Map();
293
- if (generic.methods) {
294
- for (const [mName, mType] of generic.methods)
295
- instantiated.methods.set(mName, this.substituteType(mType, map));
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);
296
586
  }
587
+ aliasScope.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
297
588
  }
298
- return {
299
- kind: "named",
300
- name: generic.declNode.name,
301
- typeArgs,
302
- underlying: instantiated,
303
- methods: instantiated.methods,
304
- };
589
+ return this.resolveTypeNode(declNode.type, aliasScope);
305
590
  },
306
591
 
307
- inferTypeArgs(genericType, argTypes) {
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 = []) {
308
595
  const map = new Map();
596
+ for (let i = 0; i < explicit.length; i++)
597
+ map.set(genericType.typeParams[i].name, explicit[i]);
309
598
  const params = genericType.underlying.params;
310
599
  for (let i = 0; i < params.length && i < argTypes.length; i++)
311
600
  this._inferFromPair(params[i], argTypes[i], map);
@@ -339,14 +628,36 @@ export const resolveMethods = {
339
628
  }
340
629
  if (paramType.kind === "func" && argType.kind === "func")
341
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
+ }
342
639
  },
343
640
 
344
641
  _inferFromPair(paramType, argType, map) {
345
642
  if (!paramType || !argType) return;
346
643
  if (paramType.kind === "typeParam") {
347
- if (!map.has(paramType.name)) map.set(paramType.name, argType);
644
+ const prev = map.get(paramType.name);
645
+ if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
348
646
  return;
349
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;
350
661
  this._inferFromStructuredPair(paramType, argType, map);
351
662
  },
352
663
 
@@ -397,11 +708,37 @@ export const resolveMethods = {
397
708
  (!t.underlying || !t.methods)
398
709
  ) {
399
710
  const found = this.types.get(t.name);
400
- if (found) return found;
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;
401
716
  }
717
+ if (t.kind === "named" && t.typeArgs?.length)
718
+ return this._withGenericMethods(t);
402
719
  return t;
403
720
  },
404
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
+
405
742
  // ── Field type lookup ─────────────────────────────────────────────
406
743
 
407
744
  fieldType(baseType, field, node) {
@@ -434,6 +771,7 @@ export const resolveMethods = {
434
771
  case "namespace":
435
772
  return this._fieldTypeNamespace(base, field, node);
436
773
  case "shared":
774
+ case "simd":
437
775
  if (base.methods?.has(field)) return base.methods.get(field);
438
776
  return this.err(`No method '${field}' on ${typeStr(baseType)}`, node);
439
777
  case "slice":
@@ -464,12 +802,54 @@ export const resolveMethods = {
464
802
  return base.fields.get(field);
465
803
  }
466
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
+ }
467
826
  return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
468
827
  },
469
828
 
470
829
  _fieldTypeInterface(base, baseType, field, node) {
471
830
  if (base.methods?.has(field)) return base.methods.get(field);
472
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
+ }
473
853
  if (base._excluded?.has(field))
474
854
  return this.err(`No member '${field}' on ${typeStr(baseType)}`, node);
475
855
  if (base._isOpen) return ANY;
@@ -517,6 +897,12 @@ export const resolveMethods = {
517
897
  `cannot refer to unexported name ${base.name}.${field}`,
518
898
  node,
519
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
+ }
520
906
  if (field in base.members) {
521
907
  if (
522
908
  (this.target === "wasm" || this.target === "both") &&
@@ -535,6 +921,164 @@ export const resolveMethods = {
535
921
  }
536
922
  return base.members[field];
537
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
+ );
538
930
  return this.err(`No member '${field}' in namespace ${base.name}`, node);
539
931
  },
932
+
933
+ resolveJsonFields(structType) {
934
+ return resolveJsonFields(structType);
935
+ },
540
936
  };
937
+
938
+ function isExported(name) {
939
+ if (!name || typeof name !== "string") return false;
940
+ const code = name.charCodeAt(0);
941
+ return code >= 65 && code <= 90;
942
+ }
943
+
944
+ function _processJsonField(
945
+ f,
946
+ depth,
947
+ path,
948
+ visited,
949
+ queue,
950
+ candidates,
951
+ index,
952
+ pos,
953
+ ) {
954
+ const tagInfo = parseJsonTag(f.tag);
955
+ if (tagInfo.ignored) return;
956
+
957
+ if (f.embedded) {
958
+ const isPtr = f.type.kind === "pointer" || f.type.kind === "PointerType";
959
+ const inner = isPtr ? f.type.base : f.type;
960
+ const embedStruct = inner.kind === "named" ? inner.underlying : inner;
961
+ const embedName = (
962
+ f.astType?.name ??
963
+ f.astType?.base?.name ??
964
+ inner.name ??
965
+ ""
966
+ )
967
+ .split(".")
968
+ .pop();
969
+
970
+ if (tagInfo.name !== null) {
971
+ if (isExported(embedName)) {
972
+ candidates.push({
973
+ jsonKey: tagInfo.name,
974
+ goName: embedName,
975
+ type: f.type,
976
+ omitempty: tagInfo.omitempty,
977
+ depth,
978
+ tagged: true,
979
+ isEmbedded: true,
980
+ index: [...index, pos],
981
+ path: [...path, { name: embedName, isPtr, typeName: embedName }],
982
+ });
983
+ }
984
+ } else if (embedStruct?.kind === "struct") {
985
+ if (!visited.has(embedStruct)) {
986
+ const nextVisited = new Set(visited);
987
+ nextVisited.add(embedStruct);
988
+ queue.push({
989
+ struct: embedStruct,
990
+ depth: depth + 1,
991
+ index: [...index, pos],
992
+ path: [...path, { name: embedName, isPtr, typeName: embedName }],
993
+ visited: nextVisited,
994
+ });
995
+ }
996
+ }
997
+ return;
998
+ }
999
+
1000
+ for (const [i, name] of f.names.entries()) {
1001
+ if (!isExported(name)) continue;
1002
+ candidates.push({
1003
+ index: [...index, pos + i],
1004
+ jsonKey: tagInfo.name !== null ? tagInfo.name : name,
1005
+ goName: name,
1006
+ type: f.type,
1007
+ omitempty: tagInfo.omitempty,
1008
+ quoted: tagInfo.options.includes("string"),
1009
+ depth,
1010
+ tagged: tagInfo.name !== null,
1011
+ path: [...path, { name, isPtr: false }],
1012
+ });
1013
+ }
1014
+ }
1015
+
1016
+ function _resolveCandidateCollisions(candidates) {
1017
+ const byKey = new Map();
1018
+ for (const cand of candidates) {
1019
+ if (!byKey.has(cand.jsonKey)) byKey.set(cand.jsonKey, []);
1020
+ byKey.get(cand.jsonKey).push(cand);
1021
+ }
1022
+
1023
+ const winningFields = [];
1024
+ for (const [, group] of byKey.entries()) {
1025
+ let minDepth = Infinity;
1026
+ for (const c of group) {
1027
+ if (c.depth < minDepth) minDepth = c.depth;
1028
+ }
1029
+
1030
+ const atMin = group.filter((c) => c.depth === minDepth);
1031
+ if (atMin.length === 1) {
1032
+ winningFields.push(atMin[0]);
1033
+ } else {
1034
+ const tagged = atMin.filter((c) => c.tagged);
1035
+ if (tagged.length === 1) winningFields.push(tagged[0]);
1036
+ }
1037
+ }
1038
+
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));
1042
+ return winningFields;
1043
+ }
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
+
1051
+ export function resolveJsonFields(structType) {
1052
+ if (!structType) return [];
1053
+ const underlying =
1054
+ structType.kind === "named" ? structType.underlying : structType;
1055
+ if (underlying?.kind !== "struct") return [];
1056
+ if (underlying._jsonFields) return underlying._jsonFields;
1057
+
1058
+ const queue = [
1059
+ {
1060
+ struct: underlying,
1061
+ depth: 0,
1062
+ path: [],
1063
+ index: [],
1064
+ visited: new Set([underlying]),
1065
+ },
1066
+ ];
1067
+ const candidates = [];
1068
+
1069
+ while (queue.length > 0) {
1070
+ const { struct: curr, depth, path, index, visited } = queue.shift();
1071
+ if (curr?.kind !== "struct") continue;
1072
+
1073
+ // Each name of a `A, B int` field is its own field, as in Go.
1074
+ let pos = 0;
1075
+ for (const f of curr._structFields || []) {
1076
+ _processJsonField(f, depth, path, visited, queue, candidates, index, pos);
1077
+ pos += f.embedded ? 1 : (f.names?.length ?? 1);
1078
+ }
1079
+ }
1080
+
1081
+ const winning = _resolveCandidateCollisions(candidates);
1082
+ underlying._jsonFields = winning;
1083
+ return winning;
1084
+ }