gofront 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +14 -14
  2. package/package.json +10 -38
  3. package/src/backend/js/expressions.js +2060 -389
  4. package/src/backend/js/index.js +586 -174
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +936 -1644
  7. package/src/backend/js/statements.js +1349 -132
  8. package/src/backend/js/stdlib/index.js +51 -94
  9. package/src/backend/js/templ.js +67 -118
  10. package/src/backend/js/type-info.js +436 -0
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +80 -0
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +753 -872
  15. package/src/backend/wasm/emit-builtins.js +914 -237
  16. package/src/backend/wasm/emit-exprs.js +1456 -195
  17. package/src/backend/wasm/emit-maps.js +78 -380
  18. package/src/backend/wasm/emit-stmts.js +1659 -214
  19. package/src/backend/wasm/emit.js +614 -88
  20. package/src/backend/wasm/encode.js +43 -13
  21. package/src/backend/wasm/globals.js +491 -0
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +406 -3195
  24. package/src/backend/wasm/method-expr.js +392 -0
  25. package/src/backend/wasm/module-emitter.js +1615 -0
  26. package/src/backend/wasm/monomorph.js +164 -9
  27. package/src/backend/wasm/natives.js +596 -0
  28. package/src/backend/wasm/prescan.js +249 -0
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -37
  31. package/src/backend/wasm/stdlib/simd.js +293 -28
  32. package/src/backend/wasm/types.js +260 -27
  33. package/src/backend/wasm/variadic.js +93 -0
  34. package/src/backend/wasm/wat.js +47 -20
  35. package/src/{cli-core.js → cli/cli-core.js} +241 -11
  36. package/src/{test-runner.js → cli/test-runner.js} +188 -122
  37. package/src/{vendor.js → cli/vendor.js} +3 -4
  38. package/src/compiler.js +202 -386
  39. package/src/index.js +15 -169
  40. package/src/{lexer.js → lexer/lexer.js} +203 -62
  41. package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
  42. package/src/{tokens.js → lexer/tokens.js} +2 -0
  43. package/src/link/sourcemap.js +97 -0
  44. package/src/link/stdlib.js +737 -0
  45. package/src/link/type-names.js +148 -0
  46. package/src/link/wasm.js +318 -0
  47. package/src/lower/bounds.js +547 -0
  48. package/src/lower/boxing.js +1 -1
  49. package/src/lower/captures.js +267 -64
  50. package/src/lower/embedding.js +32 -3
  51. package/src/lower/errors-as.js +196 -0
  52. package/src/lower/fmt-formatters.js +905 -0
  53. package/src/lower/functions.js +2 -0
  54. package/src/lower/gen-types.js +274 -0
  55. package/src/lower/index.js +33 -19
  56. package/src/lower/json-codecs.js +1379 -0
  57. package/src/lower/ownership.js +47 -8
  58. package/src/lower/range.js +86 -1
  59. package/src/lower/reachability.js +150 -0
  60. package/src/lower/walk.js +12 -0
  61. package/src/parser/declarations.js +83 -15
  62. package/src/parser/expressions.js +110 -18
  63. package/src/parser/index.js +2 -1
  64. package/src/parser/statements.js +38 -22
  65. package/src/{templ-parser.js → parser/templ.js} +179 -17
  66. package/src/parser/types.js +70 -9
  67. package/src/resolver.js +21 -1
  68. package/src/typechecker/assignability.js +291 -19
  69. package/src/typechecker/const-fold.js +399 -0
  70. package/src/typechecker/constants.js +282 -0
  71. package/src/typechecker/expressions.js +1220 -183
  72. package/src/typechecker/index.js +659 -92
  73. package/src/typechecker/init-order.js +426 -0
  74. package/src/typechecker/operand-text.js +40 -0
  75. package/src/typechecker/resolve.js +613 -62
  76. package/src/typechecker/statements.js +212 -20
  77. package/src/typechecker/stdlib/excluded.js +902 -0
  78. package/src/typechecker/stdlib/index.js +38 -0
  79. package/src/typechecker/stdlib/simd.js +65 -15
  80. package/src/typechecker/termination.js +11 -2
  81. package/src/typechecker/types.js +268 -514
  82. package/stdlib/bytes/buffer.go +479 -0
  83. package/stdlib/bytes/bytes.go +1010 -0
  84. package/stdlib/bytes/reader.go +161 -0
  85. package/stdlib/cmp/cmp.go +65 -0
  86. package/stdlib/context/context.go +115 -0
  87. package/stdlib/crypto/rand/rand.go +34 -0
  88. package/stdlib/crypto/sha256/sha256.go +249 -0
  89. package/stdlib/encoding/base64/base64.go +331 -0
  90. package/stdlib/encoding/binary/binary.go +332 -0
  91. package/stdlib/encoding/hex/hex.go +140 -0
  92. package/stdlib/encoding/json/errors.go +31 -0
  93. package/stdlib/encoding/json/indent.go +71 -0
  94. package/stdlib/encoding/json/json.go +62 -0
  95. package/stdlib/encoding/json/number.go +12 -0
  96. package/stdlib/encoding/json/stream.go +173 -0
  97. package/stdlib/errors/errors.go +174 -0
  98. package/stdlib/fmt/errors.go +110 -0
  99. package/stdlib/fmt/format.go +732 -0
  100. package/stdlib/fmt/print.go +1232 -0
  101. package/stdlib/gom/gom.go +293 -0
  102. package/stdlib/hash/hash.go +53 -0
  103. package/stdlib/html/html.go +251 -0
  104. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  105. package/stdlib/internal/jsonrt/dec.go +871 -0
  106. package/stdlib/internal/jsonrt/enc.go +516 -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 +658 -0
  111. package/stdlib/io/io.go +502 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/maps.go +131 -0
  114. package/stdlib/math/bits/bits.go +507 -0
  115. package/stdlib/math/extra.go +272 -0
  116. package/stdlib/math/math.go +439 -0
  117. package/stdlib/math/rand/rand.go +297 -0
  118. package/stdlib/os/os.go +93 -0
  119. package/stdlib/path/filepath/filepath.go +47 -0
  120. package/stdlib/path/path.go +394 -0
  121. package/stdlib/regexp/regexp.go +231 -0
  122. package/stdlib/slices/slices.go +526 -0
  123. package/stdlib/slices/sort.go +209 -0
  124. package/stdlib/slices/zsort_cmp.go +480 -0
  125. package/stdlib/slices/zsort_ordered.go +482 -0
  126. package/stdlib/sort/sort.go +500 -0
  127. package/stdlib/sort/zsort_func.go +480 -0
  128. package/stdlib/sort/zsort_iface.go +480 -0
  129. package/stdlib/strconv/float.go +1699 -0
  130. package/stdlib/strconv/isprint.go +752 -0
  131. package/stdlib/strconv/quote.go +526 -0
  132. package/stdlib/strconv/strconv.go +616 -0
  133. package/stdlib/strings/builder.go +95 -0
  134. package/stdlib/strings/reader.go +163 -0
  135. package/stdlib/strings/replacer.go +102 -0
  136. package/stdlib/strings/strings.go +807 -0
  137. package/stdlib/sync/sync.go +268 -0
  138. package/stdlib/testing/async_js.go +61 -0
  139. package/stdlib/testing/benchmark.go +297 -0
  140. package/stdlib/testing/testing.go +523 -0
  141. package/stdlib/time/duration.go +233 -0
  142. package/stdlib/time/format.go +745 -0
  143. package/stdlib/time/parse.go +790 -0
  144. package/stdlib/time/sleep_js.go +63 -0
  145. package/stdlib/time/time.go +834 -0
  146. package/stdlib/unicode/unicode.go +250 -0
  147. package/stdlib/unicode/utf8/utf8.go +464 -0
  148. package/src/backend/js/stdlib/base64.js +0 -12
  149. package/src/backend/js/stdlib/binary.js +0 -102
  150. package/src/backend/js/stdlib/bits.js +0 -46
  151. package/src/backend/js/stdlib/builder.js +0 -63
  152. package/src/backend/js/stdlib/bytes.js +0 -122
  153. package/src/backend/js/stdlib/errors.js +0 -44
  154. package/src/backend/js/stdlib/fmt.js +0 -178
  155. package/src/backend/js/stdlib/gom.js +0 -155
  156. package/src/backend/js/stdlib/hex.js +0 -17
  157. package/src/backend/js/stdlib/html.js +0 -20
  158. package/src/backend/js/stdlib/io.js +0 -137
  159. package/src/backend/js/stdlib/json.js +0 -972
  160. package/src/backend/js/stdlib/maps.js +0 -45
  161. package/src/backend/js/stdlib/math.js +0 -69
  162. package/src/backend/js/stdlib/os.js +0 -13
  163. package/src/backend/js/stdlib/path.js +0 -48
  164. package/src/backend/js/stdlib/rand.js +0 -32
  165. package/src/backend/js/stdlib/regexp.js +0 -54
  166. package/src/backend/js/stdlib/sha256.js +0 -12
  167. package/src/backend/js/stdlib/shared.js +0 -17
  168. package/src/backend/js/stdlib/simd.js +0 -24
  169. package/src/backend/js/stdlib/slices.js +0 -77
  170. package/src/backend/js/stdlib/sort.js +0 -30
  171. package/src/backend/js/stdlib/strconv.js +0 -74
  172. package/src/backend/js/stdlib/strings.js +0 -222
  173. package/src/backend/js/stdlib/sync.js +0 -13
  174. package/src/backend/js/stdlib/testing.js +0 -34
  175. package/src/backend/js/stdlib/time.js +0 -128
  176. package/src/backend/js/stdlib/unicode.js +0 -25
  177. package/src/backend/js/stdlib/utf8.js +0 -26
  178. package/src/backend/wasm/const-fold.js +0 -205
  179. package/src/backend/wasm/emit-stdlib.js +0 -13
  180. package/src/backend/wasm/stdlib/bytes.js +0 -5039
  181. package/src/backend/wasm/stdlib/core.js +0 -8903
  182. package/src/backend/wasm/stdlib/io.js +0 -2161
  183. package/src/backend/wasm/stdlib/packages.js +0 -2400
  184. package/src/backend/wasm/stdlib/pure.js +0 -720
  185. package/src/backend/wasm/stdlib/time.js +0 -1200
  186. package/src/typechecker/stdlib/core.js +0 -801
  187. package/src/typechecker/stdlib/extended.js +0 -914
  188. package/src/typechecker/stdlib.js +0 -22
  189. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  190. /package/src/{colors.js → cli/colors.js} +0 -0
  191. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  192. /package/src/{minifier.js → cli/minifier.js} +0 -0
  193. /package/src/{project-config.js → cli/project-config.js} +0 -0
  194. /package/src/{pwa.js → cli/pwa.js} +0 -0
