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.
Files changed (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  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 +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  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 +10 -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 +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  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 +512 -77
  34. package/src/backend/wasm/encode.js +43 -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/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /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
- kind: "named",
75
- name: type.name,
76
- underlying: s.substituteType(type.underlying, map),
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
- // `pkg.T` in a type position counts as using the import.
95
- const dot = node.name.indexOf(".");
96
- 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);
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
- const name =
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
- return this.resolveTypeNode(
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
- 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;
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
- const underlying = this.resolveTypeNode(generic.declNode.type, typeScope);
313
- 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
+ }
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
- if (generic.methods) {
319
- for (const [mName, mType] of generic.methods)
320
- 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);
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
- 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 = []) {
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
- 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);
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
- 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;
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(f, depth, path, visited, queue, candidates) {
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
- winningFields.sort((a, b) => candidates.indexOf(a) - candidates.indexOf(b));
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
- { struct: underlying, depth: 0, path: [], visited: new Set([underlying]) },
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