gofront 1.8.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 (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  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 +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  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 +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -1,9 +1,14 @@
1
1
  // TypeChecker expression-checking methods — installed as a mixin on TypeChecker.prototype.
2
2
 
3
- import { EXCLUDED_STDLIB_MEMBERS } from "./stdlib/excluded.js";
3
+ import { exprText, operandKind } from "./operand-text.js";
4
+ import {
5
+ EXCLUDED_STDLIB_MEMBERS,
6
+ EXCLUDED_STDLIB_PACKAGES,
7
+ } from "./stdlib/excluded.js";
4
8
  import { sharedInfo } from "./stdlib/shared.js";
5
9
  import {
6
10
  ANY,
11
+ BASIC_TYPES,
7
12
  BOOL,
8
13
  BROWSER_GLOBALS,
9
14
  COMPLEX64,
@@ -11,9 +16,12 @@ import {
11
16
  compositeLitMaxIndex,
12
17
  displayTypeName,
13
18
  ERROR,
19
+ embeddedFieldName,
14
20
  FLOAT32,
15
21
  FLOAT64,
22
+ FMT_PRINT_FUNCS,
16
23
  INT,
24
+ is64BitInt,
17
25
  isAny,
18
26
  isArray,
19
27
  isBool,
@@ -21,6 +29,7 @@ import {
21
29
  isComplex64,
22
30
  isComplexOrNumeric,
23
31
  isInteger,
32
+ isNil,
24
33
  isNumeric,
25
34
  isString,
26
35
  NIL,
@@ -30,6 +39,7 @@ import {
30
39
  STRING,
31
40
  TAINTED_ANY,
32
41
  typeStr,
42
+ UINT8,
33
43
  UNTYPED_BOOL,
34
44
  UNTYPED_COMPLEX,
35
45
  UNTYPED_FLOAT,
@@ -38,6 +48,15 @@ import {
38
48
  VOID,
39
49
  } from "./types.js";
40
50
 
51
+ const NILABLE_KINDS = new Set([
52
+ "slice",
53
+ "map",
54
+ "pointer",
55
+ "func",
56
+ "interface",
57
+ "chan",
58
+ ]);
59
+
41
60
  /** @typedef {import('./index.js').TypeChecker} TypeChecker */
42
61
 
43
62
  // min/max: the type of the first typed operand; with only untyped constants,
@@ -68,15 +87,18 @@ const BUILTIN_CHECK = {
68
87
  }
69
88
  const sb = s.resolveType(st?.kind === "named" ? st.underlying : st);
70
89
  if (sb?.kind === "slice") {
90
+ const is64 = is64BitInt(sb.elem);
71
91
  for (let i = 1; i < e.args.length; i++) {
72
92
  if (e.args[i]._spread)
73
93
  s._rejectSeqAsSlice(at[i], e.args[i], "a spread argument");
74
- if (!e.args[i]._spread && at[i])
94
+ if (!e.args[i]._spread && at[i]) {
95
+ if (is64) e.args[i]._is64Bit = true;
75
96
  s._markIfaceBox(
76
97
  s.resolveType(sb.elem),
77
98
  s.resolveType(at[i]),
78
99
  e.args[i],
79
100
  );
101
+ }
80
102
  }
81
103
  }
82
104
  return st;
@@ -91,7 +113,11 @@ const BUILTIN_CHECK = {
91
113
  if (e.args.length < 1) return ANY;
92
114
  const ta = e.args[0];
93
115
  const tn = ta.kind === "TypeExpr" ? ta.type : ta;
94
- return s.resolveTypeNode(tn, sc) ?? ANY;
116
+ const made = s.resolveTypeNode(tn, sc) ?? ANY;
117
+ // `make(S, n)` with S a type parameter makes its core type.
118
+ const core = made.kind === "typeParam" ? s._coreType(made, sc) : null;
119
+ if (core) ta._coreType = core;
120
+ return made;
95
121
  },
96
122
  new: (s, _n, e, _at, sc) => {
97
123
  if (e.args.length < 1) return ANY;
@@ -101,7 +127,11 @@ const BUILTIN_CHECK = {
101
127
  },
102
128
  print: (s, n, e, at) => s._checkPrintArgs(n, e, at),
103
129
  println: (s, n, e, at) => s._checkPrintArgs(n, e, at),
104
- panic: () => VOID,
130
+ panic: (s, _n, e, at) => {
131
+ // The operand converts to `any`, so a defined type keeps its identity.
132
+ if (e.args[0] && at?.[0]) s._markIfaceBox(ANY, at[0], e.args[0]);
133
+ return VOID;
134
+ },
105
135
  recover: () => ANY,
106
136
  error: () => ERROR,
107
137
  min: (_s, _n, _e, at) => minMaxType(at),
@@ -157,8 +187,35 @@ export const expressionCheckMethods = {
157
187
  return UNTYPED_COMPLEX;
158
188
  case "Ident": {
159
189
  const t = scope.lookup(expr.name);
160
- if (!t) return this.err(`Undefined: '${expr.name}'`, expr);
161
- if (this.types.has(expr.name) || t.kind === "typeParam")
190
+ // `(*int)(nil)`: a predeclared type named where a type may stand.
191
+ if (!t && expr._typeRefOk && Object.hasOwn(BASIC_TYPES, expr.name)) {
192
+ expr._isTypeRef = true;
193
+ return BASIC_TYPES[expr.name];
194
+ }
195
+ if (!t) {
196
+ // The import of an excluded package already reported; its
197
+ // uses are not a second root cause.
198
+ const imported = this._fileImports
199
+ ?.get(this._currentFile)
200
+ ?.get(expr.name);
201
+ if (imported && EXCLUDED_STDLIB_PACKAGES.has(imported))
202
+ return TAINTED_ANY;
203
+ if (this._unavailableImports?.has(expr.name)) return TAINTED_ANY;
204
+ // `gom` needs no import in a js package; wasm packages get
205
+ // the same answer as an `import "gom"`.
206
+ if (
207
+ expr.name === "gom" &&
208
+ (this.target === "wasm" || this.target === "both")
209
+ ) {
210
+ this.recordBlocker("gom usage", "gom");
211
+ return this.err(
212
+ "package 'gom' is not available in wasm packages",
213
+ expr,
214
+ );
215
+ }
216
+ return this.err(`Undefined: '${expr.name}'`, expr);
217
+ }
218
+ if (this._namesType(expr.name, t, scope) || t.kind === "typeParam")
162
219
  expr._isTypeRef = true;
163
220
  // A Go declaration that shadows a browser global: JS output renames
164
221
  // it so generated code keeps reaching the real global. Package
@@ -188,14 +245,6 @@ export const expressionCheckMethods = {
188
245
  `(package '${this.pkgName}' is //gofront:target ${this.target} — browser globals are only available in js packages)`,
189
246
  );
190
247
  }
191
- if (
192
- (this.target === "wasm" || this.target === "both") &&
193
- expr.name === "gom" &&
194
- scope.lookup(expr.name) === this.globals.lookup(expr.name)
195
- ) {
196
- this.recordBlocker("gom usage", "gom");
197
- this.err("package 'gom' is not available in wasm packages", expr);
198
- }
199
248
  return t;
200
249
  }
201
250
  case "BinaryExpr": {
@@ -205,7 +254,17 @@ export const expressionCheckMethods = {
205
254
  if (isAny(lt) || isAny(rt))
206
255
  return this.binaryResultType(expr.op, ANY, ANY, expr);
207
256
  this._checkConstOperands(expr.op, lt, rt, expr);
208
- return this.binaryResultType(expr.op, lt, rt, expr);
257
+ this._markTypeParamMapNil(expr, lt, rt, scope);
258
+ const res = this.binaryResultType(expr.op, lt, rt, expr);
259
+ if (expr.op !== "<<" && expr.op !== ">>") {
260
+ this._retypeUntypedShift(lt, rt, expr.right);
261
+ this._retypeUntypedShift(rt, lt, expr.left);
262
+ }
263
+ if (is64BitInt(lt) && rt?.kind === "untyped" && expr.right)
264
+ expr.right._is64Bit = true;
265
+ if (is64BitInt(rt) && lt?.kind === "untyped" && expr.left)
266
+ expr.left._is64Bit = true;
267
+ return res;
209
268
  }
210
269
  case "AwaitExpr": {
211
270
  if (this.target === "wasm" || this.target === "both") {
@@ -281,23 +340,32 @@ export const expressionCheckMethods = {
281
340
  return terms !== null && terms.length > 0 && terms.every(pred);
282
341
  },
283
342
 
284
- _constraintTerms(c, seen) {
343
+ // `scope` resolves the type parameters a term mentions (`~[]E`); it
344
+ // defaults to the package scope.
345
+ _constraintTerms(c, seen, scope = this.globals) {
285
346
  if (!c || seen.has(c)) return null;
286
347
  seen.add(c);
287
348
  // Receiver type parameters keep their constraint as an AST node.
288
- if (c.kind === "TypeName" || c.kind === "InterfaceType")
289
- return this._constraintTerms(this.resolveTypeNode(c, this.globals), seen);
290
- if (c.kind === "named") return this._constraintTerms(c.underlying, seen);
291
- if (c.kind === "interface") return this._interfaceTerms(c, seen);
349
+ if (
350
+ c.kind === "TypeName" ||
351
+ c.kind === "InterfaceType" ||
352
+ c.kind === "GenericTypeName" ||
353
+ c.kind === "InstantiationExpr" ||
354
+ c.kind === "IndexExpr"
355
+ )
356
+ return this._constraintTerms(this.resolveTypeNode(c, scope), seen, scope);
357
+ if (c.kind === "named")
358
+ return this._constraintTerms(c.underlying, seen, scope);
359
+ if (c.kind === "interface") return this._interfaceTerms(c, seen, scope);
292
360
  if (c.kind === "typeParam")
293
- return this._constraintTerms(c.constraint, seen);
361
+ return this._constraintTerms(c.constraint, seen, scope);
294
362
  if (c.kind !== "UnionConstraint") return isAny(c) ? null : [c];
295
363
  const out = [];
296
364
  for (const term of c.terms) {
297
- const t = this.resolveTypeNode(term.type, this.globals);
365
+ const t = term.resolved ?? this.resolveTypeNode(term.type, scope);
298
366
  const inner =
299
367
  t?.kind === "named" && t.underlying?.kind === "interface"
300
- ? this._constraintTerms(t, seen)
368
+ ? this._constraintTerms(t, seen, scope)
301
369
  : [t];
302
370
  if (inner === null) return null;
303
371
  out.push(...inner);
@@ -307,10 +375,10 @@ export const expressionCheckMethods = {
307
375
 
308
376
  // An interface's type set is the intersection of its own union and the
309
377
  // type sets of the interfaces it embeds; null when it has none.
310
- _interfaceTerms(iface, seen) {
378
+ _interfaceTerms(iface, seen, scope) {
311
379
  const sets = [iface.unionConstraint, ...(iface._typeSetEmbeds ?? [])]
312
380
  .filter(Boolean)
313
- .map((s) => this._constraintTerms(s, new Set(seen)));
381
+ .map((s) => this._constraintTerms(s, new Set(seen), scope));
314
382
  if (sets.length === 0 || sets.includes(null)) return null;
315
383
  const [first, ...rest] = sets;
316
384
  return first.filter((t) =>
@@ -608,6 +676,11 @@ export const expressionCheckMethods = {
608
676
  const base = this.resolveType(
609
677
  recvT?.kind === "named" ? recvT.underlying : recvT,
610
678
  );
679
+ if (base?.kind === "interface" && base.methods?.has(expr.field)) {
680
+ // `i.M` binds the interface value; the call dispatches when it runs.
681
+ expr._isMethodValue = true;
682
+ return;
683
+ }
611
684
  if (!recvT || !this._isStructOrNamedMethod(recvT, base, expr.field)) return;
612
685
  expr._isMethodValue = true;
613
686
  if (baseType.kind === "pointer")
@@ -615,6 +688,16 @@ export const expressionCheckMethods = {
615
688
  else this._markPtrRecvAddress(expr, base, ft, scope);
616
689
  },
617
690
 
691
+ // Whether the identifier `name`, which resolved to `t`, names a type. A
692
+ // local variable or parameter that shares its name with a type of the
693
+ // build (the type table spans packages) is a value.
694
+ _namesType(name, t, scope) {
695
+ if (!this.types.has(name)) return false;
696
+ return (
697
+ scope.lookupScope(name) === this.globals || this.types.get(name) === t
698
+ );
699
+ },
700
+
618
701
  _checkSelectorExpr(expr, scope) {
619
702
  expr.expr._typeRefOk = true;
620
703
  const baseType = this.checkExpr(expr.expr, scope);
@@ -688,6 +771,57 @@ export const expressionCheckMethods = {
688
771
  return u;
689
772
  },
690
773
 
774
+ // Core type of type parameter `tp`: the one underlying type shared by every
775
+ // type in its constraint (`~[]E`, `~map[K]V`), or null when there is none.
776
+ _coreType(tp, scope) {
777
+ if (tp?.kind !== "typeParam") return null;
778
+ const terms = this._constraintTerms(tp.constraint, new Set(), scope);
779
+ if (!terms || terms.length === 0) return null;
780
+ const unders = terms.map((t) => this._deepUnderlying(this.resolveType(t)));
781
+ const key = typeStr(unders[0]);
782
+ if (unders.some((u) => !u || typeStr(u) !== key)) return null;
783
+ return unders[0];
784
+ },
785
+
786
+ // `m == nil` on a type parameter whose core type is a map: the backends
787
+ // compare it as a map, so record the core type on the operand.
788
+ _markTypeParamMapNil(expr, lt, rt, scope) {
789
+ if (expr.op !== "==" && expr.op !== "!=") return;
790
+ const [operand, other] =
791
+ rt === NIL
792
+ ? [expr.left, lt]
793
+ : lt === NIL
794
+ ? [expr.right, rt]
795
+ : [null, null];
796
+ if (!operand || other?.kind !== "typeParam") return;
797
+ const core = this._coreType(other, scope);
798
+ if (core?.kind === "map") operand._coreType = core;
799
+ },
800
+
801
+ // Underlying type of `t` for indexing, slicing, ranging and the like; a
802
+ // type parameter stands for its core type.
803
+ _coreUnderlying(t, scope) {
804
+ if (t?.kind === "typeParam") return this._coreType(t, scope) ?? t;
805
+ return this._deepUnderlying(t);
806
+ },
807
+
808
+ // One step of underlying type, or the core type for a type parameter.
809
+ _underlyingOrCore(t, scope) {
810
+ if (t?.kind === "typeParam") return this._coreType(t, scope) ?? t;
811
+ return t?.kind === "named" ? t.underlying : t;
812
+ },
813
+
814
+ _markCompositeKey(key, kb, node, srcType) {
815
+ if (!node || !srcType || isAny(key) || kb?.kind === "interface")
816
+ return false;
817
+ // Arrays, anonymous structs and pointers are not JS property names: the
818
+ // backend encodes them by value (`sk`) or by identity (`pk`).
819
+ if (kb?.kind === "pointer") node._mapKeyEnc = "pk";
820
+ else if (kb?.kind === "array" || (key?.kind === "struct" && kb === key))
821
+ node._mapKeyEnc = "sk";
822
+ return true;
823
+ },
824
+
691
825
  // A key of an interface-keyed map (`map[any]T`): the backend encodes it
692
826
  // with its dynamic type, so `L("1")`, `N(1)`, `"1"` and `1` stay distinct.
693
827
  // A named basic value is boxed first, exactly as in any interface
@@ -705,8 +839,10 @@ export const expressionCheckMethods = {
705
839
  }
706
840
  return;
707
841
  }
842
+ if (this._markCompositeKey(key, kb, node, srcType)) return;
708
843
  if (!node || !srcType || (!isAny(key) && kb?.kind !== "interface")) return;
709
844
  node._ifaceKey = true;
845
+ node._ifaceKeyType = src;
710
846
  this._markIfaceBox(key, this.resolveType(srcType), node);
711
847
  if (node._ifaceBox === "named" || node._ifaceBox === "named_ptr") return;
712
848
  node._ifaceBox = undefined;
@@ -729,13 +865,14 @@ export const expressionCheckMethods = {
729
865
  const indexType = this.checkExpr(expr.index, scope);
730
866
  if (bt?._tainted) return TAINTED_ANY;
731
867
  if (isAny(bt)) return ANY;
732
- const btu = this._deepUnderlying(bt);
868
+ const btu = this._coreUnderlying(bt, scope);
733
869
  if (btu.kind === "slice" || btu.kind === "array") {
734
870
  this._checkArrayBoundsExpr(btu, bt, expr, scope);
735
871
  return btu.elem;
736
872
  }
737
873
  if (btu.kind === "map") {
738
874
  expr._mapValueType = btu.value;
875
+ this.assertAssignable(btu.key, indexType, expr.index);
739
876
  this._markIfaceKey(btu, expr.index, indexType);
740
877
  return btu.value;
741
878
  }
@@ -746,7 +883,7 @@ export const expressionCheckMethods = {
746
883
  this.err(`invalid index ${idx} (index must not be negative)`, expr);
747
884
  return btu.elem;
748
885
  }
749
- if (isString(bt)) return INT;
886
+ if (isString(bt)) return UINT8;
750
887
  return this.err(`Cannot index type ${typeStr(bt)}`, expr);
751
888
  },
752
889
 
@@ -756,6 +893,12 @@ export const expressionCheckMethods = {
756
893
  _genericFuncValueAsInstantiation(expr) {
757
894
  const toTypeNode = (n) => {
758
895
  if (n?.kind === "TypeExpr") return n.type;
896
+ // `Id[*int]`: the parser sees a dereference; on a generic function
897
+ // it is a pointer type argument.
898
+ if (n?.kind === "UnaryExpr" && n.op === "*") {
899
+ const base = toTypeNode(n.operand);
900
+ return base && { kind: "PointerType", base, _line: n._line };
901
+ }
759
902
  const parts = [];
760
903
  let cur = n;
761
904
  while (cur?.kind === "SelectorExpr") {
@@ -823,7 +966,7 @@ export const expressionCheckMethods = {
823
966
  if (expr.max) this.checkExpr(expr.max, scope);
824
967
  if (bt?._tainted) return TAINTED_ANY;
825
968
  if (isAny(bt)) return ANY;
826
- const btu = this._deepUnderlying(bt);
969
+ const btu = this._coreUnderlying(bt, scope);
827
970
  if (btu.kind === "slice" || btu.kind === "array")
828
971
  return { kind: "slice", elem: btu.elem };
829
972
  if (isString(bt)) return STRING;
@@ -862,9 +1005,12 @@ export const expressionCheckMethods = {
862
1005
  const ret = expr.returnType
863
1006
  ? this.resolveTypeNode(expr.returnType, scope)
864
1007
  : VOID;
1008
+ const bodyRet = this._injectNamedReturns(expr.returnType, inner)
1009
+ ? { ...ret, _hasNamedReturns: true }
1010
+ : ret;
865
1011
  const savedDefer = this._deferCount;
866
1012
  this._deferCount = 0;
867
- this.checkBlock(expr.body, inner, ret);
1013
+ this.checkBlock(expr.body, inner, bodyRet);
868
1014
  this._reportUnused(inner, expr);
869
1015
  if (this._deferCount > 0) expr.body._hasDefer = true;
870
1016
  this._deferCount = savedDefer;
@@ -923,6 +1069,7 @@ export const expressionCheckMethods = {
923
1069
  const targetType = this.resolveTypeNode(expr.targetType, scope);
924
1070
  // `any(x)` / `error(x)`: box x as an implicit conversion would.
925
1071
  this._markIfaceBox(targetType, srcType, expr.expr);
1072
+ this._rejectNilConversion(srcType, targetType, expr);
926
1073
  const srcResolved =
927
1074
  srcType?.kind === "named" ? srcType.underlying : srcType;
928
1075
  const tgtResolved =
@@ -964,12 +1111,42 @@ export const expressionCheckMethods = {
964
1111
  return targetType;
965
1112
  },
966
1113
 
1114
+ // `int(nil)`: only a type with a nil value converts from `nil`.
1115
+ _rejectNilConversion(srcType, targetType, expr) {
1116
+ if (!isNil(srcType) || !targetType || isAny(targetType)) return;
1117
+ if (targetType.kind === "typeParam") return;
1118
+ const kind = this._deepUnderlying(targetType)?.kind;
1119
+ if (!kind || NILABLE_KINDS.has(kind)) return;
1120
+ this.err(`cannot convert nil to ${typeStr(targetType)}`, expr);
1121
+ },
1122
+
1123
+ // Pointers convert when their base types share an underlying type.
1124
+ _pointerConversionOk(srcPtr, tgtPtr, srcType, targetType, expr) {
1125
+ const sBase = this._deepUnderlying(srcPtr.base);
1126
+ const tBase = this._deepUnderlying(tgtPtr.base);
1127
+ if (typeStr(sBase) === typeStr(tBase)) return true;
1128
+ if (!this._sameStructFields(sBase, tBase)) return false;
1129
+ // WebAssembly struct types are nominal: the two pointers could not
1130
+ // share one object.
1131
+ if (this.target === "wasm" || this.target === "both") {
1132
+ this.recordBlocker("pointer conversion between struct types");
1133
+ this.err(
1134
+ `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} in a wasm package: pointers to distinct struct types cannot share storage; keep this code in a js package`,
1135
+ expr,
1136
+ );
1137
+ }
1138
+ return true;
1139
+ },
1140
+
967
1141
  _checkConversionValidity(srcType, targetType, expr) {
968
1142
  if (!srcType || !targetType || isAny(srcType) || isAny(targetType)) return;
969
1143
  const srcResolved = srcType.kind === "named" ? srcType.underlying : srcType;
970
1144
  const tgtResolved =
971
1145
  targetType.kind === "named" ? targetType.underlying : targetType;
972
1146
 
1147
+ // `nil` converts to any type that has a nil value.
1148
+ if (isNil(srcType) && NILABLE_KINDS.has(tgtResolved?.kind)) return;
1149
+
973
1150
  if (isNumeric(targetType) && isComplex(srcType)) {
974
1151
  this.err(
975
1152
  `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)} (use real() or imag())`,
@@ -1032,17 +1209,18 @@ export const expressionCheckMethods = {
1032
1209
  srcResolved.elem?.name === "int32")))
1033
1210
  )
1034
1211
  return;
1035
- if (srcResolved?.kind === "pointer" && tgtResolved?.kind === "pointer") {
1036
- const sBase =
1037
- srcResolved.base?.kind === "named"
1038
- ? srcResolved.base.underlying
1039
- : srcResolved.base;
1040
- const tBase =
1041
- tgtResolved.base?.kind === "named"
1042
- ? tgtResolved.base.underlying
1043
- : tgtResolved.base;
1044
- if (typeStr(sBase) === typeStr(tBase)) return;
1045
- }
1212
+ if (
1213
+ srcResolved?.kind === "pointer" &&
1214
+ tgtResolved?.kind === "pointer" &&
1215
+ this._pointerConversionOk(
1216
+ srcResolved,
1217
+ tgtResolved,
1218
+ srcType,
1219
+ targetType,
1220
+ expr,
1221
+ )
1222
+ )
1223
+ return;
1046
1224
  this.err(
1047
1225
  `cannot convert ${typeStr(srcType)} to ${typeStr(targetType)}`,
1048
1226
  expr,
@@ -1052,6 +1230,7 @@ export const expressionCheckMethods = {
1052
1230
  _checkTypeAssertExpr(expr, scope) {
1053
1231
  const srcType = this.checkExpr(expr.expr, scope);
1054
1232
  const targetType = this.resolveTypeNode(expr.type, scope);
1233
+ this._markDynamicType(expr.type, targetType);
1055
1234
 
1056
1235
  // In Go, the source of a type assertion must be an interface type.
1057
1236
  // We allow `any` (which is GoFront's basic any) and interface types.
@@ -1091,8 +1270,10 @@ export const expressionCheckMethods = {
1091
1270
  "expr",
1092
1271
  this.checkExpr(expr.expr, scope),
1093
1272
  );
1094
- const resolved =
1095
- collType?.kind === "named" ? collType.underlying : collType;
1273
+ const resolved = this._underlyingOrCore(collType, scope);
1274
+ // Backends range over the core type of a type parameter.
1275
+ if (collType?.kind === "typeParam" && resolved !== collType)
1276
+ expr.expr._coreType = resolved;
1096
1277
  return this._rangeCollTypeTuple(resolved, collType);
1097
1278
  },
1098
1279
 
@@ -1119,7 +1300,10 @@ export const expressionCheckMethods = {
1119
1300
  ) {
1120
1301
  const tp = baseType.typeParams[i];
1121
1302
  const constraint = tp.constraint
1122
- ? this.resolveTypeNode(tp.constraint, scope)
1303
+ ? this.resolveTypeNode(
1304
+ tp.constraint,
1305
+ this._typeArgScope(baseType.typeParams, typeArgs, scope),
1306
+ )
1123
1307
  : null;
1124
1308
  if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
1125
1309
  }
@@ -1298,12 +1482,12 @@ export const expressionCheckMethods = {
1298
1482
  if (!resolved) return null;
1299
1483
  fnType = resolved;
1300
1484
  }
1301
- return this._completePartialInstantiation(expr, argTypes) ?? fnType;
1485
+ return this._completePartialInstantiation(expr, argTypes, scope) ?? fnType;
1302
1486
  },
1303
1487
 
1304
1488
  // `F[int](x)` with fewer explicit type arguments than type parameters:
1305
1489
  // infer the rest from the call arguments and record the full list.
1306
- _completePartialInstantiation(expr, argTypes) {
1490
+ _completePartialInstantiation(expr, argTypes, scope) {
1307
1491
  const inst = expr.func;
1308
1492
  const generic = inst?.kind === "InstantiationExpr" && inst._genericFnType;
1309
1493
  if (!generic || inst._typeArgs.length >= generic.typeParams.length)
@@ -1312,6 +1496,7 @@ export const expressionCheckMethods = {
1312
1496
  generic,
1313
1497
  this._inferArgTypes(expr, argTypes),
1314
1498
  inst._typeArgs,
1499
+ scope,
1315
1500
  );
1316
1501
  if (!typeArgs) {
1317
1502
  this.err(
@@ -1327,7 +1512,7 @@ export const expressionCheckMethods = {
1327
1512
 
1328
1513
  // `T(x)` with T a type parameter: only a numeric x converts (the constraint
1329
1514
  // decides the target at instantiation).
1330
- _checkTypeParamConversion(tp, argTypes, expr) {
1515
+ _checkTypeParamConversion(tp, argTypes, expr, scope) {
1331
1516
  expr._isTypeConversion = true;
1332
1517
  expr._conversionTargetType = tp;
1333
1518
  if (expr.args.length !== 1) {
@@ -1336,7 +1521,23 @@ export const expressionCheckMethods = {
1336
1521
  expr,
1337
1522
  );
1338
1523
  }
1339
- if (isNumeric(argTypes[0]) && !this._constraintHasTypeSet(tp.constraint))
1524
+ // A constraint with a core type (`~[]E`) converts as that type does.
1525
+ const core = this._coreType(tp, scope);
1526
+ if (core) {
1527
+ const src = argTypes[0];
1528
+ const nilOk = ["slice", "map", "pointer", "func", "chan", "interface"];
1529
+ if (src === NIL || (src?.kind === "basic" && src.name === "nil")) {
1530
+ if (!nilOk.includes(core.kind))
1531
+ this.err(`cannot convert nil to ${typeStr(tp)}`, expr);
1532
+ } else if (src?.kind !== "typeParam") {
1533
+ this._checkConversionValidity(src, core, expr);
1534
+ }
1535
+ return tp;
1536
+ }
1537
+ if (
1538
+ isNumeric(argTypes[0]) &&
1539
+ !this._constraintHasTypeSet(tp.constraint, scope)
1540
+ )
1340
1541
  return this.err(
1341
1542
  `cannot convert ${typeStr(argTypes[0])} to type ${typeStr(tp)}: ${typeStr(tp)} is not constrained to a set of convertible types`,
1342
1543
  expr,
@@ -1356,18 +1557,38 @@ export const expressionCheckMethods = {
1356
1557
  // A conversion to a type parameter needs a constraint that lists types
1357
1558
  // (`~int | ~float64`, or an interface embedding such a union); `any`,
1358
1559
  // `comparable` and method-only constraints allow every type argument.
1359
- _constraintHasTypeSet(constraint) {
1560
+ _constraintHasTypeSet(constraint, scope = this.globals) {
1360
1561
  if (!constraint) return true;
1361
1562
  if (constraint.kind === "UnionConstraint") return true;
1362
- const c = this.resolveType(constraint);
1563
+ // Receiver type parameters keep their constraint as an AST node.
1564
+ const c = this.resolveType(
1565
+ constraint.kind === "TypeName" || constraint.kind === "InterfaceType"
1566
+ ? this.resolveTypeNode(constraint, scope)
1567
+ : constraint,
1568
+ );
1363
1569
  const base = c?.kind === "named" ? this.resolveType(c.underlying) : c;
1364
1570
  if (base?.kind !== "interface") return false;
1365
1571
  if (base.unionConstraint) return true;
1366
1572
  return (base._typeSetEmbeds ?? []).some((e) =>
1367
- this._constraintHasTypeSet(e),
1573
+ this._constraintHasTypeSet(e, scope),
1368
1574
  );
1369
1575
  },
1370
1576
 
1577
+ // A direct call of a `//gofront:native` function is marked `_native` so a
1578
+ // backend may substitute host code. A function used as a value (`f :=
1579
+ // str.Index; f(a, b)`) is not a direct reference and calls the Go body.
1580
+ _markNativeCall(expr, fnType, scope) {
1581
+ const callee =
1582
+ expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
1583
+ const native = fnType?.native ?? fnType?.underlying?.native ?? null;
1584
+ const direct =
1585
+ callee.kind === "Ident"
1586
+ ? scope.lookupScope(callee.name) === this.globals &&
1587
+ Boolean(this._funcDeclNames?.has(callee.name))
1588
+ : callee.kind === "SelectorExpr" && Boolean(callee._stdName);
1589
+ expr._native = native && direct ? native : null;
1590
+ },
1591
+
1371
1592
  checkCall(expr, scope) {
1372
1593
  expr.func._typeRefOk = true;
1373
1594
  let fnType = this.checkExpr(expr.func, scope);
@@ -1391,6 +1612,7 @@ export const expressionCheckMethods = {
1391
1612
  return this.checkBuiltin(fnType.name, expr, argTypes, scope);
1392
1613
  }
1393
1614
 
1615
+ this._markNativeCall(expr, fnType, scope);
1394
1616
  const argTypes = this._resolveCallArgTypes(expr, scope);
1395
1617
 
1396
1618
  if (fnType?._tainted) return TAINTED_ANY;
@@ -1401,7 +1623,12 @@ export const expressionCheckMethods = {
1401
1623
 
1402
1624
  // Named type used as a constructor/conversion: Greeter(fn), NodeFunc(fn)
1403
1625
  // The callee is a type name, not a callable variable.
1404
- if (fnType.kind === "named" && expr.func._isTypeRef) {
1626
+ if (
1627
+ (fnType.kind === "named" ||
1628
+ fnType.kind === "pointer" ||
1629
+ fnType.kind === "basic") &&
1630
+ expr.func._isTypeRef
1631
+ ) {
1405
1632
  expr._isTypeConversion = true;
1406
1633
  expr._conversionTargetType = fnType;
1407
1634
  if (expr.args.length !== 1) {
@@ -1416,7 +1643,7 @@ export const expressionCheckMethods = {
1416
1643
  }
1417
1644
 
1418
1645
  if (fnType.kind === "typeParam" && expr.func._isTypeRef) {
1419
- return this._checkTypeParamConversion(fnType, argTypes, expr);
1646
+ return this._checkTypeParamConversion(fnType, argTypes, expr, scope);
1420
1647
  }
1421
1648
 
1422
1649
  fnType = this._resolveCallFnType(fnType, argTypes, expr, scope);
@@ -1427,13 +1654,6 @@ export const expressionCheckMethods = {
1427
1654
 
1428
1655
  this._checkCallArgs(fnType, argTypes, expr, scope);
1429
1656
 
1430
- // Pseudo-generic stdlib helpers (maps.Keys, slices.Clone, ...) declare
1431
- // how their result derives from the first argument.
1432
- if (fnType._derivedReturn) {
1433
- const derived = this._derivedReturnType(fnType._derivedReturn, argTypes);
1434
- if (derived) return derived;
1435
- }
1436
-
1437
1657
  const ret = fnType.returns;
1438
1658
  if (!ret || ret.length === 0) return VOID;
1439
1659
  if (ret.length === 1) return ret[0];
@@ -1483,19 +1703,6 @@ export const expressionCheckMethods = {
1483
1703
  );
1484
1704
  },
1485
1705
 
1486
- _derivedReturnType(mode, argTypes) {
1487
- const arg = argTypes[0];
1488
- const under = arg?.kind === "named" ? arg.underlying : arg;
1489
- switch (mode) {
1490
- case "arg0":
1491
- return under ? arg : null;
1492
- case "elemOfArg0":
1493
- return under?.elem ?? null;
1494
- default:
1495
- return null;
1496
- }
1497
- },
1498
-
1499
1706
  // Argument types for inference: a spread argument `xs...` stands for its
1500
1707
  // elements, which is what the variadic parameter type describes.
1501
1708
  _inferArgTypes(expr, argTypes) {
@@ -1506,10 +1713,23 @@ export const expressionCheckMethods = {
1506
1713
  });
1507
1714
  },
1508
1715
 
1716
+ // Scope where each type parameter name stands for its type argument, so a
1717
+ // constraint that mentions another parameter (`S ~[]E`) resolves.
1718
+ _typeArgScope(typeParams, typeArgs, scope) {
1719
+ const out = new Scope(scope);
1720
+ typeParams.forEach((tp, i) => {
1721
+ if (typeArgs[i])
1722
+ out.define(tp.name, { kind: "aliasArg", type: typeArgs[i] });
1723
+ });
1724
+ return out;
1725
+ },
1726
+
1509
1727
  _resolveGenericFnType(fnType, argTypes, expr, scope) {
1510
1728
  const typeArgs = this.inferTypeArgs(
1511
1729
  fnType,
1512
1730
  this._inferArgTypes(expr, argTypes),
1731
+ [],
1732
+ scope,
1513
1733
  );
1514
1734
  if (!typeArgs) {
1515
1735
  this.err(
@@ -1522,7 +1742,10 @@ export const expressionCheckMethods = {
1522
1742
  for (let i = 0; i < fnType.typeParams.length && i < typeArgs.length; i++) {
1523
1743
  const tp = fnType.typeParams[i];
1524
1744
  const constraint = tp.constraint
1525
- ? this.resolveTypeNode(tp.constraint, scope)
1745
+ ? this.resolveTypeNode(
1746
+ tp.constraint,
1747
+ this._typeArgScope(fnType.typeParams, typeArgs, scope),
1748
+ )
1526
1749
  : null;
1527
1750
  if (constraint) this.checkConstraint(typeArgs[i], constraint, expr);
1528
1751
  }
@@ -1562,6 +1785,9 @@ export const expressionCheckMethods = {
1562
1785
  !ns._gofront &&
1563
1786
  !ns._mangle?.has(expr.func.field);
1564
1787
  if (emitterBacked) return;
1788
+ // The generated wrapper of a Print-family call takes the results as
1789
+ // separate parameters (src/lower/fmt-formatters.js).
1790
+ if (ns?.name === "fmt" && FMT_PRINT_FUNCS.has(expr.func.field)) return;
1565
1791
  this.err(
1566
1792
  "passing multiple return values to a variadic function is not supported in wasm packages; assign them to variables first",
1567
1793
  expr,
@@ -1654,16 +1880,31 @@ export const expressionCheckMethods = {
1654
1880
 
1655
1881
  _checkBuiltinLen(name, expr, argTypes) {
1656
1882
  this._rejectSeqAsSlice(argTypes[0], expr.args?.[0], `${name}()`);
1883
+ const operand = expr.args?.[0];
1657
1884
  if (expr.args?.length === 1)
1658
1885
  argTypes[0] = this._derefArrayPtr(expr.args, 0, argTypes[0]);
1659
- if (name === "len") {
1660
- const size = this._constLenFromType(argTypes[0]);
1661
- if (size != null) expr._constLen = size;
1662
- } else if (sharedInfo(argTypes[0])) {
1663
- this.err(
1664
- `cannot use cap() on ${typeStr(argTypes[0])}: shared buffers have a fixed size; use len()`,
1665
- expr,
1666
- );
1886
+ // `len` and `cap` of an array (or pointer to one) are its length.
1887
+ const size = this._constLenFromType(argTypes[0]);
1888
+ if (size != null) {
1889
+ expr._constLen = size;
1890
+ // The operand is not dereferenced: a nil `*[N]T` has length N.
1891
+ if (expr.args.length === 1) expr.args[0] = operand;
1892
+ }
1893
+ if (name === "cap") {
1894
+ if (
1895
+ (argTypes[0]?.underlying ?? argTypes[0])?.kind === "map" ||
1896
+ isString(argTypes[0])
1897
+ ) {
1898
+ this.err(
1899
+ `invalid argument: ${exprText(expr.args?.[0])} (${operandKind(expr.args?.[0], typeStr(argTypes[0]), false)}) for cap`,
1900
+ expr,
1901
+ );
1902
+ } else if (sharedInfo(argTypes[0])) {
1903
+ this.err(
1904
+ `cannot use cap() on ${typeStr(argTypes[0])}: shared buffers have a fixed size; use len()`,
1905
+ expr,
1906
+ );
1907
+ }
1667
1908
  }
1668
1909
  return INT;
1669
1910
  },
@@ -1778,17 +2019,7 @@ export const expressionCheckMethods = {
1778
2019
  _checkStructPositionalElems(expr, scope, t, base) {
1779
2020
  const fieldNames = base._structFields
1780
2021
  ? base._structFields.flatMap((f) =>
1781
- f.embedded
1782
- ? [
1783
- (
1784
- (f.type?.kind === "pointer"
1785
- ? f.type.base?.name
1786
- : f.type?.name) ?? ""
1787
- )
1788
- .split(".")
1789
- .pop(),
1790
- ]
1791
- : f.names,
2022
+ f.embedded ? [embeddedFieldName(f)] : f.names,
1792
2023
  )
1793
2024
  : [...(base.fields?.keys() ?? [])];
1794
2025
  for (let i = 0; i < expr.elems.length; i++) {
@@ -1810,12 +2041,9 @@ export const expressionCheckMethods = {
1810
2041
  for (const elem of expr.elems) {
1811
2042
  if (elem.kind === "KeyValueExpr") {
1812
2043
  const keyName = elem.key.name;
1813
- const embed = base._embeds?.find((e) => {
1814
- const inner = e.kind === "pointer" ? e.base : e;
1815
- const name =
1816
- inner.kind === "named" ? inner.name.split(".").pop() : null;
1817
- return name === keyName;
1818
- });
2044
+ const embed = base._structFields?.find(
2045
+ (f) => f.embedded && embeddedFieldName(f) === keyName,
2046
+ )?.type;
1819
2047
  if (embed) elem._isEmbedInit = true;
1820
2048
  const fieldType = base.fields?.get(keyName) ?? embed;
1821
2049
  if (fieldType) {