@@ -2,11 +2,15 @@
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,
11
+ DECLARED_ANY,
9
12
  defaultType,
13
+ displayTypeName,
10
14
  isAny,
11
15
  isTypedArraySlice,
12
16
  Scope,
@@ -20,6 +24,97 @@ import {
20
24
  /** @typedef {import('./index.js').TypeChecker} TypeChecker */
21
25
 
22
26
  // Dispatch table for substituteType — keyed by type.kind.
27
+ // Nesting depth of in-progress instances of one generic type before the
28
+ // instantiation is reported as a cycle.
29
+ const MAX_INSTANTIATION_DEPTH = 32;
30
+
31
+ // Two imported packages may both declare `Pt`, so the cache key of an
32
+ // instance tells declarations apart by identity instead of by display name.
33
+ const declIds = new WeakMap();
34
+ let nextDeclId = 0;
35
+ function declId(t) {
36
+ if (!declIds.has(t)) declIds.set(t, ++nextDeclId);
37
+ return declIds.get(t);
38
+ }
39
+
40
+ // Go infers T from the "largest" untyped constant kind (int < rune < float <
41
+ // complex), and a typed argument beats any untyped one.
42
+ const UNTYPED_RANK = { int: 0, int32: 1, float64: 2, complex128: 3 };
43
+ function untypedWins(prev, next) {
44
+ if (prev.kind !== "untyped") return false;
45
+ if (next.kind !== "untyped") return true;
46
+ return (UNTYPED_RANK[next.base] ?? 0) > (UNTYPED_RANK[prev.base] ?? 0);
47
+ }
48
+
49
+ // Node count of a type, so a request that keeps growing can be told from one
50
+ // that merely repeats.
51
+ function typeSize(t) {
52
+ switch (t?.kind) {
53
+ case "named":
54
+ return 1 + (t.typeArgs ?? []).reduce((n, a) => n + typeSize(a), 0);
55
+ case "pointer":
56
+ return 1 + typeSize(t.base);
57
+ case "slice":
58
+ case "array":
59
+ return 1 + typeSize(t.elem);
60
+ case "map":
61
+ return 1 + typeSize(t.key) + typeSize(t.value);
62
+ case "func":
63
+ return (
64
+ 1 +
65
+ [...(t.params ?? []), ...(t.returns ?? [])].reduce(
66
+ (n, a) => n + typeSize(a),
67
+ 0,
68
+ )
69
+ );
70
+ default:
71
+ return 1;
72
+ }
73
+ }
74
+
75
+ // Whether `t` mentions one of the type parameters `params` below its root.
76
+ function nestsTypeParam(t, params) {
77
+ const mentions = (u) => {
78
+ switch (u?.kind) {
79
+ case "typeParam":
80
+ return params.includes(u);
81
+ case "pointer":
82
+ return mentions(u.base);
83
+ case "slice":
84
+ case "array":
85
+ return mentions(u.elem);
86
+ case "map":
87
+ return mentions(u.key) || mentions(u.value);
88
+ case "func":
89
+ return [...(u.params ?? []), ...(u.returns ?? [])].some(mentions);
90
+ case "named":
91
+ return (u.typeArgs ?? []).some(mentions);
92
+ default:
93
+ return false;
94
+ }
95
+ };
96
+ return t?.kind !== "typeParam" && mentions(t);
97
+ }
98
+
99
+ function instanceKey(t) {
100
+ switch (t?.kind) {
101
+ case "named":
102
+ return t.typeArgs?.length
103
+ ? `${typeStr(t)}`
104
+ : `${typeStr(t)}#${declId(t)}`;
105
+ case "pointer":
106
+ return `*${instanceKey(t.base)}`;
107
+ case "slice":
108
+ return `[]${instanceKey(t.elem)}`;
109
+ case "array":
110
+ return `[${t.size ?? "..."}]${instanceKey(t.elem)}`;
111
+ case "map":
112
+ return `map[${instanceKey(t.key)}]${instanceKey(t.value)}`;
113
+ default:
114
+ return typeStr(t);
115
+ }
116
+ }
117
+
23
118
  const SUBSTITUTE_DISPATCH = {
24
119
  basic: (_s, type) => type,
25
120
  untyped: (_s, type) => type,
@@ -63,18 +158,53 @@ const SUBSTITUTE_DISPATCH = {
63
158
  fields,
64
159
  methods,
65
160
  _embeds: type._embeds,
161
+ // The JSON codecs read the field types from here.
162
+ _structFields: type._structFields?.map((f) => ({
163
+ ...f,
164
+ type: s.substituteType(f.type, map),
165
+ })),
66
166
  };
67
167
  },
68
168
  interface: (s, type, map) => {
69
169
  const methods = new Map();
70
170
  for (const [k, v] of type.methods) methods.set(k, s.substituteType(v, map));
71
- return { kind: "interface", name: type.name, methods };
171
+ const out = { kind: "interface", name: type.name, methods };
172
+ // The type set follows the substitution (`interface{ ~[]E }` with E=int).
173
+ if (type.unionConstraint)
174
+ out.unionConstraint = {
175
+ ...type.unionConstraint,
176
+ terms: type.unionConstraint.terms.map((term) => ({
177
+ ...term,
178
+ resolved: s.substituteType(
179
+ term.resolved ?? s.resolveTypeNode(term.type, type._unionScope),
180
+ map,
181
+ ),
182
+ })),
183
+ };
184
+ if (type._typeSetEmbeds)
185
+ out._typeSetEmbeds = type._typeSetEmbeds.map((e) =>
186
+ s.substituteType(e, map),
187
+ );
188
+ return out;
189
+ },
190
+ named: (s, type, map) => {
191
+ // An instance of a generic type: re-instantiate with the substituted
192
+ // arguments (through the instance cache) rather than copying its
193
+ // possibly cyclic underlying type.
194
+ if (type._genericOf)
195
+ return s.instantiateGenericWithArgs(
196
+ type._genericOf,
197
+ type.typeArgs.map((t) => s.substituteType(t, map)),
198
+ );
199
+ return {
200
+ kind: "named",
201
+ name: type.name,
202
+ ...(type.typeArgs && {
203
+ typeArgs: type.typeArgs.map((t) => s.substituteType(t, map)),
204
+ }),
205
+ underlying: s.substituteType(type.underlying, map),
206
+ };
72
207
  },
73
- named: (s, type, map) => ({
74
- kind: "named",
75
- name: type.name,
76
- underlying: s.substituteType(type.underlying, map),
77
- }),
78
208
  };
