gofront 1.8.0 → 1.9.1

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 (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -3,14 +3,19 @@
3
3
 
4
4
  import { parseJsonTag } from "../parser/struct-tag.js";
5
5
  import { foldConstExpr } from "./const-fold.js";
6
+ import { exprText, operandKind } from "./operand-text.js";
6
7
  import { EXCLUDED_STDLIB_MEMBERS } from "./stdlib/excluded.js";
7
8
  import {
8
9
  ANY,
9
10
  BASIC_TYPES,
10
11
  COMPARABLE,
12
+ DECLARED_ANY,
11
13
  defaultType,
12
14
  displayTypeName,
15
+ isAmbiguousFieldSelector,
16
+ isAmbiguousPromotion,
13
17
  isAny,
18
+ isMethodPromotedOverField,
14
19
  isTypedArraySlice,
15
20
  Scope,
16
21
  TAINTED_ANY,
@@ -27,6 +32,11 @@ import {
27
32
  // instantiation is reported as a cycle.
28
33
  const MAX_INSTANTIATION_DEPTH = 32;
29
34
 
35
+ // Type nodes in a constraint position, where a constraint interface may
36
+ // appear. A property of the AST node, so it is shared by every checker: an
37
+ // importing package resolves the constraints of the imported generics.
38
+ const CONSTRAINT_POS = new WeakSet();
39
+
30
40
  // Two imported packages may both declare `Pt`, so the cache key of an
31
41
  // instance tells declarations apart by identity instead of by display name.
32
42
  const declIds = new WeakMap();
@@ -71,6 +81,30 @@ function typeSize(t) {
71
81
  }
72
82
  }
73
83
 
84
+ // Whether `t` mentions one of the type parameters `params` below its root.
85
+ function nestsTypeParam(t, params) {
86
+ const mentions = (u) => {
87
+ switch (u?.kind) {
88
+ case "typeParam":
89
+ return params.includes(u);
90
+ case "pointer":
91
+ return mentions(u.base);
92
+ case "slice":
93
+ case "array":
94
+ return mentions(u.elem);
95
+ case "map":
96
+ return mentions(u.key) || mentions(u.value);
97
+ case "func":
98
+ return [...(u.params ?? []), ...(u.returns ?? [])].some(mentions);
99
+ case "named":
100
+ return (u.typeArgs ?? []).some(mentions);
101
+ default:
102
+ return false;
103
+ }
104
+ };
105
+ return t?.kind !== "typeParam" && mentions(t);
106
+ }
107
+
74
108
  function instanceKey(t) {
75
109
  switch (t?.kind) {
76
110
  case "named":
@@ -133,13 +167,34 @@ const SUBSTITUTE_DISPATCH = {
133
167
  fields,
134
168
  methods,
135
169
  _embeds: type._embeds,
136
- _structFields: type._structFields,
170
+ // The JSON codecs read the field types from here.
171
+ _structFields: type._structFields?.map((f) => ({
172
+ ...f,
173
+ type: s.substituteType(f.type, map),
174
+ })),
137
175
  };
138
176
  },
139
177
  interface: (s, type, map) => {
140
178
  const methods = new Map();
141
179
  for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
142
- return { kind: "interface", name: type.name, methods };
180
+ const out = { kind: "interface", name: type.name, methods };
181
+ // The type set follows the substitution (`interface{ ~[]E }` with E=int).
182
+ if (type.unionConstraint)
183
+ out.unionConstraint = {
184
+ ...type.unionConstraint,
185
+ terms: type.unionConstraint.terms.map((term) => ({
186
+ ...term,
187
+ resolved: s.substituteType(
188
+ term.resolved ?? s.resolveTypeNode(term.type, type._unionScope),
189
+ map,
190
+ ),
191
+ })),
192
+ };
193
+ if (type._typeSetEmbeds)
194
+ out._typeSetEmbeds = type._typeSetEmbeds.map((e) =>
195
+ s.substituteType(e, map),
196
+ );
197
+ return out;
143
198
  },
144
199
  named: (s, type, map) => {
145
200
  // An instance of a generic type: re-instantiate with the substituted
@@ -167,8 +222,10 @@ export const resolveMethods = {
167
222
  if (!node) return ANY;
168
223
  switch (node.kind) {
169
224
  case "TypeName": {
170
- if (BASIC_TYPES[node.name]) return BASIC_TYPES[node.name];
171
- if (node.name === "comparable") return COMPARABLE;
225
+ if (BASIC_TYPES[node.name])
226
+ return node.name === "any" ? DECLARED_ANY : BASIC_TYPES[node.name];
227
+ if (node.name === "comparable")
228
+ return this._checkConstraintUse(COMPARABLE, node);
172
229
  if (scope) {
173
230
  const fromScope = scope.lookup(node.name);
174
231
  if (fromScope?.kind === "typeParam") return fromScope;
@@ -176,11 +233,11 @@ export const resolveMethods = {
176
233
  }
177
234
  const named = this.types.get(node.name);
178
235
  if (named) {
179
- const wasmErr = this._checkWasmStdlibTypeMember(node);
180
- if (wasmErr) return wasmErr;
181
236
  this._useQualifiedTypeName(node, named);
182
- return named;
237
+ return this._checkConstraintUse(named, node);
183
238
  }
239
+ const jsOnly = this._jsOnlyMember(node.name);
240
+ if (jsOnly) return this._errJsOnly(jsOnly, node);
184
241
  if (EXCLUDED_STDLIB_MEMBERS.has(node.name)) {
185
242
  return this.err(
186
243
  `${node.name} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(node.name)}`,
@@ -194,6 +251,7 @@ export const resolveMethods = {
194
251
  case "IndexExpr":
195
252
  return this._resolveGenericTypeName(node, scope);
196
253
  case "TypeParam": {
254
+ this._markConstraintPos(node.constraint);
197
255
  const constraint = node.constraint
198
256
  ? this.resolveTypeNode(node.constraint, scope)
199
257
  : ANY;
@@ -217,7 +275,17 @@ export const resolveMethods = {
217
275
  case "StructType":
218
276
  return this._resolveStructType(node, scope);
219
277
  case "InterfaceType":
220
- return this._resolveInterfaceType(node, scope);
278
+ return this._checkConstraintUse(
279
+ this._resolveInterfaceType(node, scope),
280
+ node,
281
+ );
282
+ case "UnaryExpr":
283
+ // `new(*T)`: the parser reads the pointer type as a dereference.
284
+ if (node.op !== "*") return ANY;
285
+ return {
286
+ kind: "pointer",
287
+ base: this.resolveTypeNode(node.operand, scope),
288
+ };
221
289
  case "Ident":
222
290
  return this.resolveTypeNode(
223
291
  { kind: "TypeName", name: node.name },
@@ -229,10 +297,9 @@ export const resolveMethods = {
229
297
  if (node.expr?.kind === "Ident" && typeof node.field === "string") {
230
298
  const qualified = `${node.expr.name}.${node.field}`;
231
299
  if (this.types.has(qualified)) {
232
- const resolved = this.resolveTypeNode(
233
- { kind: "TypeName", name: qualified },
234
- scope,
235
- );
300
+ const inner = { kind: "TypeName", name: qualified };
301
+ this._inheritConstraintPos(node, inner);
302
+ const resolved = this.resolveTypeNode(inner, scope);
236
303
  this._useQualifiedTypeName(node, resolved);
237
304
  return resolved;
238
305
  }
@@ -252,23 +319,112 @@ export const resolveMethods = {
252
319
  }
253
320
  },
254
321
 
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`,
322
+ // ── Constraint interfaces outside constraints ───────────────────
323
+
324
+ // Records a type node that sits in a constraint position (a type
325
+ // parameter's constraint, an interface's embedded element or union term,
326
+ // the right-hand side of a type declaration), where a constraint
327
+ // interface is allowed.
328
+ _markConstraintPos(node) {
329
+ if (!node || typeof node !== "object") return;
330
+ if (node.kind === "UnionConstraint") {
331
+ for (const term of node.terms) this._markConstraintPos(term.type);
332
+ return;
333
+ }
334
+ CONSTRAINT_POS.add(node);
335
+ },
336
+
337
+ // `inner` stands for `node` (a rewritten qualified name).
338
+ _inheritConstraintPos(node, inner) {
339
+ if (CONSTRAINT_POS.has(node)) CONSTRAINT_POS.add(inner);
340
+ },
341
+
342
+ // An interface's embedded elements and union terms are constraint positions.
343
+ _markInterfaceElements(node) {
344
+ for (const embed of node.embeds ?? []) this._markConstraintPos(embed);
345
+ this._markConstraintPos(node.unionConstraint);
346
+ },
347
+
348
+ // Marks the constraint positions of top-level declarations before any
349
+ // type resolves (constraints resolve lazily from several places).
350
+ _markConstraintPositions(programs) {
351
+ for (const p of programs) {
352
+ for (const d of p.decls) {
353
+ if (d.kind === "TypeDecl") this._markConstraintPos(d.type);
354
+ for (const tp of d.typeParams ?? [])
355
+ this._markConstraintPos(tp.constraint);
356
+ }
357
+ }
358
+ },
359
+
360
+ // A constraint interface (one with a type set, or comparable) resolved
361
+ // outside a constraint position is an error. A type whose declaration has
362
+ // not resolved yet is judged once every package-level type has.
363
+ _checkConstraintUse(type, node) {
364
+ if (CONSTRAINT_POS.has(node)) return type;
365
+ if (type?.kind === "named" && type.underlying == null) {
366
+ this._pendingConstraintUses.push({
367
+ type,
368
+ node,
369
+ file: this._currentFile,
370
+ source: this._currentSource,
371
+ });
372
+ return type;
373
+ }
374
+ this._reportConstraintUse(type, node);
375
+ return type;
376
+ },
377
+
378
+ _reportConstraintUse(type, node) {
379
+ const u = this._deepUnderlying(type);
380
+ if (u === COMPARABLE || (u?.kind === "interface" && u._comparable)) {
381
+ this.err(
382
+ `cannot use type ${typeStr(type)} outside a type constraint: interface is (or embeds) comparable`,
383
+ node,
384
+ );
385
+ } else if (
386
+ u?.kind === "interface" &&
387
+ (u.unionConstraint || u._typeSetEmbeds)
388
+ ) {
389
+ this.err(
390
+ `cannot use type ${typeStr(type)} outside a type constraint: interface contains type constraints`,
267
391
  node,
268
- "move this call to a JS package, or use a supported alternative",
269
392
  );
270
393
  }
271
- return null;
394
+ },
395
+
396
+ _flushConstraintUses() {
397
+ const pending = this._pendingConstraintUses;
398
+ if (pending.length === 0) return;
399
+ this._pendingConstraintUses = [];
400
+ const file = this._currentFile;
401
+ const source = this._currentSource;
402
+ for (const u of pending) {
403
+ this._currentFile = u.file;
404
+ this._currentSource = u.source;
405
+ this._reportConstraintUse(u.type, u.node);
406
+ }
407
+ this._currentFile = file;
408
+ this._currentSource = source;
409
+ },
410
+
411
+ // `pkg.Name` when `pkg` is a Go-source package whose `js` files declare
412
+ // Name and this package is wasm or both (those files are not loaded).
413
+ _jsOnlyMember(qualified) {
414
+ if (this.target !== "wasm" && this.target !== "both") return null;
415
+ const dot = qualified.indexOf(".");
416
+ if (dot < 0) return null;
417
+ const ns = this.globals.lookup(qualified.slice(0, dot));
418
+ return ns?._jsOnly?.has(qualified.slice(dot + 1)) ? qualified : null;
419
+ },
420
+
421
+ _errJsOnly(qualified, node) {
422
+ this.recordBlocker("unsupported stdlib member", qualified);
423
+ return this.err(
424
+ `'${qualified}' is only available in js packages`,
425
+ node,
426
+ "move this call to a JS package",
427
+ );
272
428
  },
273
429
 
274
430
  // `pkg.T` in a type position counts as using the import; a Go-source
@@ -288,20 +444,81 @@ export const resolveMethods = {
288
444
  const e = depth < 16 ? scope.lookupConstExpr(name) : null;
289
445
  return e ? foldConstExpr(e, (n) => resolve(n, depth + 1)) : undefined;
290
446
  };
447
+ let n = Number.NaN;
291
448
  try {
292
449
  const lit = foldConstExpr(size, resolve);
293
- const n = lit ? Number(lit.value) : Number.NaN;
294
- return Number.isInteger(n) && n >= 0 ? n : size;
450
+ n = lit ? Number(lit.value) : Number.NaN;
295
451
  } catch {
452
+ // fall through to the checker's evaluator
453
+ }
454
+ // `[math.MaxInt8]T`: a package constant the AST folder cannot see.
455
+ if (!Number.isInteger(n))
456
+ n = this._constIntValue(size, scope) ?? Number.NaN;
457
+ if (!Number.isInteger(n)) {
458
+ this._reportNonConstantArrayLen(size, scope);
296
459
  return size;
297
460
  }
461
+ this._useQualifiedConsts(size);
462
+ if (n >= 0) return n;
463
+ this.err(
464
+ `invalid array length ${exprText(size)} (untyped int constant ${n})`,
465
+ size,
466
+ );
467
+ return 0;
468
+ },
469
+
470
+ // A length that is not a constant at all (`[len(s)]T` with a slice, `[n]T`
471
+ // with a variable) cannot size an array. The operand's type is checked
472
+ // first, so it counts as used.
473
+ _reportNonConstantArrayLen(size, scope) {
474
+ if (size._type === undefined) this.checkExpr(size, scope);
475
+ this.err(
476
+ `invalid array length ${exprText(size)} (${operandKind(size, typeStr(size._type), false)}): must be constant`,
477
+ size,
478
+ );
479
+ },
480
+
481
+ // An array length must be an integer constant: `[1 == 1]T`, `["a"]T`,
482
+ // `[nil]T`, `[2.5]T`, `[2i]T` and a typed float constant are rejected.
483
+ _reportNonIntegerArrayLen(size, scope) {
484
+ const what = this._nonIntegerConst(size, scope);
485
+ if (!what) return false;
486
+ this.err(
487
+ `invalid array length ${exprText(size)} (${what}): must be integer`,
488
+ size,
489
+ );
490
+ return true;
491
+ },
492
+
493
+ // `pkg.K` inside a folded array length counts as using the import.
494
+ _useQualifiedConsts(node) {
495
+ if (!node || typeof node !== "object") return;
496
+ if (node.kind === "SelectorExpr" && node.expr?.kind === "Ident")
497
+ this.globals.lookup(node.expr.name);
498
+ for (const key of ["expr", "left", "right", "operand"])
499
+ this._useQualifiedConsts(node[key]);
500
+ for (const arg of node.args ?? []) this._useQualifiedConsts(arg);
298
501
  },
299
502
 
300
503
  _resolveArrayTypeNode(node, scope) {
504
+ if (
505
+ !node.inferLen &&
506
+ this._reportNonIntegerArrayLen(node._sizeExpr ?? node.size, scope)
507
+ )
508
+ return {
509
+ kind: "array",
510
+ size: 0,
511
+ elem: this.resolveTypeNode(node.elem, scope),
512
+ };
301
513
  // Fold in place so backends that read the type node see a literal length.
302
- if (!node.inferLen && node.size?.value === undefined) {
514
+ // The node may come from the parse cache: keep the original expression so
515
+ // a later compile of the same AST still counts `pkg.K` as using `pkg`
516
+ // and still reports an invalid length.
517
+ if (node._sizeExpr) this._foldArrayLen(node._sizeExpr, scope);
518
+ else if (!node.inferLen && node.size?.value === undefined) {
303
519
  const n = this._foldArrayLen(node.size, scope);
304
- if (typeof n === "number")
520
+ if (typeof n === "number") {
521
+ node._sizeExpr = node.size;
305
522
  node.size = {
306
523
  kind: "BasicLit",
307
524
  litKind: "INT",
@@ -309,6 +526,7 @@ export const resolveMethods = {
309
526
  _line: node.size._line,
310
527
  _col: node.size._col,
311
528
  };
529
+ }
312
530
  }
313
531
  return {
314
532
  kind: "array",
@@ -337,8 +555,11 @@ export const resolveMethods = {
337
555
  ? this.resolveTypeNode(node.expr, scope)
338
556
  : null;
339
557
  if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
340
- if (qualified) this.globals.lookup(sel.expr.name);
341
- else if (name)
558
+ if (qualified) {
559
+ this.globals.lookup(sel.expr.name);
560
+ // `iter.Seq[int](f)`: the selector names the mangled stdlib type.
561
+ this._useQualifiedTypeName(sel, base);
562
+ } else if (name)
342
563
  this._useQualifiedTypeName(node.name ? node : node.expr, base);
343
564
  if (base.kind === "named" && base._generic?.isAlias) {
344
565
  const aliasArgs = typeArgs.map((n) => this.resolveTypeNode(n, scope));
@@ -417,6 +638,7 @@ export const resolveMethods = {
417
638
  },
418
639
 
419
640
  _resolveInterfaceType(node, scope) {
641
+ this._markInterfaceElements(node);
420
642
  const methods = new Map();
421
643
  for (const m of node.methods) {
422
644
  const params = m.params.map((p) => this.resolveTypeNode(p.type, scope));
@@ -432,31 +654,41 @@ export const resolveMethods = {
432
654
  // Embedded interfaces that carry a type set (`interface{ Integer }`):
433
655
  // kept so type-parameter operator checks can follow them.
434
656
  const typeSetEmbeds = [];
435
- if (node.embeds) {
436
- for (const embed of node.embeds) {
437
- const resolved = this.resolveTypeNode(embed, scope);
438
- const base =
439
- resolved?.kind === "named" ? resolved.underlying : resolved;
440
- if (base?.kind === "interface") {
441
- for (const [mName, mType] of base.methods) {
442
- if (!methods.has(mName)) methods.set(mName, mType);
443
- }
444
- if (base.unionConstraint || base._typeSetEmbeds)
445
- typeSetEmbeds.push(base);
446
- } else if (!isAny(resolved)) {
447
- this.err(
448
- `cannot embed non-interface type ${typeStr(resolved)}`,
449
- embed,
450
- );
451
- }
452
- }
657
+ // `interface{ comparable; ... }`: the type set is restricted to
658
+ // comparable types (directly or through an embedded constraint).
659
+ let comparable = false;
660
+ for (const embed of node.embeds ?? []) {
661
+ if (this._mergeInterfaceEmbed(embed, scope, methods, typeSetEmbeds))
662
+ comparable = true;
453
663
  }
454
664
  const result = { kind: "interface", methods };
455
- if (node.unionConstraint) result.unionConstraint = node.unionConstraint;
665
+ if (node.unionConstraint) {
666
+ result.unionConstraint = node.unionConstraint;
667
+ result._unionScope = scope;
668
+ }
456
669
  if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
670
+ if (comparable) result._comparable = true;
457
671
  return result;
458
672
  },
459
673
 
674
+ // Folds one embedded element of an interface into `methods` and
675
+ // `typeSetEmbeds`; returns true when it makes the interface comparable-only.
676
+ _mergeInterfaceEmbed(embed, scope, methods, typeSetEmbeds) {
677
+ const resolved = this.resolveTypeNode(embed, scope);
678
+ const base = resolved?.kind === "named" ? resolved.underlying : resolved;
679
+ if (base === COMPARABLE) return true;
680
+ if (base?.kind !== "interface") {
681
+ if (!isAny(resolved))
682
+ this.err(`cannot embed non-interface type ${typeStr(resolved)}`, embed);
683
+ return false;
684
+ }
685
+ for (const [mName, mType] of base.methods) {
686
+ if (!methods.has(mName)) methods.set(mName, mType);
687
+ }
688
+ if (base.unionConstraint || base._typeSetEmbeds) typeSetEmbeds.push(base);
689
+ return Boolean(base._comparable);
690
+ },
691
+
460
692
  // ── Generics — substitution, instantiation, inference ────────────
461
693
 
462
694
  substituteType(type, map) {
@@ -501,8 +733,15 @@ export const resolveMethods = {
501
733
  // settles (`T[X]` containing `T[[]X]`), and doubling forms such as
502
734
  // `T[Pair[X, X]]` blow up long before a depth limit is reached.
503
735
  const top = generic._buildSizes[generic._buildSizes.length - 1];
736
+ // Inside a method of `G[T]`, `G[[]T]` makes every instance ask for a
737
+ // larger one, even if the method is never called.
738
+ const growsReceiver =
739
+ this._recvGeneric === generic &&
740
+ typeArgs.some((a) => nestsTypeParam(a, generic.typeParams));
504
741
  if (
742
+ growsReceiver ||
505
743
  building.length >= MAX_INSTANTIATION_DEPTH ||
744
+ (generic._syncDepth ?? 0) >= MAX_INSTANTIATION_DEPTH ||
506
745
  (top !== undefined && size > top)
507
746
  ) {
508
747
  if (!generic._cycleReported) {
@@ -557,13 +796,20 @@ export const resolveMethods = {
557
796
  const methods = named.methods;
558
797
  if (!methods || !named._instMap || named._syncing) return;
559
798
  // A method signature that names the instance reaches this again.
799
+ // `func (s Stack[T]) Wrap() Stack[[]T]` makes every instance's methods
800
+ // ask for a larger instance: count the nesting to diagnose it.
560
801
  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;
802
+ generic._syncDepth = (generic._syncDepth ?? 0) + 1;
803
+ try {
804
+ for (const [mName, mType] of generic.methods ?? [])
805
+ if (!methods.has(mName))
806
+ methods.set(mName, this.substituteType(mType, named._instMap));
807
+ // Methods promoted from embedded structs (`Outer[T]` embedding `Base`).
808
+ this._promoteEmbeddedInto(named.underlying);
809
+ } finally {
810
+ generic._syncDepth--;
811
+ named._syncing = false;
812
+ }
567
813
  },
568
814
 
569
815
  // A[X] for `type A[P any] = T`: resolve T with each P bound to the
@@ -591,19 +837,76 @@ export const resolveMethods = {
591
837
 
592
838
  // `explicit` seeds the leading type arguments of a partial instantiation
593
839
  // (`F[int](x)`); the rest are inferred from the call arguments.
594
- inferTypeArgs(genericType, argTypes, explicit = []) {
840
+ // `scope` resolves the type parameters of the calling function, so an
841
+ // argument of type `S ~[]E` unifies through its core type.
842
+ inferTypeArgs(genericType, argTypes, explicit = [], scope = null) {
595
843
  const map = new Map();
844
+ const saved = this._inferScope;
845
+ this._inferScope = scope;
846
+ try {
847
+ return this._inferTypeArgs(genericType, argTypes, explicit, map);
848
+ } finally {
849
+ this._inferScope = saved;
850
+ }
851
+ },
852
+
853
+ _inferTypeArgs(genericType, argTypes, explicit, map) {
596
854
  for (let i = 0; i < explicit.length; i++)
597
855
  map.set(genericType.typeParams[i].name, explicit[i]);
598
856
  const params = genericType.underlying.params;
599
857
  for (let i = 0; i < params.length && i < argTypes.length; i++)
600
858
  this._inferFromPair(params[i], argTypes[i], map);
859
+ this._inferFromCoreTypes(genericType.typeParams, map);
601
860
  for (const tp of genericType.typeParams) {
602
861
  if (!map.has(tp.name)) return null;
603
862
  }
604
863
  return genericType.typeParams.map((tp) => defaultType(map.get(tp.name)));
605
864
  },
606
865
 
866
+ // Constraint type inference: a type parameter `S` constrained by `~[]E`
867
+ // whose argument is known unifies `E` with that argument's element type.
868
+ _inferFromCoreTypes(typeParams, map) {
869
+ const scope = new Scope(this.globals);
870
+ for (const tp of typeParams) scope.define(tp.name, tp);
871
+ for (let round = 0; round < typeParams.length; round++) {
872
+ const before = map.size;
873
+ for (const tp of typeParams) {
874
+ const arg = map.get(tp.name);
875
+ if (!tp.constraint) continue;
876
+ if (!arg) {
877
+ this._inferFromPointerCore(tp, typeParams, map, scope);
878
+ continue;
879
+ }
880
+ const terms = this._constraintTerms(tp.constraint, new Set(), scope);
881
+ if (terms?.length !== 1) continue;
882
+ const argUnder =
883
+ this._coreOfTypeParam(arg) ??
884
+ this._deepUnderlying(this.resolveType(arg));
885
+ if (argUnder) this._inferFromPair(terms[0], argUnder, map);
886
+ }
887
+ if (map.size === before) break;
888
+ }
889
+ },
890
+
891
+ // An unbound type parameter whose constraint has the single exact term
892
+ // `*T` (`P interface{ *T; M() }`) is the pointer type, once `T` is known.
893
+ _inferFromPointerCore(tp, typeParams, map, scope) {
894
+ const terms = this._constraintTerms(tp.constraint, new Set(), scope);
895
+ if (terms?.length !== 1 || terms[0]?.kind !== "pointer") return;
896
+ const free = new Set(
897
+ typeParams.filter((p) => !map.has(p.name)).map((p) => p.name),
898
+ );
899
+ const inferred = this.substituteType(terms[0], map);
900
+ const mentionsFree = (t) =>
901
+ t?.kind === "typeParam"
902
+ ? free.has(t.name)
903
+ : t?.kind === "pointer"
904
+ ? mentionsFree(t.base)
905
+ : false;
906
+ if (!mentionsFree(inferred) && inferred.kind === "pointer")
907
+ map.set(tp.name, inferred);
908
+ },
909
+
607
910
  _inferFromFuncPair(paramType, argType, map) {
608
911
  for (
609
912
  let i = 0;
@@ -622,6 +925,10 @@ export const resolveMethods = {
622
925
  _inferFromStructuredPair(paramType, argType, map) {
623
926
  if (paramType.kind === "slice" && argType.kind === "slice")
624
927
  this._inferFromPair(paramType.elem, argType.elem, map);
928
+ if (paramType.kind === "array" && argType.kind === "array")
929
+ this._inferFromPair(paramType.elem, argType.elem, map);
930
+ if (paramType.kind === "pointer" && argType.kind === "pointer")
931
+ this._inferFromPair(paramType.base, argType.base, map);
625
932
  if (paramType.kind === "map" && argType.kind === "map") {
626
933
  this._inferFromPair(paramType.key, argType.key, map);
627
934
  this._inferFromPair(paramType.value, argType.value, map);
@@ -638,8 +945,19 @@ export const resolveMethods = {
638
945
  }
639
946
  },
640
947
 
948
+ // An argument whose type is a type parameter of the calling function
949
+ // (`S ~[]E`) unifies through its core type: that type, or null.
950
+ // `paramType` (optional) is the parameter it is matched against; a bare
951
+ // type parameter takes the argument as it is.
952
+ _coreOfTypeParam(argType, paramType = null) {
953
+ if (argType.kind !== "typeParam" || !this._inferScope) return null;
954
+ if (paramType?.kind === "typeParam") return null;
955
+ return this._coreType(argType, this._inferScope);
956
+ },
957
+
641
958
  _inferFromPair(paramType, argType, map) {
642
959
  if (!paramType || !argType) return;
960
+ argType = this._coreOfTypeParam(argType, paramType) ?? argType;
643
961
  if (paramType.kind === "typeParam") {
644
962
  const prev = map.get(paramType.name);
645
963
  if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
@@ -662,15 +980,91 @@ export const resolveMethods = {
662
980
  },
663
981
 
664
982
  _checkUnionConstraint(typeArg, constraint, node) {
665
- for (const term of constraint.terms) {
666
- if (term.approx) return;
667
- const termType = this.resolveTypeNode(term.type, this.globals);
668
- if (typeStr(typeArg) === typeStr(termType)) return;
669
- }
983
+ if (typeArg?._tainted || this._unionAccepts(typeArg, constraint, new Set()))
984
+ return;
670
985
  this.err(`type ${typeStr(typeArg)} does not satisfy constraint`, node);
671
986
  },
672
987
 
988
+ // Whether `typeArg` is in the type set of a union: some term holds it.
989
+ // `scope` resolves the names its terms mention.
990
+ _unionAccepts(typeArg, union, active, scope = this.globals) {
991
+ return union.terms.some((term) => {
992
+ // `~[]E` may mention a type parameter that is not in `scope`, so an
993
+ // approximation term is resolved (and compared) only when it names
994
+ // a type.
995
+ if (!term.resolved && term.approx && term.type?.kind !== "TypeName")
996
+ return true;
997
+ const t = term.resolved ?? this.resolveTypeNode(term.type, scope);
998
+ return this._termAccepts(typeArg, t, term.approx, active);
999
+ });
1000
+ },
1001
+
1002
+ // One union term: a named interface contributes its own type set
1003
+ // (`S | U` with `type S interface{ ~int }`), `~T` any type with the
1004
+ // underlying type of `T`, and `T` exactly `T`.
1005
+ _termAccepts(typeArg, t, approx, active) {
1006
+ const base =
1007
+ t?.kind === "named"
1008
+ ? (this.resolveType(t.underlying) ?? t.underlying)
1009
+ : t;
1010
+ if (base?.kind === "interface")
1011
+ return this._interfaceAccepts(typeArg, base, active);
1012
+ if (!approx) return typeStr(typeArg) === typeStr(t);
1013
+ const want = this._deepUnderlying(t);
1014
+ if (typeArg?.kind === "typeParam" || want?.kind !== "basic") return true;
1015
+ return typeStr(this._deepUnderlying(typeArg)) === typeStr(want);
1016
+ },
1017
+
1018
+ // An interface's type set is the intersection of its own union and the
1019
+ // type sets of the interfaces it embeds; one with neither holds every type.
1020
+ _interfaceAccepts(typeArg, iface, active) {
1021
+ if (active.has(iface)) return true;
1022
+ active.add(iface);
1023
+ const ok =
1024
+ (!iface.unionConstraint ||
1025
+ this._unionAccepts(
1026
+ typeArg,
1027
+ iface.unionConstraint,
1028
+ active,
1029
+ iface._unionScope ?? this.globals,
1030
+ )) &&
1031
+ (iface._typeSetEmbeds ?? []).every((e) =>
1032
+ this._interfaceAccepts(typeArg, e, active),
1033
+ );
1034
+ active.delete(iface);
1035
+ return ok;
1036
+ },
1037
+
1038
+ // Go's comparability: slices, maps and funcs are not comparable, nor are
1039
+ // arrays and structs that contain them.
1040
+ _isComparable(t, seen = new Set()) {
1041
+ const base = t?.kind === "named" ? t.underlying : t;
1042
+ if (!base || seen.has(base)) return true;
1043
+ seen.add(base);
1044
+ switch (base.kind) {
1045
+ case "slice":
1046
+ case "map":
1047
+ case "func":
1048
+ return false;
1049
+ case "array":
1050
+ return this._isComparable(base.elem, seen);
1051
+ case "struct":
1052
+ return [...(base.fields?.values() ?? [])].every((f) =>
1053
+ this._isComparable(f, seen),
1054
+ );
1055
+ default:
1056
+ return true;
1057
+ }
1058
+ },
1059
+
673
1060
  _checkInterfaceConstraint(typeArg, base, constraint, node) {
1061
+ if (base._comparable && !this._isComparable(typeArg)) {
1062
+ this.err(
1063
+ `type ${typeStr(typeArg)} does not satisfy constraint ${typeStr(constraint)}`,
1064
+ node,
1065
+ );
1066
+ return;
1067
+ }
674
1068
  if (base.methods.size === 0 && !base.unionConstraint) return;
675
1069
  if (base.unionConstraint) {
676
1070
  this.checkConstraint(typeArg, base.unionConstraint, node);
@@ -758,7 +1152,7 @@ export const resolveMethods = {
758
1152
  return (
759
1153
  baseType.kind === "pointer" &&
760
1154
  field !== "value" &&
761
- baseType.base?.kind === "named"
1155
+ (baseType.base?.kind === "named" || baseType.base?.kind === "struct")
762
1156
  );
763
1157
  },
764
1158
 
@@ -788,7 +1182,11 @@ export const resolveMethods = {
788
1182
  },
789
1183
 
790
1184
  _fieldTypeStruct(base, baseType, field, node) {
1185
+ if (base.fields?.has(field) && isMethodPromotedOverField(base, field))
1186
+ return base.methods.get(field);
791
1187
  if (base.fields?.has(field)) {
1188
+ if (isAmbiguousFieldSelector(base, field))
1189
+ return this._errAmbiguousSelector(baseType, field, node);
792
1190
  if (
793
1191
  field.startsWith("_") &&
794
1192
  (baseType?.name?.startsWith("strings.") ||
@@ -802,6 +1200,8 @@ export const resolveMethods = {
802
1200
  return base.fields.get(field);
803
1201
  }
804
1202
  if (base.methods?.has(field)) return base.methods.get(field);
1203
+ if (isAmbiguousPromotion(base, field))
1204
+ return this._errAmbiguousSelector(baseType, field, node);
805
1205
  const typeName = baseType?.name || base.name;
806
1206
  if (typeName) {
807
1207
  const dName = displayTypeName(typeName);
@@ -826,6 +1226,12 @@ export const resolveMethods = {
826
1226
  return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
827
1227
  },
828
1228
 
1229
+ _errAmbiguousSelector(baseType, field, node) {
1230
+ const text = exprText(node?.expr);
1231
+ const recv = text === "expression" ? typeStr(baseType) : text;
1232
+ return this.err(`ambiguous selector ${recv}.${field}`, node);
1233
+ },
1234
+
829
1235
  _fieldTypeInterface(base, baseType, field, node) {
830
1236
  if (base.methods?.has(field)) return base.methods.get(field);
831
1237
  if (base.fields?.has(field)) return base.fields.get(field);
@@ -891,16 +1297,35 @@ export const resolveMethods = {
891
1297
  return ANY;
892
1298
  },
893
1299
 
894
- _fieldTypeNamespace(base, field, node) {
1300
+ // An error for `field` when the namespace cannot offer it to this package:
1301
+ // an unexported name, or a member of a `js` file in a wasm or both package.
1302
+ _namespaceMemberError(base, field, node) {
895
1303
  if (base._gofront && field.length > 0 && field[0] >= "a" && field[0] <= "z")
896
1304
  return this.err(
897
1305
  `cannot refer to unexported name ${base.name}.${field}`,
898
1306
  node,
899
1307
  );
1308
+ const wasm = this.target === "wasm" || this.target === "both";
1309
+ return wasm && base._jsOnly?.has(field)
1310
+ ? this._errJsOnly(`${base.name}.${field}`, node)
1311
+ : null;
1312
+ },
1313
+
1314
+ _fieldTypeNamespace(base, field, node) {
1315
+ const bad = this._namespaceMemberError(base, field, node);
1316
+ if (bad) return bad;
900
1317
  // Go-source stdlib member: backends emit the mangled top-level name.
901
1318
  const mangled = base._mangle?.get(field);
902
1319
  if (mangled) {
903
1320
  node._stdName = mangled;
1321
+ // Lazy stdlib bodies: referencing a member checks what it reaches.
1322
+ const failure = base._reach?.(mangled);
1323
+ if (failure)
1324
+ this.err(
1325
+ `internal stdlib error in ${base._path}; please report`,
1326
+ node,
1327
+ failure,
1328
+ );
904
1329
  return base.members[field];
905
1330
  }
906
1331
  if (field in base.members) {