79
209
 
80
210
  /** @type {ThisType<TypeChecker>} */
@@ -83,45 +213,33 @@ export const resolveMethods = {
83
213
  if (!node) return ANY;
84
214
  switch (node.kind) {
85
215
  case "TypeName": {
86
- if (BASIC_TYPES[node.name]) return BASIC_TYPES[node.name];
216
+ if (BASIC_TYPES[node.name])
217
+ return node.name === "any" ? DECLARED_ANY : BASIC_TYPES[node.name];
87
218
  if (node.name === "comparable") return COMPARABLE;
88
219
  if (scope) {
89
220
  const fromScope = scope.lookup(node.name);
90
221
  if (fromScope?.kind === "typeParam") return fromScope;
222
+ if (fromScope?.kind === "aliasArg") return fromScope.type;
91
223
  }
92
224
  const named = this.types.get(node.name);
93
225
  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));
226
+ this._useQualifiedTypeName(node, named);
97
227
  return named;
98
228
  }
229
+ const jsOnly = this._jsOnlyMember(node.name);
230
+ if (jsOnly) return this._errJsOnly(jsOnly, node);
231
+ if (EXCLUDED_STDLIB_MEMBERS.has(node.name)) {
232
+ return this.err(
233
+ `${node.name} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(node.name)}`,
234
+ node,
235
+ );
236
+ }
99
237
  return this.err(`Unknown type '${node.name}'`, node);
100
238
  }
101
239
  case "GenericTypeName":
102
240
  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
- }
241
+ case "IndexExpr":
242
+ return this._resolveGenericTypeName(node, scope);
125
243
  case "TypeParam": {
126
244
  const constraint = node.constraint
127
245
  ? this.resolveTypeNode(node.constraint, scope)
@@ -147,6 +265,13 @@ export const resolveMethods = {
147
265
  return this._resolveStructType(node, scope);
148
266
  case "InterfaceType":
149
267
  return this._resolveInterfaceType(node, scope);
268
+ case "UnaryExpr":
269
+ // `new(*T)`: the parser reads the pointer type as a dereference.
270
+ if (node.op !== "*") return ANY;
271
+ return {
272
+ kind: "pointer",
273
+ base: this.resolveTypeNode(node.operand, scope),
274
+ };
150
275
  case "Ident":
151
276
  return this.resolveTypeNode(
152
277
  { kind: "TypeName", name: node.name },
@@ -157,11 +282,14 @@ export const resolveMethods = {
157
282
  // SelectorExpr; resolve it like a qualified TypeName.
158
283
  if (node.expr?.kind === "Ident" && typeof node.field === "string") {
159
284
  const qualified = `${node.expr.name}.${node.field}`;
160
- if (this.types.has(qualified))
161
- return this.resolveTypeNode(
285
+ if (this.types.has(qualified)) {
286
+ const resolved = this.resolveTypeNode(
162
287
  { kind: "TypeName", name: qualified },
163
288
  scope,
164
289
  );
290
+ this._useQualifiedTypeName(node, resolved);
291
+ return resolved;
292
+ }
165
293
  }
166
294
  return ANY;
167
295
  case "ArrayType":
@@ -178,24 +306,134 @@ export const resolveMethods = {
178
306
  }
179
307
  },
180
308
 
309
+ // `pkg.Name` when `pkg` is a Go-source package whose `js` files declare
310
+ // Name and this package is wasm or both (those files are not loaded).
311
+ _jsOnlyMember(qualified) {
312
+ if (this.target !== "wasm" && this.target !== "both") return null;
313
+ const dot = qualified.indexOf(".");
314
+ if (dot < 0) return null;
315
+ const ns = this.globals.lookup(qualified.slice(0, dot));
316
+ return ns?._jsOnly?.has(qualified.slice(dot + 1)) ? qualified : null;
317
+ },
318
+
319
+ _errJsOnly(qualified, node) {
320
+ this.recordBlocker("unsupported stdlib member", qualified);
321
+ return this.err(
322
+ `'${qualified}' is only available in js packages`,
323
+ node,
324
+ "move this call to a JS package",
325
+ );
326
+ },
327
+
328
+ // `pkg.T` in a type position counts as using the import; a Go-source
329
+ // stdlib type is emitted under its mangled name.
330
+ _useQualifiedTypeName(node, named) {
331
+ const name =
332
+ node.name ?? (node.expr ? `${node.expr.name}.${node.field}` : "");
333
+ const dot = name.indexOf(".");
334
+ if (dot <= 0) return;
335
+ this.globals.lookup(name.slice(0, dot));
336
+ if (named.name?.includes("$$")) node._stdName = named.name;
337
+ },
338
+
339
+ // `[N]T` with a named constant (or constant expression) length folds to a number.
340
+ _foldArrayLen(size, scope) {
341
+ const resolve = (name, depth = 0) => {
342
+ const e = depth < 16 ? scope.lookupConstExpr(name) : null;
343
+ return e ? foldConstExpr(e, (n) => resolve(n, depth + 1)) : undefined;
344
+ };
345
+ try {
346
+ const lit = foldConstExpr(size, resolve);
347
+ const n = lit ? Number(lit.value) : Number.NaN;
348
+ if (Number.isInteger(n) && n >= 0) return n;
349
+ } catch {
350
+ // fall through to the checker's evaluator
351
+ }
352
+ // `[math.MaxInt8]T`: a package constant the AST folder cannot see.
353
+ const q = this._constIntValue(size, scope);
354
+ if (q === null || !Number.isInteger(q) || q < 0) return size;
355
+ this._useQualifiedConsts(size);
356
+ return q;
357
+ },
358
+
359
+ // `pkg.K` inside a folded array length counts as using the import.
360
+ _useQualifiedConsts(node) {
361
+ if (!node || typeof node !== "object") return;
362
+ if (node.kind === "SelectorExpr" && node.expr?.kind === "Ident")
363
+ this.globals.lookup(node.expr.name);
364
+ for (const key of ["expr", "left", "right", "operand"])
365
+ this._useQualifiedConsts(node[key]);
366
+ for (const arg of node.args ?? []) this._useQualifiedConsts(arg);
367
+ },
368
+
181
369
  _resolveArrayTypeNode(node, scope) {
370
+ // Fold in place so backends that read the type node see a literal length.
371
+ if (!node.inferLen && node.size?.value === undefined) {
372
+ const n = this._foldArrayLen(node.size, scope);
373
+ if (typeof n === "number")
374
+ node.size = {
375
+ kind: "BasicLit",
376
+ litKind: "INT",
377
+ value: String(n),
378
+ _line: node.size._line,
379
+ _col: node.size._col,
380
+ };
381
+ }
182
382
  return {
183
383
  kind: "array",
184
384
  size: node.inferLen
185
385
  ? null
186
386
  : node.size?.value !== undefined
187
387
  ? Number(node.size.value)
188
- : node.size,
388
+ : this._foldArrayLen(node.size, scope),
189
389
  elem: this.resolveTypeNode(node.elem, scope),
190
390
  };
191
391
  },
192
392
 
393
+ // `G[X]`: instantiate a generic named type, or expand a generic alias.
394
+ _resolveGenericTypeName(node, scope) {
395
+ const sel = node.expr?.kind === "SelectorExpr" ? node.expr : null;
396
+ const qualified =
397
+ sel?.expr?.kind === "Ident" ? `${sel.expr.name}.${sel.field}` : null;
398
+ const name =
399
+ node.name ??
400
+ (node.expr?.kind === "Ident" ? node.expr.name : null) ??
401
+ qualified;
402
+ const typeArgs = node.typeArgs ?? (node.index ? [node.index] : []);
403
+ const base = name
404
+ ? this.types.get(name)
405
+ : node.expr
406
+ ? this.resolveTypeNode(node.expr, scope)
407
+ : null;
408
+ if (!base) return this.err(`Unknown type '${name ?? "?"}'`, node);
409
+ if (qualified) {
410
+ this.globals.lookup(sel.expr.name);
411
+ // `iter.Seq[int](f)`: the selector names the mangled stdlib type.
412
+ this._useQualifiedTypeName(sel, base);
413
+ } else if (name)
414
+ this._useQualifiedTypeName(node.name ? node : node.expr, base);
415
+ if (base.kind === "named" && base._generic?.isAlias) {
416
+ const aliasArgs = typeArgs.map((n) => this.resolveTypeNode(n, scope));
417
+ node._typeArgs = aliasArgs;
418
+ return this._instantiateGenericAlias(base._generic, aliasArgs);
419
+ }
420
+ if (base.kind === "named" && base._generic) {
421
+ const res = this.instantiateGenericType(base._generic, typeArgs, scope);
422
+ node._typeArgs = res.typeArgs;
423
+ node._genericType = base;
424
+ return res;
425
+ }
426
+ return this.err(`Type '${name ?? "?"}' is not generic`, node);
427
+ },
428
+
193
429
  _resolveFuncTypeNode(node, scope) {
194
430
  const params = node.params.map((p) => this.resolveTypeNode(p.type, scope));
195
431
  const returns = node.returnType
196
432
  ? [this.resolveTypeNode(node.returnType, scope)]
197
433
  : [VOID];
198
- return { kind: "func", params, returns };
434
+ const fn = { kind: "func", params, returns };
435
+ if (node.params.at(-1)?.variadic) fn.variadic = true;
436
+ return fn;
199
437
  },
200
438
 
201
439
  resolveTypeNodeName(node, scope) {
@@ -223,6 +461,7 @@ export const resolveMethods = {
223
461
  if (f.embedded) {
224
462
  const inner = ft.kind === "pointer" ? ft.base : ft;
225
463
  const embedName = (
464
+ f.embeddedName ??
226
465
  f.astType?.name ??
227
466
  f.type?.name ??
228
467
  f.type?.base?.name ??
@@ -262,6 +501,9 @@ export const resolveMethods = {
262
501
  if (isVariadic) mType.variadic = true;
263
502
  methods.set(m.name, mType);
264
503
  }
504
+ // Embedded interfaces that carry a type set (`interface{ Integer }`):
505
+ // kept so type-parameter operator checks can follow them.
506
+ const typeSetEmbeds = [];
265
507
  if (node.embeds) {
266
508
  for (const embed of node.embeds) {
267
509
  const resolved = this.resolveTypeNode(embed, scope);
@@ -271,6 +513,8 @@ export const resolveMethods = {
271
513
  for (const [mName, mType] of base.methods) {
272
514
  if (!methods.has(mName)) methods.set(mName, mType);
273
515
  }
516
+ if (base.unionConstraint || base._typeSetEmbeds)
517
+ typeSetEmbeds.push(base);
274
518
  } else if (!isAny(resolved)) {
275
519
  this.err(
276
520
  `cannot embed non-interface type ${typeStr(resolved)}`,
@@ -280,7 +524,11 @@ export const resolveMethods = {
280
524
  }
281
525
  }
282
526
  const result = { kind: "interface", methods };
283
- if (node.unionConstraint) result.unionConstraint = node.unionConstraint;
527
+ if (node.unionConstraint) {
528
+ result.unionConstraint = node.unionConstraint;
529
+ result._unionScope = scope;
530
+ }
531
+ if (typeSetEmbeds.length > 0) result._typeSetEmbeds = typeSetEmbeds;
284
532
  return result;
285
533
  },
286
534
 
@@ -301,45 +549,184 @@ export const resolveMethods = {
301
549
  },
302
550
 
303
551
  instantiateGenericType(generic, typeArgNodes, scope) {
304
- const map = new Map();
305
552
  const typeArgs = typeArgNodes.map((n) => this.resolveTypeNode(n, scope));
553
+ return this.instantiateGenericWithArgs(generic, typeArgs);
554
+ },
555
+
556
+ // One instance per type-argument list. The instance is cached before its
557
+ // fields are resolved, so a field that names the instance again (`Next
558
+ // *List[T]`, `Children []Tree[T]`, or a cycle through another generic type)
559
+ // reaches the same object instead of recursing forever.
560
+ instantiateGenericWithArgs(generic, typeArgs) {
561
+ const key = typeArgs.map(instanceKey).join(",");
562
+ generic._instances ??= new Map();
563
+ const cached = generic._instances.get(key);
564
+ if (cached) {
565
+ this._syncInstanceMethods(generic, cached);
566
+ return cached;
567
+ }
568
+ // `type T[X any] struct{ n *T[[]X] }` asks for T[[]X], then T[[][]X], and
569
+ // so on: an instance still being built that keeps asking for a larger
570
+ // one never settles.
571
+ generic._building ??= [];
572
+ generic._buildSizes ??= [];
573
+ const building = generic._building;
574
+ const size = typeArgs.reduce((n, a) => n + typeSize(a), 0);
575
+ // A request for a larger instance while one is being built never
576
+ // settles (`T[X]` containing `T[[]X]`), and doubling forms such as
577
+ // `T[Pair[X, X]]` blow up long before a depth limit is reached.
578
+ const top = generic._buildSizes[generic._buildSizes.length - 1];
579
+ // Inside a method of `G[T]`, `G[[]T]` makes every instance ask for a
580
+ // larger one, even if the method is never called.
581
+ const growsReceiver =
582
+ this._recvGeneric === generic &&
583
+ typeArgs.some((a) => nestsTypeParam(a, generic.typeParams));
584
+ if (
585
+ growsReceiver ||
586
+ building.length >= MAX_INSTANTIATION_DEPTH ||
587
+ (generic._syncDepth ?? 0) >= MAX_INSTANTIATION_DEPTH ||
588
+ (top !== undefined && size > top)
589
+ ) {
590
+ if (!generic._cycleReported) {
591
+ generic._cycleReported = true;
592
+ this.err(
593
+ `instantiation cycle: ${generic.declNode.name} instantiates itself with ever larger type arguments`,
594
+ generic.declNode,
595
+ );
596
+ }
597
+ return ANY;
598
+ }
599
+ const map = new Map();
306
600
  for (let i = 0; i < generic.typeParams.length; i++)
307
601
  map.set(generic.typeParams[i].name, typeArgs[i] ?? ANY);
602
+ const named = {
603
+ kind: "named",
604
+ name: generic.declNode.name,
605
+ typeArgs,
606
+ underlying: null,
607
+ _genericOf: generic,
608
+ };
609
+ generic._instances.set(key, named);
308
610
  // Resolve the declaration with its type params in scope (as typeParams,
309
611
  // never as value symbols), then substitute the concrete arguments.
310
612
  const typeScope = new Scope(this.globals);
311
613
  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);
614
+ building.push(key);
615
+ generic._buildSizes.push(size);
616
+ let instantiated;
617
+ try {
618
+ const underlying = this.resolveTypeNode(generic.declNode.type, typeScope);
619
+ instantiated = this.substituteType(underlying, map);
620
+ } finally {
621
+ building.pop();
622
+ generic._buildSizes.pop();
623
+ }
314
624
  if (instantiated.kind === "struct") {
315
625
  instantiated.name = generic.declNode.name;
316
626
  instantiated.typeArgs = typeArgs;
317
627
  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));
628
+ }
629
+ named.underlying = instantiated;
630
+ named.methods = instantiated.methods;
631
+ named._instMap = map;
632
+ this._syncInstanceMethods(generic, named);
633
+ return named;
634
+ },
635
+
636
+ // Methods are collected after types, so an instance created earlier picks
637
+ // up the methods declared since.
638
+ _syncInstanceMethods(generic, named) {
639
+ const methods = named.methods;
640
+ if (!methods || !named._instMap || named._syncing) return;
641
+ // A method signature that names the instance reaches this again.
642
+ // `func (s Stack[T]) Wrap() Stack[[]T]` makes every instance's methods
643
+ // ask for a larger instance: count the nesting to diagnose it.
644
+ named._syncing = true;
645
+ generic._syncDepth = (generic._syncDepth ?? 0) + 1;
646
+ try {
647
+ for (const [mName, mType] of generic.methods ?? [])
648
+ if (!methods.has(mName))
649
+ methods.set(mName, this.substituteType(mType, named._instMap));
650
+ // Methods promoted from embedded structs (`Outer[T]` embedding `Base`).
651
+ this._promoteEmbeddedInto(named.underlying);
652
+ } finally {
653
+ generic._syncDepth--;
654
+ named._syncing = false;
655
+ }
656
+ },
657
+
658
+ // A[X] for `type A[P any] = T`: resolve T with each P bound to the
659
+ // concrete X, so A[int] is exactly T[int] (generic named types inside T
660
+ // are instantiated normally and keep their identity).
661
+ _instantiateGenericAlias(generic, typeArgs) {
662
+ const { typeParams, declNode } = generic;
663
+ if (typeArgs.length !== typeParams.length) {
664
+ return this.err(
665
+ `got ${typeArgs.length} type arguments but ${declNode.name} has ${typeParams.length} type parameters`,
666
+ declNode,
667
+ );
668
+ }
669
+ const aliasScope = new Scope(this.globals);
670
+ for (let i = 0; i < typeParams.length; i++) {
671
+ const tp = typeParams[i];
672
+ if (tp.constraint) {
673
+ const constraint = this.resolveTypeNode(tp.constraint, this.globals);
674
+ this.checkConstraint(typeArgs[i], constraint, declNode);
321
675
  }
676
+ aliasScope.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
322
677
  }
323
- return {
324
- kind: "named",
325
- name: generic.declNode.name,
326
- typeArgs,
327
- underlying: instantiated,
328
- methods: instantiated.methods,
329
- };
678
+ return this.resolveTypeNode(declNode.type, aliasScope);
330
679
  },
331
680
 
332
- inferTypeArgs(genericType, argTypes) {
681
+ // `explicit` seeds the leading type arguments of a partial instantiation
682
+ // (`F[int](x)`); the rest are inferred from the call arguments.
683
+ // `scope` resolves the type parameters of the calling function, so an
684
+ // argument of type `S ~[]E` unifies through its core type.
685
+ inferTypeArgs(genericType, argTypes, explicit = [], scope = null) {
333
686
  const map = new Map();
687
+ const saved = this._inferScope;
688
+ this._inferScope = scope;
689
+ try {
690
+ return this._inferTypeArgs(genericType, argTypes, explicit, map);
691
+ } finally {
692
+ this._inferScope = saved;
693
+ }
694
+ },
695
+
696
+ _inferTypeArgs(genericType, argTypes, explicit, map) {
697
+ for (let i = 0; i < explicit.length; i++)
698
+ map.set(genericType.typeParams[i].name, explicit[i]);
334
699
  const params = genericType.underlying.params;
335
700
  for (let i = 0; i < params.length && i < argTypes.length; i++)
336
701
  this._inferFromPair(params[i], argTypes[i], map);
702
+ this._inferFromCoreTypes(genericType.typeParams, map);
337
703
  for (const tp of genericType.typeParams) {
338
704
  if (!map.has(tp.name)) return null;
339
705
  }
340
706
  return genericType.typeParams.map((tp) => defaultType(map.get(tp.name)));
341
707
  },
342
708
 
709
+ // Constraint type inference: a type parameter `S` constrained by `~[]E`
710
+ // whose argument is known unifies `E` with that argument's element type.
711
+ _inferFromCoreTypes(typeParams, map) {
712
+ const scope = new Scope(this.globals);
713
+ for (const tp of typeParams) scope.define(tp.name, tp);
714
+ for (let round = 0; round < typeParams.length; round++) {
715
+ const before = map.size;
716
+ for (const tp of typeParams) {
717
+ const arg = map.get(tp.name);
718
+ if (!arg || !tp.constraint) continue;
719
+ const terms = this._constraintTerms(tp.constraint, new Set(), scope);
720
+ if (terms?.length !== 1) continue;
721
+ const argUnder =
722
+ this._coreOfTypeParam(arg) ??
723
+ this._deepUnderlying(this.resolveType(arg));
724
+ if (argUnder) this._inferFromPair(terms[0], argUnder, map);
725
+ }
726
+ if (map.size === before) break;
727
+ }
728
+ },
729
+
343
730
  _inferFromFuncPair(paramType, argType, map) {
344
731
  for (
345
732
  let i = 0;
@@ -358,27 +745,63 @@ export const resolveMethods = {
358
745
  _inferFromStructuredPair(paramType, argType, map) {
359
746
  if (paramType.kind === "slice" && argType.kind === "slice")
360
747
  this._inferFromPair(paramType.elem, argType.elem, map);
748
+ if (paramType.kind === "pointer" && argType.kind === "pointer")
749
+ this._inferFromPair(paramType.base, argType.base, map);
361
750
  if (paramType.kind === "map" && argType.kind === "map") {
362
751
  this._inferFromPair(paramType.key, argType.key, map);
363
752
  this._inferFromPair(paramType.value, argType.value, map);
364
753
  }
365
754
  if (paramType.kind === "func" && argType.kind === "func")
366
755
  this._inferFromFuncPair(paramType, argType, map);
756
+ if (paramType.kind === "tuple" && argType.kind === "tuple") {
757
+ for (
758
+ let i = 0;
759
+ i < paramType.types.length && i < argType.types.length;
760
+ i++
761
+ )
762
+ this._inferFromPair(paramType.types[i], argType.types[i], map);
763
+ }
764
+ },
765
+
766
+ // An argument whose type is a type parameter of the calling function
767
+ // (`S ~[]E`) unifies through its core type: that type, or null.
768
+ // `paramType` (optional) is the parameter it is matched against; a bare
769
+ // type parameter takes the argument as it is.
770
+ _coreOfTypeParam(argType, paramType = null) {
771
+ if (argType.kind !== "typeParam" || !this._inferScope) return null;
772
+ if (paramType?.kind === "typeParam") return null;
773
+ return this._coreType(argType, this._inferScope);
367
774
  },
368
775
 
369
776
  _inferFromPair(paramType, argType, map) {
370
777
  if (!paramType || !argType) return;
778
+ argType = this._coreOfTypeParam(argType, paramType) ?? argType;
371
779
  if (paramType.kind === "typeParam") {
372
- if (!map.has(paramType.name)) map.set(paramType.name, argType);
780
+ const prev = map.get(paramType.name);
781
+ if (!prev || untypedWins(prev, argType)) map.set(paramType.name, argType);
373
782
  return;
374
783
  }
784
+ if (paramType.kind === "named" && paramType.typeArgs) {
785
+ // `iter.Seq[E]` against `iter.Seq[int]`: unify the type arguments;
786
+ // against any other type, its underlying type.
787
+ if (argType.kind === "named" && argType.name === paramType.name) {
788
+ const args = argType.typeArgs ?? [];
789
+ for (let i = 0; i < paramType.typeArgs.length; i++)
790
+ this._inferFromPair(paramType.typeArgs[i], args[i], map);
791
+ return;
792
+ }
793
+ paramType = paramType.underlying;
794
+ }
795
+ if (argType.kind === "named" && argType.underlying)
796
+ argType = argType.underlying;
375
797
  this._inferFromStructuredPair(paramType, argType, map);
376
798
  },
377
799
 
378
800
  _checkUnionConstraint(typeArg, constraint, node) {
379
801
  for (const term of constraint.terms) {
380
802
  if (term.approx) return;
381
- const termType = this.resolveTypeNode(term.type, this.globals);
803
+ const termType =
804
+ term.resolved ?? this.resolveTypeNode(term.type, this.globals);
382
805
  if (typeStr(typeArg) === typeStr(termType)) return;
383
806
  }
384
807
  this.err(`type ${typeStr(typeArg)} does not satisfy constraint`, node);
@@ -422,11 +845,37 @@ export const resolveMethods = {
422
845
  (!t.underlying || !t.methods)
423
846
  ) {
424
847
  const found = this.types.get(t.name);
425
- if (found) return found;
848
+ // Two imported packages can both declare `Pt`; the bare name only
849
+ // reaches the last one registered, so a declaration that came from
850
+ // an import's own type table stays itself.
851
+ if (found && !(t.underlying && this._ambiguousImported?.has(t)))
852
+ return found;
426
853
  }
854
+ if (t.kind === "named" && t.typeArgs?.length)
855
+ return this._withGenericMethods(t);
427
856
  return t;
428
857
  },
429
858
 
859
+ // A substituted generic instance (e.g. Stack[int] from an inferred call)
860
+ // drops the methods map; re-derive it from the generic declaration.
861
+ _withGenericMethods(t) {
862
+ const decl = this.types.get(t.name);
863
+ const generic = decl?._generic;
864
+ // Struct generics only: codegen has no receiver support for generic
865
+ // slice/map/func named types, so those keep the baseline diagnostics.
866
+ if (this.resolveType(decl?.underlying)?.kind !== "struct") return t;
867
+ if (!generic?.methods || generic.methods.size === 0) return t;
868
+ if (t.methods && generic.methods.size <= t.methods.size) return t;
869
+ const map = new Map();
870
+ for (let i = 0; i < generic.typeParams.length; i++)
871
+ map.set(generic.typeParams[i].name, t.typeArgs[i] ?? ANY);
872
+ const methods = new Map(t.methods);
873
+ for (const [name, mType] of generic.methods)
874
+ if (!methods.has(name))
875
+ methods.set(name, this.substituteType(mType, map));
876
+ return { ...t, methods };
877
+ },
878
+
430
879
  // ── Field type lookup ─────────────────────────────────────────────
431
880
 
432
881
  fieldType(baseType, field, node) {
@@ -446,7 +895,7 @@ export const resolveMethods = {
446
895
  return (
447
896
  baseType.kind === "pointer" &&
448
897
  field !== "value" &&
449
- baseType.base?.kind === "named"
898
+ (baseType.base?.kind === "named" || baseType.base?.kind === "struct")
450
899
  );
451
900
  },
452
901
 
@@ -490,12 +939,54 @@ export const resolveMethods = {
490
939
  return base.fields.get(field);
491
940
  }
492
941
  if (base.methods?.has(field)) return base.methods.get(field);
942
+ const typeName = baseType?.name || base.name;
943
+ if (typeName) {
944
+ const dName = displayTypeName(typeName);
945
+ const shortName = typeName.includes("$$")
946
+ ? typeName.split("$$")[0]
947
+ : typeName.includes(".")
948
+ ? typeName.split(".").pop()
949
+ : typeName;
950
+ for (const key of [
951
+ `${dName}.${field}`,
952
+ `${typeName}.${field}`,
953
+ `${shortName}.${field}`,
954
+ ]) {
955
+ if (EXCLUDED_STDLIB_MEMBERS.has(key)) {
956
+ return this.err(
957
+ `${dName}.${field} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(key)}`,
958
+ node,
959
+ );
960
+ }
961
+ }
962
+ }
493
963
  return this.err(`No field '${field}' on ${typeStr(baseType)}`, node);
494
964
  },
495
965
 
496
966
  _fieldTypeInterface(base, baseType, field, node) {
497
967
  if (base.methods?.has(field)) return base.methods.get(field);
498
968
  if (base.fields?.has(field)) return base.fields.get(field);
969
+ const typeName = baseType?.name || base.name;
970
+ if (typeName) {
971
+ const dName = displayTypeName(typeName);
972
+ const shortName = typeName.includes("$$")
973
+ ? typeName.split("$$")[0]
974
+ : typeName.includes(".")
975
+ ? typeName.split(".").pop()
976
+ : typeName;
977
+ for (const key of [
978
+ `${dName}.${field}`,
979
+ `${typeName}.${field}`,
980
+ `${shortName}.${field}`,
981
+ ]) {
982
+ if (EXCLUDED_STDLIB_MEMBERS.has(key)) {
983
+ return this.err(
984
+ `${dName}.${field} is not available: ${EXCLUDED_STDLIB_MEMBERS.get(key)}`,
985
+ node,
986
+ );
987
+ }
988
+ }
989
+ }
499
990
  if (base._excluded?.has(field))
500
991
  return this.err(`No member '${field}' on ${typeStr(baseType)}`, node);
501
992
  if (base._isOpen) return ANY;
@@ -537,12 +1028,37 @@ export const resolveMethods = {
537
1028
  return ANY;
538
1029
  },
539
1030
 
540
- _fieldTypeNamespace(base, field, node) {
1031
+ // An error for `field` when the namespace cannot offer it to this package:
1032
+ // an unexported name, or a member of a `js` file in a wasm or both package.
1033
+ _namespaceMemberError(base, field, node) {
541
1034
  if (base._gofront && field.length > 0 && field[0] >= "a" && field[0] <= "z")
542
1035
  return this.err(
543
1036
  `cannot refer to unexported name ${base.name}.${field}`,
544
1037
  node,
545
1038
  );
1039
+ const wasm = this.target === "wasm" || this.target === "both";
1040
+ return wasm && base._jsOnly?.has(field)
1041
+ ? this._errJsOnly(`${base.name}.${field}`, node)
1042
+ : null;
1043
+ },
1044
+
1045
+ _fieldTypeNamespace(base, field, node) {
1046
+ const bad = this._namespaceMemberError(base, field, node);
1047
+ if (bad) return bad;
1048
+ // Go-source stdlib member: backends emit the mangled top-level name.
1049
+ const mangled = base._mangle?.get(field);
1050
+ if (mangled) {
1051
+ node._stdName = mangled;
1052
+ // Lazy stdlib bodies: referencing a member checks what it reaches.
1053
+ const failure = base._reach?.(mangled);
1054
+ if (failure)
1055
+ this.err(
1056
+ `internal stdlib error in ${base._path}; please report`,
1057
+ node,
1058
+ failure,
1059
+ );
1060
+ return base.members[field];
1061
+ }
546
1062
  if (field in base.members) {
547
1063
  if (
548
1064
  (this.target === "wasm" || this.target === "both") &&
@@ -561,6 +1077,12 @@ export const resolveMethods = {
561
1077
  }
562
1078
  return base.members[field];
563
1079
  }
1080
+ const excluded = EXCLUDED_STDLIB_MEMBERS.get(`${base.name}.${field}`);
1081
+ if (excluded)
1082
+ return this.err(
1083
+ `${base.name}.${field} is not available: ${excluded}`,
1084
+ node,
1085
+ );
564
1086
  return this.err(`No member '${field}' in namespace ${base.name}`, node);
565
1087
  },
566
1088
 
@@ -575,7 +1097,16 @@ function isExported(name) {
575
1097
  return code >= 65 && code <= 90;
576
1098
  }
577
1099
 
578
- function _processJsonField(f, depth, path, visited, queue, candidates) {
1100
+ function _processJsonField(
1101
+ f,
1102
+ depth,
1103
+ path,
1104
+ visited,
1105
+ queue,
1106
+ candidates,
1107
+ index,
1108
+ pos,
1109
+ ) {
579
1110
  const tagInfo = parseJsonTag(f.tag);
580
1111
  if (tagInfo.ignored) return;
581
1112
 
@@ -602,6 +1133,7 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
602
1133
  depth,
603
1134
  tagged: true,
604
1135
  isEmbedded: true,
1136
+ index: [...index, pos],
605
1137
  path: [...path, { name: embedName, isPtr, typeName: embedName }],
606
1138
  });
607
1139
  }
@@ -612,6 +1144,7 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
612
1144
  queue.push({
613
1145
  struct: embedStruct,
614
1146
  depth: depth + 1,
1147
+ index: [...index, pos],
615
1148
  path: [...path, { name: embedName, isPtr, typeName: embedName }],
616
1149
  visited: nextVisited,
617
1150
  });
@@ -620,9 +1153,10 @@ function _processJsonField(f, depth, path, visited, queue, candidates) {
620
1153
  return;
621
1154
  }
622
1155
 
623
- for (const name of f.names) {
1156
+ for (const [i, name] of f.names.entries()) {
624
1157
  if (!isExported(name)) continue;
625
1158
  candidates.push({
1159
+ index: [...index, pos + i],
626
1160
  jsonKey: tagInfo.name !== null ? tagInfo.name : name,
627
1161
  goName: name,
628
1162
  type: f.type,
@@ -658,10 +1192,18 @@ function _resolveCandidateCollisions(candidates) {
658
1192
  }
659
1193
  }
660
1194
 
661
- winningFields.sort((a, b) => candidates.indexOf(a) - candidates.indexOf(b));
1195
+ // Go orders the fields of a struct by their index sequence, so a promoted
1196
+ // field sits where its embedded struct is declared.
1197
+ winningFields.sort((a, b) => compareIndex(a.index, b.index));
662
1198
  return winningFields;
663
1199
  }
664
1200
 
1201
+ function compareIndex(a, b) {
1202
+ for (let i = 0; i < a.length && i < b.length; i++)
1203
+ if (a[i] !== b[i]) return a[i] - b[i];
1204
+ return a.length - b.length;
1205
+ }
1206
+
665
1207
  export function resolveJsonFields(structType) {
666
1208
  if (!structType) return [];
667
1209
  const underlying =
@@ -670,16 +1212,25 @@ export function resolveJsonFields(structType) {
670
1212
  if (underlying._jsonFields) return underlying._jsonFields;
671
1213
 
672
1214
  const queue = [
673
- { struct: underlying, depth: 0, path: [], visited: new Set([underlying]) },
1215
+ {
1216
+ struct: underlying,
1217
+ depth: 0,
1218
+ path: [],
1219
+ index: [],
1220
+ visited: new Set([underlying]),
1221
+ },
674
1222
  ];
675
1223
  const candidates = [];
676
1224
 
677
1225
  while (queue.length > 0) {
678
- const { struct: curr, depth, path, visited } = queue.shift();
1226
+ const { struct: curr, depth, path, index, visited } = queue.shift();
679
1227
  if (curr?.kind !== "struct") continue;
680
1228
 
1229
+ // Each name of a `A, B int` field is its own field, as in Go.
1230
+ let pos = 0;
681
1231
  for (const f of curr._structFields || []) {
682
- _processJsonField(f, depth, path, visited, queue, candidates);
1232
+ _processJsonField(f, depth, path, visited, queue, candidates, index, pos);
1233
+ pos += f.embedded ? 1 : (f.names?.length ?? 1);
683
1234
  }
684
1235
  }
685
1236