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
@@ -2,21 +2,26 @@ import {
2
2
  closureMutates,
3
3
  fnMutates,
4
4
  nodeMutatesVar,
5
- nodeWritesVar,
6
5
  rootIdentName,
7
6
  someChild,
8
7
  } from "../../lower/index.js";
8
+ import { hasCallOrReceive } from "../../lower/walk.js";
9
9
  import { constIntValue } from "../../typechecker/const-fold.js";
10
10
  import {
11
11
  BROWSER_GLOBALS,
12
12
  compositeLitIndices,
13
13
  ERROR,
14
+ embeddedFieldName,
15
+ is64BitInt,
16
+ is64BitUnsigned,
14
17
  isAny,
15
18
  isComplex,
16
19
  isTypedArraySlice,
17
20
  typedArrayConstructorForElem,
18
21
  typeStr,
19
22
  } from "../../typechecker/types.js";
23
+ import { goTypeName } from "../shared/fmt-types.js";
24
+ import { jsNativeCall, NATIVE_HELPERS } from "./natives.js";
20
25
 
21
26
  // True when `node` compares the identifier `name` with nil (`l == nil`): a
22
27
  // pointer-receiver method that does so is meant to run on a nil receiver.
@@ -141,20 +146,6 @@ function collectFunctionLabels(node, labels = new Set()) {
141
146
  }
142
147
 
143
148
  const NAMED_ZERO_TABLE = {
144
- "time.Time": [null, "{ _d: new Date(0), _isZero: true }"],
145
- "strings.Builder": [null, '{ _buf: "" }'],
146
- "bytes.Buffer": [null, "{ _buf: [] }"],
147
- "strings.Replacer": [null, "{ _p: [], Replace(s) { return s; } }"],
148
- "sync.Mutex": ["sync", "__sync_mutex()"],
149
- "sync.RWMutex": ["sync", "__sync_rwmutex()"],
150
- "sync.Once": ["sync", "__sync_once()"],
151
- "sync.WaitGroup": ["sync", "__sync_waitgroup()"],
152
- "binary.LittleEndian": ["binary", "__binary_le"],
153
- "encoding/binary.LittleEndian": ["binary", "__binary_le"],
154
- "binary.BigEndian": ["binary", "__binary_be"],
155
- "encoding/binary.BigEndian": ["binary", "__binary_be"],
156
- "base64.Encoding": ["base64", "__base64_std"],
157
- "encoding/base64.Encoding": ["base64", "__base64_std"],
158
149
  "simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
159
150
  F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
160
151
  "simd.Mask32x4": [null, "Object.freeze([false, false, false, false])"],
@@ -236,91 +227,13 @@ function splitLastAccess(js) {
236
227
 
237
228
  // Namespace constants: pkg.Field → JS literal
238
229
  const NS_CONSTANTS = {
239
- math: {
240
- Pi: "Math.PI",
241
- E: "Math.E",
242
- Phi: "1.618033988749895",
243
- Sqrt2: "Math.SQRT2",
244
- SqrtE: "1.6487212707001282",
245
- SqrtPi: "1.7724538509055159",
246
- SqrtPhi: "1.272019649514069",
247
- Ln2: "Math.LN2",
248
- Log2E: "Math.LOG2E",
249
- Ln10: "Math.LN10",
250
- Log10E: "Math.LOG10E",
251
- MaxFloat32: "3.4028234663852886e38",
252
- SmallestNonzeroFloat32: "1.401298464324817e-45",
253
- MaxFloat64: "Number.MAX_VALUE",
254
- SmallestNonzeroFloat64: "Number.MIN_VALUE",
255
- MaxInt: "Number.MAX_SAFE_INTEGER",
256
- MinInt: "Number.MIN_SAFE_INTEGER",
257
- MaxInt8: "127",
258
- MinInt8: "-128",
259
- MaxInt16: "32767",
260
- MinInt16: "-32768",
261
- MaxInt32: "2147483647",
262
- MinInt32: "-2147483648",
263
- MaxInt64: "9223372036854775807",
264
- MinInt64: "-9223372036854775808",
265
- MaxUint8: "255",
266
- MaxUint16: "65535",
267
- MaxUint32: "4294967295",
268
- MaxUint: "18446744073709551615",
269
- MaxUint64: "18446744073709551615",
270
- },
271
- strconv: {
272
- ErrSyntax: "__strconv_err_syntax",
273
- ErrRange: "__strconv_err_range",
274
- },
275
- bits: {
276
- UintSize: "64",
277
- },
278
- path: {
279
- ErrBadPattern: "__path_err_bad_pattern",
280
- },
281
- filepath: {
282
- ErrBadPattern: "__path_err_bad_pattern",
283
- },
284
- io: {
285
- EOF: "__io_eof",
286
- Discard:
287
- "{ WriteString(s) { return [s.length, null]; }, Write(b) { return [b.length, null]; } }",
288
- },
289
- sha256: {
290
- Size: "32",
291
- BlockSize: "64",
292
- },
293
- hex: {
294
- ErrLength: '__error("encoding/hex: odd length hex string")',
295
- },
296
- base64: {
297
- StdPadding: "61",
298
- NoPadding: "-1",
299
- StdEncoding: "__base64_std",
300
- URLEncoding: "__base64_url",
301
- RawStdEncoding: "__base64_raw_std",
302
- RawURLEncoding: "__base64_raw_url",
303
- },
304
- binary: {
305
- LittleEndian: "__binary_le",
306
- BigEndian: "__binary_be",
307
- },
308
- rand: {
309
- Reader: "__crypto_rand_reader",
310
- },
311
- os: {
312
- Args: "process.argv",
313
- Stdout: "__os_stdout",
314
- Stderr: "__os_stderr",
315
- Stdin: "__os_stdin",
316
- },
317
230
  time: {
318
- Nanosecond: "1",
319
- Microsecond: "1000",
320
- Millisecond: "1000000",
321
- Second: "1000000000",
322
- Minute: "60000000000",
323
- Hour: "3600000000000",
231
+ Nanosecond: "1n",
232
+ Microsecond: "1000n",
233
+ Millisecond: "1000000n",
234
+ Second: "1000000000n",
235
+ Minute: "60000000000n",
236
+ Hour: "3600000000000n",
324
237
  RFC3339: '"2006-01-02T15:04:05Z07:00"',
325
238
  RFC3339Nano: '"2006-01-02T15:04:05.999999999Z07:00"',
326
239
  DateOnly: '"2006-01-02"',
@@ -348,7 +261,6 @@ const NS_CONSTANTS = {
348
261
  Friday: "5",
349
262
  Saturday: "6",
350
263
  },
351
- utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
352
264
  };
353
265
 
354
266
  // JavaScript precedence of the operators emitted for Go binary expressions.
@@ -413,6 +325,7 @@ const SHARED_ZERO_FACTORIES = {
413
325
  0: "__z0",
414
326
  '""': "__zs",
415
327
  false: "__zf",
328
+ "0n": "__z64",
416
329
  };
417
330
 
418
331
  // Zero-value JS literals for basic Go types.
@@ -422,11 +335,11 @@ const ZERO_FOR_BASIC = {
422
335
  int8: "0",
423
336
  int16: "0",
424
337
  int32: "0",
425
- int64: "0",
338
+ int64: "0n",
426
339
  uint8: "0",
427
340
  uint16: "0",
428
341
  uint32: "0",
429
- uint64: "0",
342
+ uint64: "0n",
430
343
  uintptr: "0",
431
344
  float32: "0",
432
345
  float64: "0",
@@ -439,13 +352,18 @@ const ZERO_FOR_BASIC = {
439
352
  };
440
353
 
441
354
  // Set of all numeric type names (for type-assertion codegen).
442
- const TYPEOF_NUMBER_NAMES = new Set([...INT_TYPE_NAMES, "float32", "float64"]);
355
+ const TYPEOF_NUMBER_NAMES = new Set([
356
+ ...[...INT_TYPE_NAMES].filter((n) => n !== "int64" && n !== "uint64"),
357
+ "float32",
358
+ "float64",
359
+ ]);
443
360
 
444
361
  // Builtin function codegen — keyed by builtin name, value is (self, expr) => string.
445
362
  const BUILTIN_GEN = {
446
363
  append: (s, e) => s.genAppend(e),
447
364
  len: (s, e) => s._genBuiltinLen(e),
448
365
  cap: (s, e) => {
366
+ if (e._constLen != null) return s._genConstLen(e);
449
367
  s.useHelper("slice");
450
368
  return `__cap(${s.genExpr(e.args[0])})`;
451
369
  },
@@ -457,13 +375,11 @@ const BUILTIN_GEN = {
457
375
  copy: (s, e) => {
458
376
  const dst = s.genExpr(e.args[0]);
459
377
  const src = s._genSliceSource(e.args[1]);
460
- // Array elements are values: the copied elements must not alias src.
378
+ // Array and struct elements are values: the copied elements must not
379
+ // alias src.
461
380
  const t = e.args[1]._type;
462
381
  const elem = (t?.kind === "named" ? t.underlying : t)?.elem;
463
- const clone =
464
- s._aggregateBase(elem)?.kind === "array"
465
- ? s._cloneJs(elem, "__e")
466
- : "__e";
382
+ const clone = s._aggregateBase(elem) ? s._cloneJs(elem, "__e") : "__e";
467
383
  const items =
468
384
  clone === "__e"
469
385
  ? "(__cs.slice ? __cs.slice(0, n) : __cs.subarray(0, n))"
@@ -496,15 +412,21 @@ const BUILTIN_GEN = {
496
412
  // `print` writes without a trailing newline (Go semantics); the wasm glue
497
413
  // buffers the same way, so output parity holds across backends.
498
414
  print: (s, e) =>
499
- `((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))([${e.args.map((a) => s.genExpr(a)).join(", ")}].map(String).join(" "))`,
415
+ `((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))([${e.args.map((a) => s.genExpr(a)).join(", ")}].map(String).join(""))`,
500
416
  println: (s, e) =>
501
417
  `console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
502
- panic: (s, e) => `(() => { throw new Error(${s.genExpr(e.args[0])}); })()`,
418
+ panic: (s, e) => {
419
+ const arg = s.genExpr(e.args[0]);
420
+ s.useHelper("panicRt");
421
+ return `__gopanicv(${arg})`;
422
+ },
503
423
  recover: (s) => {
504
424
  s.useHelper("panicRt");
505
425
  return `__recover(${s._recoverOkInScope ? "__recoverOk" : "false"})`;
506
426
  },
507
427
  error: (s, e) => {
428
+ // `error(x)` of an error (or nil) is the conversion: x itself.
429
+ if (!s._isStringExprType(e.args[0]?._type)) return s.genExpr(e.args[0]);
508
430
  s.useHelper("error");
509
431
  return `__error(${s.genExpr(e.args[0])})`;
510
432
  },
@@ -521,13 +443,25 @@ const BUILTIN_GEN = {
521
443
  // A type parameter may be a string or a number: pick the form at run time
522
444
  // (Math.min/max keeps NaN propagation and -0 for floats).
523
445
  function genMinMax(s, e, fn, cmp) {
524
- const args = e.args.map((a) => s.genExpr(a));
446
+ const is64 = s._is64BitInt(e._type);
447
+ const args = e.args.map((a) => {
448
+ const js = s.genExpr(a);
449
+ if (
450
+ is64 &&
451
+ a._type?.kind === "untyped" &&
452
+ !js.endsWith("n") &&
453
+ /^-?\d+$/.test(js)
454
+ ) {
455
+ return `${js}n`;
456
+ }
457
+ return js;
458
+ });
525
459
  const fold = (xs) => `${xs}.reduce((a, b) => (b ${cmp} a ? b : a))`;
526
460
  if (args.length === 1) return args[0];
527
461
  if (e._type?.kind === "typeParam")
528
- return `((...v) => (typeof v[0] === "string" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
529
- if (!s._isStringExprType(e._type)) return `Math.${fn}(${args.join(", ")})`;
530
- return fold(`[${args.join(", ")}]`);
462
+ return `((...v) => (typeof v[0] === "string" || typeof v[0] === "bigint" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
463
+ if (is64 || s._isStringExprType(e._type)) return fold(`[${args.join(", ")}]`);
464
+ return `Math.${fn}(${args.join(", ")})`;
531
465
  }
532
466
 
533
467
  // Expression kinds whose folded constant value (`_const`, attached by the
@@ -590,6 +524,14 @@ function hasTypeParamType(t) {
590
524
 
591
525
  export const expressionGenMethods = {
592
526
  genExpr(expr) {
527
+ if (expr._asComplex && !expr._complexWrapping) {
528
+ expr._complexWrapping = true;
529
+ try {
530
+ return `{ re: ${this.genExpr(expr)}, im: 0 }`;
531
+ } finally {
532
+ expr._complexWrapping = false;
533
+ }
534
+ }
593
535
  if (expr._ifaceKeyDyn && !expr._ifaceKeying) {
594
536
  // A key typed by a type parameter: encode it for the instantiated type.
595
537
  const f = this._typeParamZeroFactory(expr._ifaceKeyParam);
@@ -613,21 +555,66 @@ export const expressionGenMethods = {
613
555
  expr._ifaceKeying = true;
614
556
  try {
615
557
  this.useHelper("structKey");
616
- return `__ik(${this.genExpr(expr)})`;
558
+ const tag = this._ifaceKeyTag(expr._ifaceKeyType);
559
+ return `__ik(${this.genExpr(expr)}${tag ? `, ${JSON.stringify(tag)}` : ""})`;
617
560
  } finally {
618
561
  expr._ifaceKeying = false;
619
562
  }
620
563
  }
621
564
  }
565
+ if (expr._mapKeyEnc && !expr._mapKeying) {
566
+ // Array, anonymous struct and pointer keys: see HELPER_STRUCT_KEY.
567
+ expr._mapKeying = true;
568
+ try {
569
+ this.useHelper("structKey");
570
+ const enc = expr._mapKeyEnc === "pk" ? "__pk" : "__sk";
571
+ return `${enc}(${this.genExpr(expr)})`;
572
+ } finally {
573
+ expr._mapKeying = false;
574
+ }
575
+ }
576
+ if (
577
+ expr._ifaceSrc?.kind === "pointer" &&
578
+ expr._ifaceSrc.base?.kind === "typeParam" &&
579
+ !expr._pbsWrapping
580
+ ) {
581
+ // `&v` of a type parameter's variable is a box; a struct behind it
582
+ // is its own pointer.
583
+ expr._pbsWrapping = true;
584
+ try {
585
+ this.useHelper("ifaceBox");
586
+ return `__pbs(${this.genExpr(expr)})`;
587
+ } finally {
588
+ expr._pbsWrapping = false;
589
+ }
590
+ }
591
+ if (
592
+ expr._ifaceSrc?.kind === "typeParam" &&
593
+ !expr._tpBoxing &&
594
+ !expr._ifaceKeyDyn &&
595
+ this._typeParamZeroFactory(expr._ifaceSrc.name)
596
+ ) {
597
+ // A type parameter's value entering an interface is boxed and
598
+ // tagged by the instantiated type (see `__ti`).
599
+ expr._tpBoxing = true;
600
+ try {
601
+ const f = this._typeParamZeroFactory(expr._ifaceSrc.name);
602
+ return `(${f}.b ?? ${f}.fb ?? ((__v) => __v))(${this.genExpr(expr)})`;
603
+ } finally {
604
+ expr._tpBoxing = false;
605
+ }
606
+ }
622
607
  if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
623
- if (expr._ifaceSrc && !expr._fmtDescing && !this._isNamedBoxKind(expr)) {
624
- const desc = this._fmtDesc(expr._ifaceSrc);
625
- if (desc) {
626
- expr._fmtDescing = true;
608
+ if (expr._ifaceSrc && !expr._ityTagging && !this._isNamedBoxKind(expr)) {
609
+ const tag = this._dynTypeTag(expr._ifaceSrc);
610
+ if (tag) {
611
+ this.useHelper("ifaceBox");
612
+ expr._ityTagging = true;
627
613
  try {
628
- return this._fmtDescWrap(this.genExpr(expr), desc);
614
+ const ptr = expr._ifaceSrc.kind === "pointer" ? ", true" : "";
615
+ return `__ity(${this.genExpr(expr)}, ${JSON.stringify(tag)}${ptr})`;
629
616
  } finally {
630
- expr._fmtDescing = false;
617
+ expr._ityTagging = false;
631
618
  }
632
619
  }
633
620
  }
@@ -635,11 +622,13 @@ export const expressionGenMethods = {
635
622
  expr._ifaceBoxing = true;
636
623
  try {
637
624
  this.useHelper("ifaceBox");
638
- return this._ifaceBoxWrap(
625
+ const boxed = this._ifaceBoxWrap(
639
626
  this.genExpr(expr),
640
627
  expr._ifaceBox,
641
628
  expr._ifacePtrFields,
629
+ expr.kind === "CompositeLit",
642
630
  );
631
+ return this._typedNilWrap(boxed, expr);
643
632
  } finally {
644
633
  expr._ifaceBoxing = false;
645
634
  }
@@ -649,15 +638,30 @@ export const expressionGenMethods = {
649
638
  switch (expr.kind) {
650
639
  case "BasicLit":
651
640
  if (expr.litKind === "STRING") return JSON.stringify(expr.value);
641
+ if (
642
+ (this._is64BitInt(expr._type) || expr._is64Bit) &&
643
+ (expr.litKind === "INT" || expr._const?.k === "INT")
644
+ ) {
645
+ return `${expr.value}n`;
646
+ }
652
647
  if (expr._const?.k === "INT")
653
648
  this._warnConstPrecision(expr._const, expr);
654
649
  return expr.value; // int, float, bool, nil(null)
655
650
  case "ImagLit":
656
651
  return `{ re: 0, im: ${expr.value} }`;
657
- case "Ident":
652
+ case "Ident": {
658
653
  if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
659
654
  return `${expr.name}.value`;
660
- return expr.name;
655
+ // A named untyped constant used as an int64 operand: its
656
+ // declaration holds a Number, so emit the exact BigInt.
657
+ const lit64 =
658
+ expr._is64Bit &&
659
+ expr._const &&
660
+ (expr._type?.kind === "untyped" || this._is64BitInt(expr._type))
661
+ ? this._gen64BitFoldedConst(expr, expr._const)
662
+ : null;
663
+ return lit64 ?? expr.name;
664
+ }
661
665
  case "AwaitExpr":
662
666
  return `await ${this.genExpr(expr.expr)}`;
663
667
  case "InstantiationExpr":
@@ -672,6 +676,17 @@ export const expressionGenMethods = {
672
676
  }
673
677
  },
674
678
 
679
+ _gen64BitFoldedConst(expr, val) {
680
+ if (
681
+ (this._is64BitInt(expr._type) || expr._is64Bit) &&
682
+ (val.k === "INT" || expr._intConst)
683
+ ) {
684
+ const n = constIntValue(val);
685
+ if (n !== null) return n < 0n ? `(${n}n)` : `${n}n`;
686
+ }
687
+ return null;
688
+ },
689
+
675
690
  // The literal for a folded constant expression (`1 << 40` →
676
691
  // `1099511627776`), or null. Integers outside ±2⁵³ emit the nearest
677
692
  // Number and a precision warning.
@@ -686,6 +701,8 @@ export const expressionGenMethods = {
686
701
  return null;
687
702
  }
688
703
  if (!FOLDED_CONST_KINDS.has(expr.kind) || isSignedLit(expr)) return null;
704
+ const lit64 = this._gen64BitFoldedConst(expr, val);
705
+ if (lit64 !== null) return lit64;
689
706
  if (val.k === "INT" || expr._intConst) this._warnConstPrecision(val, expr);
690
707
  return CONST_LITS[val.k]?.(val.v) ?? null;
691
708
  },
@@ -694,6 +711,8 @@ export const expressionGenMethods = {
694
711
  // per node per generate run: some forms generate the same node more than
695
712
  // once, and cached ASTs are generated again by later compiles).
696
713
  _warnConstPrecision(val, expr) {
714
+ if (this._is64BitInt(expr?._type) || expr?._is64Bit || expr?._floatConst)
715
+ return;
697
716
  const i = constIntValue(val);
698
717
  if (i === null || (i <= JS_SAFE_INT && i >= -JS_SAFE_INT)) return;
699
718
  if (BigInt(Number(i)) !== i && !this._precisionWarned.has(expr)) {
@@ -703,7 +722,9 @@ export const expressionGenMethods = {
703
722
  },
704
723
 
705
724
  // `js` boxed as `box` (a checker _ifaceBoxMeta kind) for an interface.
706
- _ifaceBoxWrap(js, box, ptrFields) {
725
+ // `fresh` marks a value nothing else refers to (a composite literal), which
726
+ // a struct box then takes over instead of copying.
727
+ _ifaceBoxWrap(js, box, ptrFields, fresh = false) {
707
728
  this.useHelper("ifaceBox");
708
729
  const fn =
709
730
  {
@@ -713,14 +734,22 @@ export const expressionGenMethods = {
713
734
  map: "__ifm",
714
735
  slice: "__ifs",
715
736
  }[box] ?? "__ifv";
716
- const extra = ptrFields?.length ? `, ${JSON.stringify(ptrFields)}` : "";
717
- return `${fn}(${js}${extra})`;
737
+ const fields = ptrFields?.length ? JSON.stringify(ptrFields) : "";
738
+ if (fn === "__ifv" && fresh)
739
+ return `${fn}(${js}, ${fields || "null"}, true)`;
740
+ return `${fn}(${js}${fields ? `, ${fields}` : ""})`;
718
741
  },
719
742
 
720
- // `js` tagged with the fmt descriptor source `desc` (see _fmtDesc).
721
- _fmtDescWrap(js, desc) {
722
- this.useHelper("ifd");
723
- return `__ifd(${js}, ${desc})`;
743
+ // A nil `*T` entering an interface is a typed nil: it keeps T's methods
744
+ // (callable with a nil receiver) and differs from the nil interface.
745
+ _typedNilWrap(js, expr) {
746
+ if (expr._ifaceBox !== "ptr") return js;
747
+ const src = expr._ifaceSrc;
748
+ const base = src?.kind === "pointer" ? src.base : null;
749
+ if (base?.kind !== "named" || base.underlying?.kind !== "struct") return js;
750
+ const cls = this._dequalify(base.name).replace(/\[.*$/, "");
751
+ if (!this.structNames.has(cls)) return js;
752
+ return `__ifn(${js}, ${cls})`;
724
753
  },
725
754
 
726
755
  _isNamedBoxKind(expr) {
@@ -819,8 +848,12 @@ export const expressionGenMethods = {
819
848
  // Pseudo type object for an AST type node (used where no checker type is available).
820
849
  _typeFromNode(node) {
821
850
  if (!node) return null;
822
- if (node.kind === "TypeName" && this.structNames.has(node.name))
823
- return { kind: "named", name: node.name, underlying: { kind: "struct" } };
851
+ if (node.kind === "TypeName" || node.kind === "SelectorExpr") {
852
+ // `pkg.S` of a bundled package names the class `S`.
853
+ const name = this.getTypeName(node);
854
+ if (name && this.structNames.has(name))
855
+ return { kind: "named", name, underlying: { kind: "struct" } };
856
+ }
824
857
  if (node.kind === "ArrayType")
825
858
  return { kind: "array", elem: this._typeFromNode(node.elem) };
826
859
  return null;
@@ -857,7 +890,9 @@ export const expressionGenMethods = {
857
890
  _isAddressableExpr(e) {
858
891
  switch (e?.kind) {
859
892
  case "Ident":
860
- return e.name !== "_" && !e._isTypeRef;
893
+ return (
894
+ e.name !== "_" && (!e._isTypeRef || e._type?.kind === "typeParam")
895
+ );
861
896
  case "SelectorExpr":
862
897
  return !e._isMethodValue && !e._isMethodExpr;
863
898
  case "IndexExpr":
@@ -889,11 +924,6 @@ export const expressionGenMethods = {
889
924
  return rootIdentName(e);
890
925
  },
891
926
 
892
- // True when `node` itself (not its children) writes to variable `name`.
893
- _nodeWritesVar(node, name) {
894
- return nodeWritesVar(node, name);
895
- },
896
-
897
927
  // True when `node` may modify (or take the address of) the value held by variable `name`.
898
928
  _nodeMutatesVar(node, name, addrOnly = false) {
899
929
  return nodeMutatesVar(node, name, addrOnly);
@@ -1031,7 +1061,9 @@ export const expressionGenMethods = {
1031
1061
  // Dereference: *p
1032
1062
  if (op === "*") {
1033
1063
  if (this._isStructPointerType(expr.operand._type)) {
1034
- return this._genNilChecked(this.genExpr(expr.operand));
1064
+ return expr._nonNil
1065
+ ? this.genExpr(expr.operand)
1066
+ : this._genNilChecked(this.genExpr(expr.operand));
1035
1067
  }
1036
1068
  if (expr.operand._type?.base?.kind === "typeParam") {
1037
1069
  if (expr._derefOpTarget) return `${this.genExpr(expr.operand)}.value`;
@@ -1046,6 +1078,17 @@ export const expressionGenMethods = {
1046
1078
  return inner;
1047
1079
  }
1048
1080
  const code = this.genExpr(expr.operand);
1081
+ if (this._is64BitInt(expr._type) || expr._is64Bit) {
1082
+ let operandCode = code;
1083
+ if (!operandCode.endsWith("n") && /^-?\d+$/.test(operandCode)) {
1084
+ operandCode = `${operandCode}n`;
1085
+ }
1086
+ if (op === "+") return operandCode;
1087
+ const wrap = this._is64BitUnsigned(expr._type)
1088
+ ? "BigInt.asUintN"
1089
+ : "BigInt.asIntN";
1090
+ if (op === "-" || op === "~") return `${wrap}(64, ${op}(${operandCode}))`;
1091
+ }
1049
1092
  const wrap = this.strict
1050
1093
  ? SMALL_UNSIGNED_WRAP[this._typeName(expr._type)]
1051
1094
  : null;
@@ -1064,11 +1107,23 @@ export const expressionGenMethods = {
1064
1107
 
1065
1108
  _genSelectorExpr(expr) {
1066
1109
  // Go-source stdlib member: bundled under its mangled top-level name.
1067
- if (expr._stdName) return expr._stdName;
1110
+ if (expr._stdName) {
1111
+ // An untyped constant used as an int64 operand: its declaration
1112
+ // holds a Number, so emit the exact BigInt (as for an Ident).
1113
+ const lit64 =
1114
+ expr._is64Bit && expr._type?.kind === "untyped" && expr._const
1115
+ ? this._gen64BitFoldedConst(expr, expr._const)
1116
+ : null;
1117
+ return lit64 ?? expr._stdName;
1118
+ }
1068
1119
  if (this._isStdlibFuncValue(expr)) return this._genStdlibFuncValue(expr);
1069
1120
  if (expr._isMethodExpr) {
1070
1121
  const namedExpr = this._namedMethodExprInfo(expr);
1071
1122
  if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
1123
+ const rt = expr._methodExprRecv;
1124
+ const protoCls = this._ptrMethodProtoClass(rt, expr.field);
1125
+ if (protoCls)
1126
+ return `((recv, ...args) => ${protoCls}.prototype.${expr.field}.call(recv, ...args))`;
1072
1127
  return `((recv, ...args) => recv.${expr.field}(...args))`;
1073
1128
  }
1074
1129
  const namedValue = this._genNamedMethodValue(expr);
@@ -1076,26 +1131,6 @@ export const expressionGenMethods = {
1076
1131
  if (expr.expr.kind === "Ident") {
1077
1132
  const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
1078
1133
  if (nsConst !== undefined) {
1079
- if (expr.expr.name === "strconv") {
1080
- this.useHelper("strconvError");
1081
- } else if (expr.expr.name === "path" || expr.expr.name === "filepath") {
1082
- if (expr.field === "ErrBadPattern") this.useHelper("pathError");
1083
- } else if (expr.expr.name === "io" && expr.field === "EOF") {
1084
- this.useHelper("ioEOF");
1085
- } else if (expr.expr.name === "binary") {
1086
- this.useHelper("binary");
1087
- } else if (expr.expr.name === "hex") {
1088
- this.useHelper("hex");
1089
- this.useHelper("error");
1090
- } else if (expr.expr.name === "base64") {
1091
- this.useHelper("base64");
1092
- } else if (expr.expr.name === "rand" && expr.field === "Reader") {
1093
- this.useHelper("cryptoRand");
1094
- } else if (expr.expr.name === "sha256") {
1095
- this.useHelper("sha256");
1096
- } else if (expr.expr.name === "os") {
1097
- this.useHelper("os");
1098
- }
1099
1134
  return nsConst;
1100
1135
  }
1101
1136
  }
@@ -1178,7 +1213,7 @@ export const expressionGenMethods = {
1178
1213
  const inner = sf.type?.kind === "pointer" ? sf.type.base : sf.type;
1179
1214
  if (inner?.kind !== "named" || !this.structNames.has(inner.name))
1180
1215
  continue;
1181
- const name = inner.name;
1216
+ const name = embeddedFieldName(sf);
1182
1217
  if (name === field) return null;
1183
1218
  const ist = inner.underlying;
1184
1219
  if (!ist?.fields?.has(field)) continue;
@@ -1299,21 +1334,54 @@ export const expressionGenMethods = {
1299
1334
 
1300
1335
  _genCallArgs(expr) {
1301
1336
  if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
1302
- return expr.args
1303
- .map((a, i) =>
1304
- a._spread
1305
- ? this._genSpreadArg(expr, a)
1337
+ return this._genCallArgList(expr).join(", ");
1338
+ },
1339
+
1340
+ _genCallArgList(expr) {
1341
+ return expr.args.map((a, i) =>
1342
+ a._spread
1343
+ ? this._genSpreadArg(expr, a)
1344
+ : a._boxTypeParam
1345
+ ? this._genTypeParamBox(a)
1306
1346
  : this._callerCopiesArg(expr, i)
1307
1347
  ? this.genValueExpr(a)
1308
1348
  : this.genExpr(a),
1309
- )
1310
- .join(", ");
1349
+ );
1350
+ },
1351
+
1352
+ // A type parameter operand of a Print-family fmt call (see
1353
+ // src/lower/fmt-formatters.js): a JS value does not know its Go type, so
1354
+ // the type argument boxes it as converting it to an `any` does.
1355
+ _genTypeParamBox(a) {
1356
+ // Boxed below: skip the boxing of a value entering an interface.
1357
+ const wasBoxing = a._tpBoxing;
1358
+ a._tpBoxing = true;
1359
+ const js = this.genExpr(a);
1360
+ a._tpBoxing = wasBoxing;
1361
+ const tpf = this._typeParamZeroFactory(a._type.name);
1362
+ return tpf ? `(${tpf}.b ?? ${tpf}.fb ?? ((__v) => __v))(${js})` : js;
1363
+ },
1364
+
1365
+ // A direct call of a `//gofront:native` function: host code from the
1366
+ // registry, or null (declined, natives off, spread arguments) to call the
1367
+ // Go body.
1368
+ _genNativeCall(expr) {
1369
+ if (!this.natives || !expr._native) return null;
1370
+ if (expr._multiForward || expr.args.some((a) => a._spread)) return null;
1371
+ const typeArgs = expr._typeArgs ?? expr.func._typeArgs ?? [];
1372
+ const js = jsNativeCall(expr._native, this._genCallArgList(expr), typeArgs);
1373
+ if (js !== null && NATIVE_HELPERS.has(expr._native))
1374
+ this.useHelper(NATIVE_HELPERS.get(expr._native));
1375
+ return js;
1311
1376
  },
1312
1377
 
1313
1378
  // `f(xs...)`: a Go-compiled variadic callee receives the slice itself in a
1314
1379
  // marker (its prologue unwraps it); other callees get a JS spread.
1315
1380
  _genSpreadArg(call, arg) {
1316
1381
  const js = this.genExpr(arg);
1382
+ // A variadic function of a Go-source package (`fmt.Println`): a nil
1383
+ // slice spreads to no operands.
1384
+ if (call.func._stdName) return `...(${js} ?? [])`;
1317
1385
  return this._calleeIsGoVariadic(call) ? `{ __sp: ${js} }` : `...${js}`;
1318
1386
  },
1319
1387
 
@@ -1355,7 +1423,9 @@ export const expressionGenMethods = {
1355
1423
  if (this._isStructPointerType(expr.expr._type)) {
1356
1424
  // Strict mode: a field of a nil struct pointer panics as in Go.
1357
1425
  const recv =
1358
- this.strict && !expr._isMethodValue ? this._genNilChecked(base) : base;
1426
+ this.strict && !expr._isMethodValue && !expr._nonNil
1427
+ ? this._genNilChecked(base)
1428
+ : base;
1359
1429
  const sel = `${recv}${this._promotedPath(expr)}.${expr.field}`;
1360
1430
  if (expr._isMethodValue && !expr._callee) {
1361
1431
  // `p.M` with a value receiver binds a copy of `*p` now.
@@ -1366,6 +1436,15 @@ export const expressionGenMethods = {
1366
1436
  );
1367
1437
  return `((__r) => __r.${expr.field}.bind(__r))(${copy})`;
1368
1438
  }
1439
+ // A pointer receiver may be nil: bind through the prototype so
1440
+ // `s.M` on a nil pointer does not read `.M` off null.
1441
+ const protoCls = this._ptrMethodProtoClass(
1442
+ expr.expr._type,
1443
+ expr.field,
1444
+ expr,
1445
+ );
1446
+ if (protoCls)
1447
+ return `${protoCls}.prototype.${expr.field}.bind(${base})`;
1369
1448
  return `${sel}.bind(${base})`;
1370
1449
  }
1371
1450
  return sel;
@@ -1382,11 +1461,12 @@ export const expressionGenMethods = {
1382
1461
  const base = wrapField
1383
1462
  ? `${this.genExpr(expr.expr)}.${wrapField}`
1384
1463
  : this.genExpr(expr.expr);
1385
- const idx = this.genExpr(expr.index);
1464
+ let idx = this.genExpr(expr.index);
1386
1465
  if (expr._mapValueType) {
1387
1466
  if (!expr._lvalue) return this._genMapIndexExpr(expr, base, idx);
1388
1467
  return `${this._genMapWriteBase(base)}[${idx}]`;
1389
1468
  }
1469
+ if (this._is64BitInt(expr.index?._type)) idx = `Number(${idx})`;
1390
1470
  const checked = this._genStrictIndex(expr, base, idx);
1391
1471
  if (checked) return checked;
1392
1472
  if (this._isStringExprType(exprType) && !expr._lvalue)
@@ -1428,8 +1508,8 @@ export const expressionGenMethods = {
1428
1508
  _genMapIndexExpr(expr, base, idx) {
1429
1509
  const zero = this.zeroValueForType(expr._mapValueType);
1430
1510
  if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index))
1431
- return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
1432
- return `(${base}[${idx}] ?? ${zero})`;
1511
+ return `((__m, __k) => __m?.[__k] ?? ${zero})(${base}, ${idx})`;
1512
+ return `(${base}?.[${idx}] ?? ${zero})`;
1433
1513
  },
1434
1514
 
1435
1515
  _isStringExprType(exprType) {
@@ -1442,8 +1522,16 @@ export const expressionGenMethods = {
1442
1522
 
1443
1523
  _genSliceExpr(expr) {
1444
1524
  const base = this.genExpr(expr.expr);
1445
- const lo = expr.low ? this.genExpr(expr.low) : "0";
1446
- const hi = expr.high ? this.genExpr(expr.high) : "undefined";
1525
+ const lo = expr.low
1526
+ ? this._is64BitInt(expr.low._type)
1527
+ ? `Number(${this.genExpr(expr.low)})`
1528
+ : this.genExpr(expr.low)
1529
+ : "0";
1530
+ const hi = expr.high
1531
+ ? this._is64BitInt(expr.high._type)
1532
+ ? `Number(${this.genExpr(expr.high)})`
1533
+ : this.genExpr(expr.high)
1534
+ : "undefined";
1447
1535
  if (this._isStringExprType(expr.expr._type))
1448
1536
  return expr.high ? `${base}.slice(${lo}, ${hi})` : `${base}.slice(${lo})`;
1449
1537
  // `b[:]`, `b[0:]` and `a[:]` have the operand's length and capacity:
@@ -1456,7 +1544,9 @@ export const expressionGenMethods = {
1456
1544
  // the result stays within `len(x)`; typed arrays always share.
1457
1545
  if (expr._selfReslice && !isTypedArraySlice(expr.expr._type))
1458
1546
  return `__reslice(${base}, ${lo}, ${hi})`;
1459
- const max = expr.max ? `, ${this.genExpr(expr.max)}` : "";
1547
+ const max = expr.max
1548
+ ? `, ${this._is64BitInt(expr.max._type) ? `Number(${this.genExpr(expr.max)})` : this.genExpr(expr.max)}`
1549
+ : "";
1460
1550
  return `__slice(${base}, ${lo}, ${hi}${max})`;
1461
1551
  },
1462
1552
 
@@ -1471,7 +1561,7 @@ export const expressionGenMethods = {
1471
1561
  this._withFnCtx(expr.body, () =>
1472
1562
  this.indented(() => {
1473
1563
  this._emitParamCopies(expr.params);
1474
- this._genBody(expr.body, expr);
1564
+ this._withNamedReturns(expr, () => this._genBody(expr.body, expr));
1475
1565
  }),
1476
1566
  );
1477
1567
  const body = this.out.join("\n");
@@ -1493,18 +1583,38 @@ export const expressionGenMethods = {
1493
1583
  if (!expr._commaOk) {
1494
1584
  // plain assertion: panic if check fails (matches Go behavior)
1495
1585
  if (check === "true") return src; // can't check at runtime — pass through
1586
+ this.useHelper("ifaceBox");
1496
1587
  return wrap(
1497
- `(${check} ? ${out} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
1588
+ `(${check} ? ${out} : (() => { throw ${this._typeAssertFailJs(expr, val)}; })())`,
1498
1589
  );
1499
1590
  }
1500
- // comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted array
1501
- // is a copy of the boxed value.
1591
+ // comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted struct
1592
+ // or array is a copy of the boxed value.
1502
1593
  const zero = this.zeroValueForTypeNode(expr.type);
1503
- const arrT = this._typeFromNode(expr.type);
1504
- const okOut = arrT?.kind === "array" ? this._cloneJs(arrT, out) : out;
1594
+ const valT = this._typeFromNode(expr.type);
1595
+ const okOut = this._aggregateBase(valT) ? this._cloneJs(valT, out) : out;
1505
1596
  return wrap(`(${check} ? [${okOut}, true] : [${zero}, false])`);
1506
1597
  },
1507
1598
 
1599
+ // The error a failed `x.(T)` throws: Go's `interface conversion: ...` text
1600
+ // naming the static interface type, the dynamic type and the target.
1601
+ _typeAssertFailJs(expr, val) {
1602
+ const target = expr._type;
1603
+ const u = this._underlyingOf(target);
1604
+ const methods =
1605
+ u?.kind === "interface" ? [...(u.methods?.keys() ?? [])] : [];
1606
+ const tName = this._assertTypeName(target) ?? goTypeName(expr.type);
1607
+ const src = this._assertTypeName(expr.expr?._type) ?? "interface {}";
1608
+ return `__tafail(${JSON.stringify(src)}, ${val}, ${JSON.stringify(tName)}, ${methods.length ? JSON.stringify(methods.sort()) : "null"})`;
1609
+ },
1610
+
1611
+ // Go's spelling of the static type in a failed assertion message; null for
1612
+ // `any` (`interface {}`).
1613
+ _assertTypeName(t) {
1614
+ if (!t || isAny(t)) return null;
1615
+ return t.name === "error" ? "error" : goTypeName(t);
1616
+ },
1617
+
1508
1618
  // `csv.ParseError` names the class of the bundled Go-source package type.
1509
1619
  _qualifiedStructName(name) {
1510
1620
  if (!name.includes(".") || this.structNames.has(name)) return name;
@@ -1512,20 +1622,37 @@ export const expressionGenMethods = {
1512
1622
  return cls && this.structNames.has(cls) ? cls : name;
1513
1623
  },
1514
1624
 
1625
+ _simdCheckExpr(name, val) {
1626
+ if (name === "simd.F32x4" || name === "F32x4")
1627
+ return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
1628
+ if (name === "simd.Mask32x4" || name === "Mask32x4")
1629
+ return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
1630
+ return null;
1631
+ },
1632
+
1515
1633
  _typeNameCheckExpr(typeName, val) {
1516
1634
  // A type parameter: the instantiated type's test travels with its factory.
1517
1635
  const factory = this._typeParamZeroFactory(typeName);
1518
1636
  if (factory) return `(${factory}?.is?.(${val}) ?? true)`;
1519
1637
  const name = this._qualifiedStructName(typeName);
1638
+ // int64 and uint64 share the BigInt: a value out of int64's range can
1639
+ // only be a uint64.
1640
+ if (name === "int64")
1641
+ return `(typeof ${val} === "bigint" && ${val} >= -9223372036854775808n && ${val} <= 9223372036854775807n)`;
1642
+ if (name === "uint64")
1643
+ return `(typeof ${val} === "bigint" && ${val} >= 0n)`;
1644
+ // Integer types share the JS number with the floats: only a whole
1645
+ // number is one, so a fraction or -0 in an `any` falls through to float64.
1646
+ if (INT_TYPE_NAMES.has(name))
1647
+ return `(Number.isSafeInteger(${val}) && !Object.is(${val}, -0))`;
1520
1648
  if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
1649
+ if (name === "complex128" || name === "complex64")
1650
+ return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.re === "number" && typeof ${val}.im === "number")`;
1521
1651
  if (name === "string") return `typeof ${val} === "string"`;
1522
1652
  if (name === "bool") return `typeof ${val} === "boolean"`;
1523
1653
  if (name === "nil") return `${val} === null`;
1524
- // F32x4 and Mask32x4 are both frozen 4-element arrays; lanes tell them apart.
1525
- if (name === "simd.F32x4" || name === "F32x4")
1526
- return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
1527
- if (name === "simd.Mask32x4" || name === "Mask32x4")
1528
- return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
1654
+ const simd = this._simdCheckExpr(name, val);
1655
+ if (simd) return simd;
1529
1656
  if (name === "error")
1530
1657
  return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
1531
1658
  if (this.structNames.has(name))
@@ -1534,12 +1661,16 @@ export const expressionGenMethods = {
1534
1661
  const namedInfo = this._namedBoxInfo(named);
1535
1662
  if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
1536
1663
  // User-declared interfaces: check the dynamic value has every method.
1537
- if (!name.includes(".") && named?.underlying?.kind === "interface")
1664
+ if (named?.underlying?.kind === "interface")
1538
1665
  return this._ifaceMethodCheck(
1539
1666
  val,
1540
1667
  [...named.underlying.methods.keys()],
1541
1668
  this._unwrapIsMulti(named.underlying.methods.get("Unwrap")),
1542
1669
  );
1670
+ if (this._isNamedComposite(named)) {
1671
+ this.useHelper("ifaceBox");
1672
+ return `__isty(${val}, ${JSON.stringify(name)}, "named")`;
1673
+ }
1543
1674
  return "true"; // unknown type — can't check at runtime
1544
1675
  },
1545
1676
 
@@ -1573,11 +1704,14 @@ export const expressionGenMethods = {
1573
1704
  },
1574
1705
 
1575
1706
  _ifaceMethodCheck(val, methodNames, multiUnwrap = null) {
1576
- const checks = methodNames.map((m) =>
1577
- m === "Unwrap" && multiUnwrap !== null
1578
- ? `typeof ${val}.${m} === "function" && (${val}.${m}.__multi === true) === ${multiUnwrap}`
1579
- : `typeof ${val}.${m} === "function"`,
1580
- );
1707
+ const checks = methodNames.map((m) => {
1708
+ const base =
1709
+ m === "Unwrap" && multiUnwrap !== null
1710
+ ? `typeof ${val}.${m} === "function" && (${val}.${m}.__multi === true) === ${multiUnwrap}`
1711
+ : `typeof ${val}.${m} === "function"`;
1712
+ // A struct value's method set lacks the pointer-receiver methods.
1713
+ return `${base} && !(${val}.__v === true && ${val}.constructor.__pm?.includes(${JSON.stringify(m)}))`;
1714
+ });
1581
1715
  return `(${[`${val} != null`, ...checks].join(" && ")})`;
1582
1716
  },
1583
1717
 
@@ -1586,6 +1720,18 @@ export const expressionGenMethods = {
1586
1720
  // for a type parameter).
1587
1721
  _assertedValueJs(typeNode, val) {
1588
1722
  if (this._isNamedBoxTypeNode(typeNode)) return `${val}.__gfUnbox()`;
1723
+ if (
1724
+ this._isStructPtrTypeNode(typeNode) ||
1725
+ (typeNode?.kind === "PointerType" && typeNode._assertType)
1726
+ ) {
1727
+ this.useHelper("ifaceBox");
1728
+ return `__unnil(${val})`;
1729
+ }
1730
+ if (typeNode?._assertType?.kind === "slice") {
1731
+ // A nil slice in an interface is a tagged empty array.
1732
+ this.useHelper("ifaceBox");
1733
+ return `__unnil(${val})`;
1734
+ }
1589
1735
  const factory =
1590
1736
  typeNode?.kind === "TypeName"
1591
1737
  ? this._typeParamZeroFactory(typeNode.name)
@@ -1593,6 +1739,19 @@ export const expressionGenMethods = {
1593
1739
  return factory ? `(${factory}?.u ? ${val}.__gfUnbox() : ${val})` : val;
1594
1740
  },
1595
1741
 
1742
+ _isStructPtrTypeNode(typeNode) {
1743
+ const base = typeNode?.kind === "PointerType" ? typeNode.base : null;
1744
+ const name =
1745
+ base?.kind === "TypeName"
1746
+ ? base.name
1747
+ : base?.kind === "GenericTypeName"
1748
+ ? this._dequalify(base.name)
1749
+ : null;
1750
+ return (
1751
+ name !== null && this.structNames.has(this._qualifiedStructName(name))
1752
+ );
1753
+ },
1754
+
1596
1755
  _isNamedBoxTypeNode(typeNode) {
1597
1756
  if (typeNode?.kind === "TypeName")
1598
1757
  return Boolean(
@@ -1630,11 +1789,10 @@ export const expressionGenMethods = {
1630
1789
  arg,
1631
1790
  );
1632
1791
  if (structConv) return structConv;
1633
- const isRaw =
1634
- targetType?.name === "json.RawMessage" ||
1635
- targetType?.name === "encoding/json.RawMessage";
1636
1792
  const base =
1637
1793
  targetType?.kind === "named" ? targetType.underlying : targetType;
1794
+ if (base?.kind === "map" && this._isNilLiteral(expr.args[0]))
1795
+ return this._nilMapZero();
1638
1796
  if (
1639
1797
  targetType?.kind === "named" &&
1640
1798
  base?.kind === "basic" &&
@@ -1651,19 +1809,29 @@ export const expressionGenMethods = {
1651
1809
  (elem === "byte" || elem === "uint8") &&
1652
1810
  this._isStringExprType(expr.args[0]?._type)
1653
1811
  ) {
1654
- const enc = `new TextEncoder().encode(${arg})`;
1655
- return isRaw ? `((u) => { u.__raw = true; return u; })(${enc})` : enc;
1656
- }
1657
- if (isRaw) {
1658
- return `((u) => { u.__raw = true; return u; })(${arg})`;
1812
+ this.useHelper("u8enc");
1813
+ return `__u8enc(${arg})`;
1659
1814
  }
1660
1815
  }
1661
1816
  return arg;
1662
1817
  },
1663
1818
 
1819
+ // `json.Marshal(v)` / `enc.Encode(v)` call the generated codec entry
1820
+ // (see src/lower/json-codecs.js); a stream method takes its receiver first.
1821
+ _jsonEntryCall(expr) {
1822
+ const sel = expr.func;
1823
+ const func = { kind: "Ident", name: sel._genEntry, _type: sel._type };
1824
+ const args = sel._jsonStream ? [sel.expr, ...expr.args] : expr.args;
1825
+ return { ...expr, func, args };
1826
+ },
1827
+
1664
1828
  genCall(expr) {
1829
+ if (expr.func._genEntry) return this.genCall(this._jsonEntryCall(expr));
1665
1830
  if (expr._isTypeConversion) return this._genTypeConversionCall(expr);
1666
1831
 
1832
+ const native = this._genNativeCall(expr);
1833
+ if (native !== null) return native;
1834
+
1667
1835
  const funcExpr =
1668
1836
  expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
1669
1837
  if (funcExpr.kind === "Ident") {
@@ -1671,11 +1839,17 @@ export const expressionGenMethods = {
1671
1839
  if (builtin !== undefined) return builtin;
1672
1840
  }
1673
1841
 
1842
+ const viaBox = this._genTypeParamMethodCall(expr);
1843
+ if (viaBox !== null) return viaBox;
1844
+
1674
1845
  if (expr.func.kind === "SelectorExpr") {
1675
1846
  const result = this._genSelectorCall(expr);
1676
1847
  if (result !== undefined) return result;
1677
1848
  }
1678
1849
 
1850
+ const converted = this._convertedPointerMethodCall(expr);
1851
+ if (converted) return converted;
1852
+
1679
1853
  const nilSafe = this._nilSafeMethodClass(expr.func);
1680
1854
  if (nilSafe) {
1681
1855
  // The method tests its receiver for nil: call it through the
@@ -1694,6 +1868,50 @@ export const expressionGenMethods = {
1694
1868
  return `${fn}(${args ? `${zeros}, ${args}` : zeros})`;
1695
1869
  },
1696
1870
 
1871
+ // `x.M(...)` on a type-parameter value: a raw value of a named non-struct
1872
+ // type argument is boxed (by the type's factory) to reach its methods.
1873
+ _genTypeParamMethodCall(expr) {
1874
+ if (!this._isTypeParamMethodCall(expr)) return null;
1875
+ const factory = this._typeParamZeroFactory(expr.func.expr._type.name);
1876
+ if (!factory) return null;
1877
+ this.useHelper("generics");
1878
+ const recv = `__tbox(${factory}, ${this.genExpr(expr.func.expr)})`;
1879
+ const args = this._genCallArgs(expr);
1880
+ return `${recv}.${expr.func.field}(${args})`;
1881
+ },
1882
+
1883
+ // `(*S)(p).M()` where `p` points at another struct type: the JS object keeps
1884
+ // its own class, so call S's prototype method on it.
1885
+ _convertedPointerMethodCall(expr) {
1886
+ const sel = expr.func;
1887
+ if (sel?.kind !== "SelectorExpr" || sel._isMethodExpr) return null;
1888
+ let recv = sel.expr;
1889
+ while (recv?.kind === "ParenExpr") recv = recv.expr;
1890
+ if (recv?._isTypeConversion !== true) return null;
1891
+ const target = recv._conversionTargetType;
1892
+ const from = recv.args?.[0]?._type;
1893
+ if (target?.kind !== "pointer" || target.base?.kind !== "named")
1894
+ return null;
1895
+ if (from?.kind !== "pointer" || from.base === target.base) return null;
1896
+ if (target.base.underlying?.kind !== "struct") return null;
1897
+ const cls = target.base.name;
1898
+ if (!target.base.methods?.has(sel.field)) return null;
1899
+ const rest = this._genCallArgs(expr);
1900
+ const obj = this.genExpr(recv.args[0]);
1901
+ return `${cls}.prototype.${sel.field}.call(${rest ? `${obj}, ${rest}` : obj})`;
1902
+ },
1903
+
1904
+ // The class whose prototype holds pointer-receiver method `field`, for a
1905
+ // pointer to a struct type that declares it directly (not promoted), so a
1906
+ // method value or expression can reach it with a nil receiver; else null.
1907
+ _ptrMethodProtoClass(ptr, field, sel) {
1908
+ if (ptr?.kind !== "pointer" || ptr.base?.kind !== "named") return null;
1909
+ if (ptr.base.underlying?.kind !== "struct") return null;
1910
+ if (sel && this._promotedPath(sel)) return null;
1911
+ if (!ptr.base.methods?.has(field)) return null;
1912
+ return ptr.base.name;
1913
+ },
1914
+
1697
1915
  // The class of a pointer-receiver struct method that accepts a nil receiver
1698
1916
  // when `func` selects it on a pointer of exactly that struct, else null.
1699
1917
  _nilSafeMethodClass(func) {
@@ -1702,7 +1920,15 @@ export const expressionGenMethods = {
1702
1920
  if (t?.kind !== "pointer" || t.base?.kind !== "named") return null;
1703
1921
  const cls = t.base.name;
1704
1922
  if (t.base.typeArgs?.length) return null;
1705
- return this.nilSafeMethods?.has(`${cls}.${func.field}`) ? cls : null;
1923
+ if (this.nilSafeMethods?.has(`${cls}.${func.field}`)) return cls;
1924
+ // A Go-source stdlib type (`Buffer$$bytes`) is declared outside this
1925
+ // package: its method decl tells whether it tests the receiver for nil.
1926
+ const decl = cls.includes("$$")
1927
+ ? t.base.methods?.get(func.field)?._methodDecl
1928
+ : null;
1929
+ return decl?.recvPointer && comparesToNil(decl.body, decl.recvName)
1930
+ ? cls
1931
+ : null;
1706
1932
  },
1707
1933
 
1708
1934
  // The leading zero-value factories of a call to a generic function
@@ -1741,79 +1967,7 @@ export const expressionGenMethods = {
1741
1967
  return recvType?.name ?? recvType?.base?.name;
1742
1968
  },
1743
1969
 
1744
- _genJsonStreamCall(recvName, expr) {
1745
- const isEnc = this._isJsonEncoderName(recvName);
1746
- if (isEnc && expr.func.field === "Encode") {
1747
- this.useHelper("json");
1748
- const enc = this.genExpr(expr.func.expr);
1749
- this._markJsonAnyEncoders();
1750
- const argType = expr.args[0]?._type;
1751
- const codec = this._getJsonEncoder(argType);
1752
- const arg = this.genExpr(expr.args[0]);
1753
- return `__json_encoder_encode(${enc}, ${codec}, ${arg})`;
1754
- }
1755
- const isDec =
1756
- recvName === "json.Decoder" ||
1757
- recvName === "encoding/json.Decoder" ||
1758
- recvName?.startsWith("Decoder$$");
1759
- if (isDec && expr.func.field === "Decode") {
1760
- this.useHelper("json");
1761
- const dec = this.genExpr(expr.func.expr);
1762
- const targetType = expr.args[0]?._type;
1763
- const codec = this._getJsonDecoder(targetType);
1764
- const arg = this.genExpr(expr.args[0]);
1765
- return `__json_decoder_decode(${dec}, ${codec}, ${arg}, ${this._jsonTargetArgs(targetType)})`;
1766
- }
1767
- return undefined;
1768
- },
1769
-
1770
1970
  _genReceiverTypeCall(recvName, expr) {
1771
- const jsonStream = this._genJsonStreamCall(recvName, expr);
1772
- if (jsonStream !== undefined) return jsonStream;
1773
- if (recvName === "strings.Builder" || recvName === "bytes.Buffer")
1774
- return this._genBuilderCall(recvName, expr.func.field, expr);
1775
- if (recvName === "regexp.Regexp")
1776
- return this._genRegexpMethodCall(expr.func.field, expr);
1777
- if (recvName === "time.Time")
1778
- return this._genTimeMethodCall(expr.func.field, expr);
1779
- if (recvName === "time.Duration")
1780
- return this._genDurationMethodCall(expr.func.field, expr);
1781
- if (recvName === "time.Month") {
1782
- this.useHelper("duration");
1783
- return `__monthString(${this.genExpr(expr.func.expr)})`;
1784
- }
1785
- if (recvName === "time.Weekday") {
1786
- this.useHelper("duration");
1787
- return `__weekdayString(${this.genExpr(expr.func.expr)})`;
1788
- }
1789
- if (recvName === "time.Location") {
1790
- const recv = this.genExpr(expr.func.expr);
1791
- return `(${recv} ? ${recv}.String() : "UTC")`;
1792
- }
1793
- if (recvName === "time.Timer") {
1794
- const recv = this.genExpr(expr.func.expr);
1795
- if (expr.func.field === "Stop")
1796
- return `(${recv} ? ${recv}.Stop() : false)`;
1797
- if (expr.func.field === "Reset")
1798
- return `(${recv} ? ${recv}.Reset(${this.genExpr(expr.args[0])}) : false)`;
1799
- }
1800
- if (
1801
- recvName === "testing.T" ||
1802
- recvName === "testing.B" ||
1803
- recvName === "testing.M" ||
1804
- recvName === "testing.TB"
1805
- )
1806
- return this._genTestingMethodCall(expr.func.field, expr);
1807
- if (recvName === "strings.Replacer") {
1808
- const recv = this.genExpr(expr.func.expr);
1809
- if (expr.func.field === "WriteString") {
1810
- const w = this.genExpr(expr.args[0]);
1811
- const s = this.genExpr(expr.args[1]);
1812
- return `((r,w,s) => { s = r ? r.Replace(s) : s; const x = w?.value ?? w; if (typeof x.WriteString === "function") return x.WriteString(s); if (typeof x._buf === "string") { x._buf += s; return [s.length, null]; } if (Array.isArray(x._buf)) { const bytes = new TextEncoder().encode(s); for (let i = 0; i < bytes.length; i++) x._buf.push(bytes[i]); return [bytes.length, null]; } if (typeof x.Write === "function") return x.Write(new TextEncoder().encode(s)); return [0, "strings.Replacer.WriteString: unsupported writer"]; })(${recv}, ${w}, ${s})`;
1813
- }
1814
- const arg = this.genExpr(expr.args[0]);
1815
- return `(${recv} ? ${recv}.Replace(${arg}) : ${arg})`;
1816
- }
1817
1971
  if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
1818
1972
  const args = expr.args.map((a) => this.genExpr(a)).join(", ");
1819
1973
  return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
@@ -1842,28 +1996,6 @@ export const expressionGenMethods = {
1842
1996
  }
1843
1997
  if (expr.func.field === "Wait") return "(void 0)";
1844
1998
  }
1845
- if (
1846
- recvName === "binary.LittleEndian" ||
1847
- recvName === "binary.BigEndian" ||
1848
- recvName === "encoding/binary.LittleEndian" ||
1849
- recvName === "encoding/binary.BigEndian" ||
1850
- recvName === "binary.ByteOrder" ||
1851
- recvName === "encoding/binary.ByteOrder"
1852
- ) {
1853
- this.useHelper("binary");
1854
- const recv = this.genExpr(expr.func.expr);
1855
- const args = expr.args.map((a) => this.genExpr(a)).join(", ");
1856
- return `${recv}.${expr.func.field}(${args})`;
1857
- }
1858
- if (
1859
- recvName === "base64.Encoding" ||
1860
- recvName === "encoding/base64.Encoding"
1861
- ) {
1862
- this.useHelper("base64");
1863
- const recv = this.genExpr(expr.func.expr);
1864
- const args = expr.args.map((a) => this.genExpr(a)).join(", ");
1865
- return `${recv}.${expr.func.field}(${args})`;
1866
- }
1867
1999
  return undefined;
1868
2000
  },
1869
2001
 
@@ -1889,12 +2021,44 @@ export const expressionGenMethods = {
1889
2021
  const ns = expr.func.expr.name;
1890
2022
  const fn = expr.func.field;
1891
2023
  const result = this._genStdlibCall(ns, fn, expr);
1892
- if (result !== undefined) return result;
2024
+ if (result !== undefined) {
2025
+ if (this._is64BitInt(expr._type)) {
2026
+ return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${result})`;
2027
+ }
2028
+ if (
2029
+ expr._type?.kind === "tuple" &&
2030
+ expr._type.types.some((t) => this._is64BitInt(t))
2031
+ ) {
2032
+ const casts = expr._type.types.map((t, i) =>
2033
+ this._is64BitInt(t)
2034
+ ? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
2035
+ : `__r[${i}]`,
2036
+ );
2037
+ return `((__r) => [${casts.join(", ")}])(${result})`;
2038
+ }
2039
+ return result;
2040
+ }
1893
2041
  }
1894
2042
  const recvType = expr.func.expr._type;
1895
2043
  const recvName = this._resolveRecvName(recvType);
1896
2044
  const typed = this._genReceiverTypeCall(recvName, expr);
1897
- if (typed !== undefined) return typed;
2045
+ if (typed !== undefined) {
2046
+ if (this._is64BitInt(expr._type)) {
2047
+ return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${typed})`;
2048
+ }
2049
+ if (
2050
+ expr._type?.kind === "tuple" &&
2051
+ expr._type.types.some((t) => this._is64BitInt(t))
2052
+ ) {
2053
+ const casts = expr._type.types.map((t, i) =>
2054
+ this._is64BitInt(t)
2055
+ ? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
2056
+ : `__r[${i}]`,
2057
+ );
2058
+ return `((__r) => [${casts.join(", ")}])(${typed})`;
2059
+ }
2060
+ return typed;
2061
+ }
1898
2062
  const namedCall = this._genNamedMethodCall(expr);
1899
2063
  if (namedCall !== undefined) return namedCall;
1900
2064
  if (this.strict && this._isInterfaceType(recvType)) {
@@ -1910,6 +2074,16 @@ export const expressionGenMethods = {
1910
2074
  return undefined;
1911
2075
  },
1912
2076
 
2077
+ // `len`/`cap` of an array or array pointer is a constant, but an operand
2078
+ // containing a call or receive is still evaluated (and a nil pointer is
2079
+ // not dereferenced).
2080
+ _genConstLen(expr) {
2081
+ const arg = expr.args[0];
2082
+ if (arg && hasCallOrReceive(arg))
2083
+ return `(${this.genExpr(arg)}, ${expr._constLen})`;
2084
+ return String(expr._constLen);
2085
+ },
2086
+
1913
2087
  _isInterfaceType(t) {
1914
2088
  if (!t) return false;
1915
2089
  if (t.kind === "interface") return true;
@@ -1917,13 +2091,13 @@ export const expressionGenMethods = {
1917
2091
  },
1918
2092
 
1919
2093
  _genBuiltinLen(expr) {
1920
- if (expr._constLen != null) return String(expr._constLen);
2094
+ if (expr._constLen != null) return this._genConstLen(expr);
1921
2095
  const arg = expr.args[0];
1922
2096
  const t = arg?._type;
1923
2097
  const wrapField = this._namedWrapperField(t, arg);
1924
2098
  if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
1925
2099
  const js = this.genExpr(arg);
1926
- if (t?.kind === "map") return `Object.keys(${js}).length`;
2100
+ if (t?.kind === "map") return `Object.keys(${js} ?? {}).length`;
1927
2101
  this.useHelper("len");
1928
2102
  return `__len(${js})`;
1929
2103
  },
@@ -1954,9 +2128,9 @@ export const expressionGenMethods = {
1954
2128
  // contributes its UTF-8 bytes.
1955
2129
  _genSliceSource(node) {
1956
2130
  const js = this.genExpr(node);
1957
- return this._isStringExprType(node._type)
1958
- ? `new TextEncoder().encode(${js})`
1959
- : js;
2131
+ if (!this._isStringExprType(node._type)) return js;
2132
+ this.useHelper("u8enc");
2133
+ return `__u8enc(${js})`;
1960
2134
  },
1961
2135
 
1962
2136
  genAppend(expr) {
@@ -1981,12 +2155,23 @@ export const expressionGenMethods = {
1981
2155
  const st = expr._type ?? expr.args[0]._type;
1982
2156
  const elemT = (st?.kind === "named" ? st.underlying : st)?.elem;
1983
2157
  const spreadOf = (a) =>
1984
- this._aggregateBase(elemT)?.kind === "array"
2158
+ this._aggregateBase(elemT)
1985
2159
  ? `...(${this.genExpr(a)} ?? []).map((__e) => ${this._cloneJs(elemT, "__e")})`
1986
2160
  : `...(${this._genSliceSource(a)} ?? [])`;
1987
- const elems = expr.args
1988
- .slice(1)
1989
- .map((a) => (a._spread ? spreadOf(a) : this.genValueExpr(a)));
2161
+ const is64Elem = this._is64BitInt(elemT);
2162
+ const elems = expr.args.slice(1).map((a) => {
2163
+ if (a._spread) return spreadOf(a);
2164
+ const val = this.genValueExpr(a);
2165
+ if (
2166
+ is64Elem &&
2167
+ a._type?.kind === "untyped" &&
2168
+ !val.endsWith("n") &&
2169
+ /^-?\d+$/.test(val)
2170
+ ) {
2171
+ return `${val}n`;
2172
+ }
2173
+ return val;
2174
+ });
1990
2175
  if (elems.length === 0) return slice;
1991
2176
  this.useHelper("append");
1992
2177
  const sliceType = expr._type ?? expr.args[0]._type;
@@ -1995,11 +2180,35 @@ export const expressionGenMethods = {
1995
2180
  (sliceType.kind === "named" ? sliceType.underlying : sliceType).elem,
1996
2181
  )
1997
2182
  : null;
1998
- if (typedCtor)
1999
- return `__append((${slice}) ?? new ${typedCtor}(0), ${elems.join(", ")})`;
2183
+ if (typedCtor) {
2184
+ const single = expr.args.length === 2 && expr.args[1]._spread;
2185
+ // One source slice or string: append it as it is, not spread.
2186
+ if (single)
2187
+ return `__appendv((${slice}) ?? __tn(${typedCtor}), ${this._genSliceSource(expr.args[1])} ?? [])`;
2188
+ return `__append((${slice}) ?? __tn(${typedCtor}), ${elems.join(", ")})`;
2189
+ }
2190
+ // A json codec grows a slice it has just made and no one else holds:
2191
+ // pushing in place is O(1), where __append copies the whole slice.
2192
+ if (
2193
+ this._inJsonCodecFile() &&
2194
+ expr.args.length === 2 &&
2195
+ expr.args[0].kind === "Ident" &&
2196
+ !expr.args[1]._spread
2197
+ ) {
2198
+ this.useHelper("pushs");
2199
+ return `__pushs(${slice}, ${elems[0]})`;
2200
+ }
2000
2201
  return `__append(${slice}, ${elems.join(", ")})`;
2001
2202
  },
2002
2203
 
2204
+ // A `make` length or capacity expression wrapped in its negative-value
2205
+ // check (a literal needs none).
2206
+ _guardMakeSize(code, guard) {
2207
+ if (/^\d+$/.test(code)) return code;
2208
+ this.useHelper("mklen");
2209
+ return `${guard}(${code})`;
2210
+ },
2211
+
2003
2212
  genMake(expr) {
2004
2213
  // make([]T, n) or make([]T, n, cap) → new Array(n).fill(zero)
2005
2214
  // make(map[K]V) → {}
@@ -2007,12 +2216,17 @@ export const expressionGenMethods = {
2007
2216
  const typeNode = typeArg.kind === "TypeExpr" ? typeArg.type : typeArg;
2008
2217
  // A defined type (`type S []int`) makes like its underlying type.
2009
2218
  const madeType =
2010
- typeArg._type?.kind === "named"
2219
+ typeArg._coreType ??
2220
+ (typeArg._type?.kind === "named"
2011
2221
  ? typeArg._type.underlying
2012
- : typeArg._type;
2222
+ : typeArg._type);
2013
2223
  const resolvedKind = madeType?.kind;
2014
2224
  if (typeNode.kind === "SliceType" || resolvedKind === "slice") {
2015
- const n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
2225
+ let n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
2226
+ if (expr.args[1] && this._is64BitInt(expr.args[1]._type))
2227
+ n = `Number(${n})`;
2228
+ // A negative length or capacity panics with Go's message.
2229
+ n = this._guardMakeSize(n, "__mkn");
2016
2230
  const elemNode = typeNode.kind === "SliceType" ? typeNode.elem : null;
2017
2231
  const elemResolved = madeType?.kind === "slice" ? madeType.elem : null;
2018
2232
  const typedCtor =
@@ -2025,8 +2239,13 @@ export const expressionGenMethods = {
2025
2239
  : null);
2026
2240
  // make([]T, n, cap) allocates cap elements and returns a view of
2027
2241
  // the first n (`__mkslice`), so append within cap shares them.
2028
- const capArg = expr.args[2] ? this.genExpr(expr.args[2]) : null;
2029
- if (capArg) this.useHelper("slice");
2242
+ let capArg = expr.args[2] ? this.genExpr(expr.args[2]) : null;
2243
+ if (capArg && expr.args[2] && this._is64BitInt(expr.args[2]._type))
2244
+ capArg = `Number(${capArg})`;
2245
+ if (capArg) {
2246
+ this.useHelper("slice");
2247
+ capArg = this._guardMakeSize(capArg, "__mkc");
2248
+ }
2030
2249
  const withCap = (alloc) =>
2031
2250
  capArg ? `__mkslice(${n}, ${alloc(capArg)})` : alloc(n);
2032
2251
  if (typedCtor) {
@@ -2045,24 +2264,18 @@ export const expressionGenMethods = {
2045
2264
  }
2046
2265
  return withCap((k) => `new Array(${k}).fill(${zero})`);
2047
2266
  }
2048
- // map or fallback
2267
+ // map or fallback. The json codecs' maps hold decoded keys, among them
2268
+ // "__proto__": without a prototype that is an ordinary key.
2269
+ if (
2270
+ this._inJsonCodecFile() &&
2271
+ (resolvedKind === "map" || typeNode.kind === "MapType")
2272
+ )
2273
+ return "Object.create(null)";
2049
2274
  return "{}";
2050
2275
  },
2051
2276
 
2052
- // Render struct elements as a JS field list string.
2053
- // Handles positional (_positionalField), embedded-spread (_isEmbedInit), and keyed (KeyValueExpr).
2054
- _genStructFields(elems) {
2055
- return elems
2056
- .map((e) => {
2057
- if (e._positionalField) {
2058
- return `${e._positionalField}: ${this.genExpr(e)}`;
2059
- }
2060
- if (e.kind !== "KeyValueExpr") return null;
2061
- if (e._isEmbedInit) return `...${this.genExpr(e.value)}`;
2062
- return `${e.key.name ?? this.genExpr(e.key)}: ${this.genExpr(e.value)}`;
2063
- })
2064
- .filter((s) => s !== null)
2065
- .join(", ");
2277
+ _inJsonCodecFile() {
2278
+ return this._jsonCodecFileIdx === (this._currentSrcFileIdx ?? 0);
2066
2279
  },
2067
2280
 
2068
2281
  _isSliceOrArrayType(t) {
@@ -2322,10 +2535,6 @@ export const expressionGenMethods = {
2322
2535
  const t = expr.typeExpr;
2323
2536
  if (t === null) return this._genImplicitCompositeLit(expr);
2324
2537
  const typeName = this.getTypeName(t);
2325
- if (typeName === "bytes.Buffer" || typeName === "Buffer")
2326
- return "{ _buf: [] }";
2327
- if (typeName === "strings.Builder" || typeName === "Builder")
2328
- return '{ _buf: "" }';
2329
2538
  if (typeName === "simd.Mask32x4" || typeName === "Mask32x4")
2330
2539
  return "Object.freeze([false, false, false, false])";
2331
2540
  if (typeName === "simd.F32x4" || typeName === "F32x4") {
@@ -2445,26 +2654,44 @@ export const expressionGenMethods = {
2445
2654
  // ── Helpers ───────────────────────────────────────────────────
2446
2655
 
2447
2656
  _genComplexOperand(expr) {
2448
- if (isComplex(expr._type)) return this.genExpr(expr);
2657
+ if (isComplex(expr._type) || expr._asComplex) return this.genExpr(expr);
2449
2658
  return `{ re: ${this.genExpr(expr)}, im: 0 }`;
2450
2659
  },
2451
2660
 
2452
2661
  getTypeName(typeNode) {
2453
2662
  if (!typeNode) return null;
2454
2663
  if (typeNode._stdName) return typeNode._stdName;
2455
- if (typeNode.kind === "TypeName") return this._dequalify(typeNode.name);
2664
+ if (typeNode.kind === "TypeName")
2665
+ return this._aliasTarget(this._dequalify(typeNode.name));
2456
2666
  if (typeNode.kind === "GenericTypeName") return typeNode.name;
2457
- if (typeNode.kind === "Ident") return typeNode.name;
2667
+ if (typeNode.kind === "Ident") return this._aliasTarget(typeNode.name);
2458
2668
  if (typeNode.kind === "InstantiationExpr")
2459
2669
  return this.getTypeName(typeNode.expr);
2460
2670
  if (typeNode.kind === "SelectorExpr") {
2461
2671
  const base = this.getTypeName(typeNode.expr);
2462
- if (this.bundledPackages.has(base)) return typeNode.field;
2672
+ if (this.bundledPackages.has(base)) {
2673
+ // `p.A` for `type A = Base` in p: no class is emitted for A.
2674
+ const t = this.checker?.types.get(`${base}.${typeNode.field}`);
2675
+ return t?.kind === "named" && t.name && !t.name.includes("$$")
2676
+ ? t.name
2677
+ : typeNode.field;
2678
+ }
2463
2679
  return `${base}.${typeNode.field}`;
2464
2680
  }
2465
2681
  return null;
2466
2682
  },
2467
2683
 
2684
+ // `type A = Base`: no class is emitted for A, so it names Base's.
2685
+ _aliasTarget(name) {
2686
+ const t = this.checker?.types.get(name);
2687
+ return t?.kind === "named" &&
2688
+ t.name &&
2689
+ t.name !== name &&
2690
+ !name.includes(".")
2691
+ ? t.name
2692
+ : name;
2693
+ },
2694
+
2468
2695
  // Bundled GoFront packages are inlined, so `pkg.T` names the class `T`.
2469
2696
  _dequalify(name) {
2470
2697
  const dot = name.indexOf(".");
@@ -2514,6 +2741,16 @@ export const expressionGenMethods = {
2514
2741
  },
2515
2742
 
2516
2743
  // Runs `fn` with type parameters `names` bound to the factories `$z_<name>`.
2744
+ _withoutNatives(fn) {
2745
+ const saved = this.natives;
2746
+ this.natives = false;
2747
+ try {
2748
+ return fn();
2749
+ } finally {
2750
+ this.natives = saved;
2751
+ }
2752
+ },
2753
+
2517
2754
  _withTypeParamZeros(names, fn) {
2518
2755
  const prev = this._typeParamZeros;
2519
2756
  this._typeParamZeros = names
@@ -2534,6 +2771,7 @@ export const expressionGenMethods = {
2534
2771
  if (bound) return bound;
2535
2772
  }
2536
2773
  this.useHelper("generics");
2774
+ if (this._is64BitUnsigned(t)) return "__zu64";
2537
2775
  const zero = this.zeroValueForType(t);
2538
2776
  return Object.hasOwn(SHARED_ZERO_FACTORIES, zero)
2539
2777
  ? SHARED_ZERO_FACTORIES[zero]
@@ -2722,6 +2960,8 @@ export const expressionGenMethods = {
2722
2960
  return this._typeNameCheckExpr(typeNode.name, val);
2723
2961
  const generic = this._genericInstanceCheck(typeNode, val);
2724
2962
  if (generic) return generic;
2963
+ const dynamic = this._dynTypeCheck(typeNode, val);
2964
+ if (dynamic) return dynamic;
2725
2965
  const extended = this._namedOrIfaceCheckExpr(typeNode, val);
2726
2966
  if (extended) return extended;
2727
2967
  if (
@@ -2821,6 +3061,9 @@ export const expressionGenMethods = {
2821
3061
  const nilMap = this._genNilMapCompare(expr);
2822
3062
  if (nilMap) return nilMap;
2823
3063
 
3064
+ const anyNil = this._genAnyNilCompare(expr);
3065
+ if (anyNil) return anyNil;
3066
+
2824
3067
  const tpEq = this._genTypeParamEquality(expr);
2825
3068
  if (tpEq) return tpEq;
2826
3069
 
@@ -2833,12 +3076,85 @@ export const expressionGenMethods = {
2833
3076
  const ptrCmp = this._genPointerEquality(expr);
2834
3077
  if (ptrCmp !== null) return ptrCmp;
2835
3078
 
2836
- if (this.strict && this._needsStrictOp(expr.op, expr._type)) {
3079
+ if (this._is64BitInt(expr._type)) return this._gen64BitBinaryExpr(expr);
3080
+
3081
+ if (
3082
+ (this.strict || this._isFloat32(expr._type)) &&
3083
+ this._needsStrictOp(expr.op, expr._type)
3084
+ ) {
2837
3085
  const l = this.genExpr(expr.left);
2838
3086
  const r = this.genExpr(expr.right);
2839
- return this._genStrictBinary(expr.op, l, r, expr._type);
3087
+ return this._genStrictBinary(expr.op, l, r, expr._type, expr.right._type);
2840
3088
  }
2841
3089
 
3090
+ return this._genStandardBinaryExpr(expr);
3091
+ },
3092
+
3093
+ _gen64BitBinaryExpr(expr) {
3094
+ let l = this.genExpr(expr.left);
3095
+ let r = this.genExpr(expr.right);
3096
+ // An untyped operand is a literal (`3` -> `3n`) or a named constant,
3097
+ // which holds a Number.
3098
+ const lit = (js) => (/^-?\d+$/.test(js) ? `${js}n` : `BigInt(${js})`);
3099
+ if (expr.left._type?.kind === "untyped" && !l.endsWith("n")) l = lit(l);
3100
+ else if (!this._is64BitInt(expr.left?._type)) l = `BigInt(${l})`;
3101
+ if (expr.right._type?.kind === "untyped" && !r.endsWith("n")) r = lit(r);
3102
+ else if (!this._is64BitInt(expr.right?._type)) r = `BigInt(${r})`;
3103
+ return this._gen64BitBinary(expr.op, l, r, expr._type);
3104
+ },
3105
+
3106
+ _tconvBinaryOperands(expr, l, r) {
3107
+ if (
3108
+ expr.left._type?.kind === "typeParam" &&
3109
+ expr.right._type?.kind === "untyped"
3110
+ ) {
3111
+ const factory = this._typeParamZeroFactory(expr.left._type.name);
3112
+ if (factory) {
3113
+ this.useHelper("generics");
3114
+ r = `__tconv(${factory}, ${r})`;
3115
+ }
3116
+ } else if (
3117
+ expr.right._type?.kind === "typeParam" &&
3118
+ expr.left._type?.kind === "untyped"
3119
+ ) {
3120
+ const factory = this._typeParamZeroFactory(expr.right._type.name);
3121
+ if (factory) {
3122
+ this.useHelper("generics");
3123
+ l = `__tconv(${factory}, ${l})`;
3124
+ }
3125
+ }
3126
+ return [l, r];
3127
+ },
3128
+
3129
+ _coerceUntyped64(expr, l, r, isLeft64, isRight64) {
3130
+ if (isLeft64 && expr.right?._type?.kind === "untyped") {
3131
+ if (!r.endsWith("n") && /^-?\d+$/.test(r)) return [l, `${r}n`];
3132
+ } else if (isRight64 && expr.left?._type?.kind === "untyped") {
3133
+ if (!l.endsWith("n") && /^-?\d+$/.test(l)) return [`${l}n`, r];
3134
+ }
3135
+ return [l, r];
3136
+ },
3137
+
3138
+ _coerceMixed64(expr, l, r, isLeft64, isRight64) {
3139
+ const isShift = expr.op === "<<" || expr.op === ">>";
3140
+ if (isLeft64 && !isRight64 && !isShift) r = `BigInt(${r})`;
3141
+ else if (isRight64 && !isLeft64 && !isShift) l = `BigInt(${l})`;
3142
+ if (isShift && isRight64) r = `Number(${r})`;
3143
+ return [l, r];
3144
+ },
3145
+
3146
+ _wrapBinaryResult(expr, res, isLeft64, isRight64) {
3147
+ const isCmp =
3148
+ expr.op === "==" ||
3149
+ expr.op === "!=" ||
3150
+ expr.op === "<" ||
3151
+ expr.op === "<=" ||
3152
+ expr.op === ">" ||
3153
+ expr.op === ">=";
3154
+ return (isLeft64 || isRight64) && !isCmp ? `Number(${res})` : res;
3155
+ },
3156
+
3157
+ _genStandardBinaryExpr(expr) {
2842
3158
  const jsOp = this._emittedBinaryOp(expr) ?? "/";
2843
3159
  const operand = (child, isRight) =>
2844
3160
  wrapForJsOp(
@@ -2847,17 +3163,29 @@ export const expressionGenMethods = {
2847
3163
  jsOp,
2848
3164
  isRight,
2849
3165
  );
2850
- const l = operand(expr.left, false);
3166
+ let [l, r] = this._tconvBinaryOperands(
3167
+ expr,
3168
+ operand(expr.left, false),
3169
+ operand(expr.right, true),
3170
+ );
3171
+ const isLeft64 = this._is64BitInt(expr.left?._type);
3172
+ const isRight64 = this._is64BitInt(expr.right?._type);
3173
+ [l, r] = this._coerceUntyped64(expr, l, r, isLeft64, isRight64);
3174
+ [l, r] = this._coerceMixed64(expr, l, r, isLeft64, isRight64);
2851
3175
 
2852
3176
  if (expr.op === "&^") {
2853
3177
  const rCode = this.genExpr(expr.right);
2854
- const r =
3178
+ const rP =
2855
3179
  this._emittedBinaryOp(expr.right) !== null ? `(${rCode})` : rCode;
2856
- return `${l} & ~${r}`;
3180
+ return this._wrapBinaryResult(expr, `${l} & ~${rP}`, isLeft64, isRight64);
2857
3181
  }
2858
- const r = operand(expr.right, true);
2859
3182
  if (this._isIntDivision(expr)) return `Math.trunc(${l} / ${r})`;
2860
- return `${l} ${jsOp} ${r}`;
3183
+ return this._wrapBinaryResult(
3184
+ expr,
3185
+ `${l} ${jsOp} ${r}`,
3186
+ isLeft64,
3187
+ isRight64,
3188
+ );
2861
3189
  },
2862
3190
 
2863
3191
  // `m == nil` / `m != nil` on a map: true for a nil map (null, the strict
@@ -2872,7 +3200,7 @@ export const expressionGenMethods = {
2872
3200
  if (!other) return null;
2873
3201
  const t =
2874
3202
  other._type?.kind === "named" ? other._type.underlying : other._type;
2875
- return t?.kind === "map" ? other : null;
3203
+ return t?.kind === "map" || other._coreType?.kind === "map" ? other : null;
2876
3204
  },
2877
3205
 
2878
3206
  _genNilMapCompare(expr) {
@@ -2937,6 +3265,25 @@ export const expressionGenMethods = {
2937
3265
  );
2938
3266
  },
2939
3267
 
3268
+ // The `any` operand of `x == nil` / `x != nil` (JS documents a nil slice
3269
+ // stored in an `any` as equal to nil, though it keeps a type tag), or null.
3270
+ _anyNilOperand(expr) {
3271
+ if (expr.op !== "==" && expr.op !== "!=") return null;
3272
+ const operand = this._isNilLiteral(expr.right)
3273
+ ? expr.left
3274
+ : this._isNilLiteral(expr.left)
3275
+ ? expr.right
3276
+ : null;
3277
+ return operand && this._isIfaceType(operand._type) ? operand : null;
3278
+ },
3279
+
3280
+ _genAnyNilCompare(expr) {
3281
+ const operand = this._anyNilOperand(expr);
3282
+ if (!operand) return null;
3283
+ const test = `((__a) => __a == null || __a.__nils === true)(${this.genExpr(operand)})`;
3284
+ return expr.op === "==" ? test : `!${test}`;
3285
+ },
3286
+
2940
3287
  _isNilLiteral(expr) {
2941
3288
  return (
2942
3289
  (expr.kind === "Ident" && expr.name === "nil") ||
@@ -2955,6 +3302,7 @@ export const expressionGenMethods = {
2955
3302
  return null;
2956
3303
  if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
2957
3304
  if (this._nilMapOperand(expr)) return null;
3305
+ if (this._anyNilOperand(expr)) return null;
2958
3306
  if (this._isIfaceEquality(expr)) return null;
2959
3307
  if (expr.op === "&^") return "&";
2960
3308
  if (expr.op === "==" || expr.op === "!=") {
@@ -2985,6 +3333,10 @@ export const expressionGenMethods = {
2985
3333
  return this._genArrayTypeConversion(expr, inner, t);
2986
3334
  if (t?.kind === "SliceType")
2987
3335
  return this._genSliceTypeConversion(expr, inner, t);
3336
+ // `map[K]V(nil)` is a nil map value, which stays distinct from the nil
3337
+ // interface once boxed in an `any`.
3338
+ if (t?.kind === "MapType" && this._isNilLiteral(expr.expr))
3339
+ return this._nilMapZero();
2988
3340
  const target = t?.name;
2989
3341
  const structConv = this._genStructClassConversion(
2990
3342
  target,
@@ -2995,6 +3347,7 @@ export const expressionGenMethods = {
2995
3347
  if (target === "error") {
2996
3348
  // A boxed named value that implements error is already an error.
2997
3349
  if (this._isNamedBoxKind(expr.expr)) return inner;
3350
+ if (!this._isStringExprType(expr.expr._type)) return inner;
2998
3351
  this.useHelper("error");
2999
3352
  return `__error(${inner})`;
3000
3353
  }
@@ -3025,11 +3378,25 @@ export const expressionGenMethods = {
3025
3378
  // `[]T(nil)` is the nil slice, not a copy of anything.
3026
3379
  if (this._isNilLiteral(expr.expr)) return "null";
3027
3380
  const elem = t.elem?.name;
3381
+ // Converting a nil slice yields a nil slice.
3382
+ const src = this._underlyingOf(expr.expr._type);
3383
+ if (src?.kind === "slice" && !this._sliceConvNilSafe) {
3384
+ this._sliceConvNilSafe = true;
3385
+ try {
3386
+ return `((__s) => __s == null ? null : ${this._genSliceTypeConversion(expr, "__s", t)})(${inner})`;
3387
+ } finally {
3388
+ this._sliceConvNilSafe = false;
3389
+ }
3390
+ }
3028
3391
  if (elem === "byte" || elem === "uint8") {
3029
- if (this._isStringExprType(expr.expr._type))
3030
- return `new TextEncoder().encode(${inner})`;
3031
- if (isAny(expr.expr._type) || !expr.expr._type)
3032
- return `(typeof ${inner} === "string" ? new TextEncoder().encode(${inner}) : new Uint8Array(${inner}))`;
3392
+ if (this._isStringExprType(expr.expr._type)) {
3393
+ this.useHelper("u8enc");
3394
+ return `__u8enc(${inner})`;
3395
+ }
3396
+ if (isAny(expr.expr._type) || !expr.expr._type) {
3397
+ this.useHelper("u8enc");
3398
+ return `(typeof ${inner} === "string" ? __u8enc(${inner}) : new Uint8Array(${inner}))`;
3399
+ }
3033
3400
  return `new Uint8Array(${inner})`;
3034
3401
  }
3035
3402
  const typedCtor = typedArrayConstructorForElem(
@@ -3048,41 +3415,71 @@ export const expressionGenMethods = {
3048
3415
 
3049
3416
  _genStringConversion(srcType, inner) {
3050
3417
  const base = srcType?.kind === "named" ? srcType.underlying : srcType;
3051
- if (base && this.isIntType(base)) return `String.fromCodePoint(${inner})`;
3418
+ if (base && this.isIntType(base)) {
3419
+ this.useHelper("runestr");
3420
+ return `__rstr(${inner})`;
3421
+ }
3052
3422
  if (base?.kind === "slice") {
3053
3423
  const elem = base.elem?.name;
3054
- if (elem === "byte" || elem === "uint8")
3055
- return `new TextDecoder().decode(new Uint8Array(${inner}))`;
3056
- if (elem === "rune" || elem === "int" || elem === "int32")
3057
- return `Array.from(${inner}, c => String.fromCodePoint(c)).join("")`;
3424
+ if (elem === "byte" || elem === "uint8") {
3425
+ this.useHelper("u8dec");
3426
+ return `__u8dec(${inner})`;
3427
+ }
3428
+ if (elem === "rune" || elem === "int" || elem === "int32") {
3429
+ this.useHelper("runestr");
3430
+ return `Array.from(${inner}, c => __rstr(c)).join("")`;
3431
+ }
3058
3432
  }
3059
3433
  return `String(${inner})`;
3060
3434
  },
3061
3435
 
3062
3436
  _genPrimitiveConversion(expr, inner, target) {
3437
+ const srcType = expr.expr?._type;
3438
+ const isSrc64 = this._is64BitInt(srcType);
3063
3439
  switch (target) {
3064
3440
  case "string":
3065
- return this._genStringConversion(expr.expr._type, inner);
3441
+ return this._genStringConversion(
3442
+ srcType,
3443
+ isSrc64 ? `Number(${inner})` : inner,
3444
+ );
3445
+ case "int64": {
3446
+ this.useHelper("to64");
3447
+ return `__to_i64(${inner})`;
3448
+ }
3449
+ case "uint64": {
3450
+ this.useHelper("to64");
3451
+ return `__to_u64(${inner})`;
3452
+ }
3066
3453
  case "int":
3067
- case "int64":
3068
3454
  case "uint":
3069
- case "uint64":
3070
3455
  case "uintptr":
3071
- return `Math.trunc(Number(${inner}))`;
3456
+ return isSrc64 ? `Number(${inner})` : `Math.trunc(Number(${inner}))`;
3072
3457
  case "int8":
3073
- return `(Number(${inner}) << 24 >> 24)`;
3458
+ return isSrc64
3459
+ ? `Number(BigInt.asIntN(8, ${inner}))`
3460
+ : `(Number(${inner}) << 24 >> 24)`;
3074
3461
  case "int16":
3075
- return `(Number(${inner}) << 16 >> 16)`;
3462
+ return isSrc64
3463
+ ? `Number(BigInt.asIntN(16, ${inner}))`
3464
+ : `(Number(${inner}) << 16 >> 16)`;
3076
3465
  case "int32":
3077
3466
  case "rune":
3078
- return `(Number(${inner}) | 0)`;
3467
+ return isSrc64
3468
+ ? `Number(BigInt.asIntN(32, ${inner}))`
3469
+ : `(Number(${inner}) | 0)`;
3079
3470
  case "uint8":
3080
3471
  case "byte":
3081
- return `(Number(${inner}) & 0xFF)`;
3472
+ return isSrc64
3473
+ ? `Number(BigInt.asUintN(8, ${inner}))`
3474
+ : `(Number(${inner}) & 0xFF)`;
3082
3475
  case "uint16":
3083
- return `(Number(${inner}) & 0xFFFF)`;
3476
+ return isSrc64
3477
+ ? `Number(BigInt.asUintN(16, ${inner}))`
3478
+ : `(Number(${inner}) & 0xFFFF)`;
3084
3479
  case "uint32":
3085
- return `(Number(${inner}) >>> 0)`;
3480
+ return isSrc64
3481
+ ? `Number(BigInt.asUintN(32, ${inner}))`
3482
+ : `(Number(${inner}) >>> 0)`;
3086
3483
  case "float32":
3087
3484
  return `Math.fround(Number(${inner}))`;
3088
3485
  case "float64":
@@ -3106,9 +3503,62 @@ export const expressionGenMethods = {
3106
3503
  return STRICT_NUMERIC_TYPES.has(typeName);
3107
3504
  },
3108
3505
 
3506
+ _is64BitInt(t) {
3507
+ return is64BitInt(t);
3508
+ },
3509
+
3510
+ // float32 arithmetic rounds through Math.fround and arithmetic on a type
3511
+ // parameter wraps for the instantiated type (`__tar`) in every package kind.
3512
+ _isFloat32(t) {
3513
+ return (
3514
+ this._typeName(t) === "float32" ||
3515
+ (t?.kind === "typeParam" && this._typeParamZeroFactory(t.name) != null)
3516
+ );
3517
+ },
3518
+
3519
+ _is64BitUnsigned(t) {
3520
+ return is64BitUnsigned(t);
3521
+ },
3522
+
3523
+ _gen64BitBinary(op, l, r, type) {
3524
+ const unsigned = this._is64BitUnsigned(type);
3525
+ const wrap = unsigned ? "BigInt.asUintN" : "BigInt.asIntN";
3526
+ const divZero =
3527
+ '(() => { throw new Error("runtime error: integer divide by zero"); })()';
3528
+ if (op === "/" || op === "%") {
3529
+ // The divisor is read twice (zero check, division): bind both
3530
+ // operands once, left first, unless they are plain names or literals.
3531
+ const simple = /^[A-Za-z_$][\w$.]*$|^-?\d+n$/;
3532
+ if ((simple.test(l) && simple.test(r)) || /\b(await|yield)\b/.test(l + r))
3533
+ return `((${r}) === 0n ? ${divZero} : ${wrap}(64, (${l}) ${op} (${r})))`;
3534
+ return `((__a, __b) => __b === 0n ? ${divZero} : ${wrap}(64, __a ${op} __b))(${l}, ${r})`;
3535
+ }
3536
+ if (op === "<<" || op === ">>") {
3537
+ this.useHelper("shift");
3538
+ return op === "<<"
3539
+ ? `__shl64(${l}, ${r}, ${unsigned})`
3540
+ : `__shr64(${l}, ${r}, ${unsigned})`;
3541
+ }
3542
+ if (op === "&^") return `${wrap}(64, (${l}) & ~(${r}))`;
3543
+ return `${wrap}(64, (${l}) ${op} (${r}))`;
3544
+ },
3545
+
3546
+ // `uint` / `uintptr`: 64-bit unsigned values held as JS numbers.
3547
+ _isUintWord(type) {
3548
+ const name = this._typeName(type);
3549
+ return name === "uint" || name === "uintptr";
3550
+ },
3551
+
3109
3552
  _needsStrictOp(op, type) {
3553
+ if (type?.kind === "typeParam")
3554
+ return (
3555
+ STRICT_OPS.has(op) && this._typeParamZeroFactory(type.name) != null
3556
+ );
3557
+ if (this._is64BitInt(type))
3558
+ return STRICT_OPS.has(op) || op === "<<" || op === ">>" || op === "&^";
3110
3559
  if (!STRICT_OPS.has(op)) return false;
3111
3560
  if (this._isStrictAtomicOp(op, this._typeName(type))) return true;
3561
+ if (op === "-" && this._isUintWord(type)) return true;
3112
3562
  if (op === "<<" || op === ">>")
3113
3563
  return WIDE_INT_NAMES.has(this._typeName(type));
3114
3564
  return this.isIntType(type) && (op === "/" || op === "%");
@@ -3116,7 +3566,14 @@ export const expressionGenMethods = {
3116
3566
 
3117
3567
  // Emits `lhs = strict(lhs op rhs)` for any lvalue shape; returns false when not applicable.
3118
3568
  // Indexed / nested targets are split into object + key temps so sub-expressions run once.
3119
- _genStrictCompound(lhsNode, op, rhsJs, srcLine = null, lhsJs = null) {
3569
+ _genStrictCompound(
3570
+ lhsNode,
3571
+ op,
3572
+ rhsJs,
3573
+ srcLine = null,
3574
+ lhsJs = null,
3575
+ countType = null,
3576
+ ) {
3120
3577
  const type = lhsNode._type;
3121
3578
  if (!this._needsStrictOp(op, type)) return false;
3122
3579
  if (lhsNode.kind === "IndexExpr" && lhsNode.expr?._type?.kind === "map")
@@ -3124,7 +3581,7 @@ export const expressionGenMethods = {
3124
3581
  const l = lhsJs ?? this._genAssignLhsExpr(lhsNode);
3125
3582
  if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(l)) {
3126
3583
  this.line(
3127
- `${l} = ${this._genStrictBinary(op, l, rhsJs, type)};`,
3584
+ `${l} = ${this._genStrictBinary(op, l, rhsJs, type, countType)};`,
3128
3585
  srcLine,
3129
3586
  );
3130
3587
  return true;
@@ -3137,21 +3594,40 @@ export const expressionGenMethods = {
3137
3594
  const k = `__k${this._tmpCounter}`;
3138
3595
  const target = `${o}[${k}]`;
3139
3596
  this.line(
3140
- `{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
3597
+ `{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
3141
3598
  srcLine,
3142
3599
  );
3143
3600
  } else {
3144
3601
  const target = `${o}.${split.prop}`;
3145
3602
  this.line(
3146
- `{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
3603
+ `{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
3147
3604
  srcLine,
3148
3605
  );
3149
3606
  }
3150
3607
  return true;
3151
3608
  },
3152
3609
 
3153
- _genStrictBinary(op, l, r, type) {
3610
+ // A non-constant signed shift count on a narrow integer is checked for a
3611
+ // negative value (Go panics); 64-bit counts arrive as BigInt.
3612
+ _guardShiftCount(r, countType) {
3613
+ if (!countType || countType.kind === "untyped") return r;
3614
+ const name = this._typeName(countType);
3615
+ if (!name || name.startsWith("u")) return r;
3616
+ this.useHelper("shift");
3617
+ return `__shc(${this._is64BitInt(countType) ? `Number(${r})` : r})`;
3618
+ },
3619
+
3620
+ _genStrictBinary(op, l, r, type, countType = null) {
3621
+ if (type?.kind === "typeParam") {
3622
+ this.useHelper("generics");
3623
+ return `__tar(${this._typeParamZeroFactory(type.name)}, "${op}", ${l}, ${r})`;
3624
+ }
3625
+ if (this._is64BitInt(type)) {
3626
+ return this._gen64BitBinary(op, l, r, type);
3627
+ }
3154
3628
  const tName = this._typeName(type);
3629
+ const isShift = op === "<<" || op === ">>";
3630
+ const rc = isShift ? this._guardShiftCount(r, countType) : r;
3155
3631
  if (tName === "float32") {
3156
3632
  return `Math.fround(${l} ${op} ${r})`;
3157
3633
  }
@@ -3171,7 +3647,7 @@ export const expressionGenMethods = {
3171
3647
  if (op === "<<" || op === ">>") {
3172
3648
  const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
3173
3649
  const jsOp = !signed && op === ">>" ? ">>>" : op;
3174
- return `(((${r}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
3650
+ return `(((${rc}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
3175
3651
  }
3176
3652
  if (op === "&^") return `(((${l}) & ~(${r})) ${cast})`;
3177
3653
  return `((${l} ${op} ${r}) ${cast})`;
@@ -3184,7 +3660,7 @@ export const expressionGenMethods = {
3184
3660
  if (op === "%")
3185
3661
  return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
3186
3662
  if (op === "<<" || op === ">>") {
3187
- return `((${r}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
3663
+ return `((${rc}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
3188
3664
  }
3189
3665
  if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
3190
3666
  return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
@@ -3198,11 +3674,13 @@ export const expressionGenMethods = {
3198
3674
  return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
3199
3675
  if (op === "<<" || op === ">>") {
3200
3676
  const jsOp = op === ">>" ? ">>>" : "<<";
3201
- return `((${r}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
3677
+ return `((${rc}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
3202
3678
  }
3203
3679
  if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
3204
3680
  return `((${l} ${op} ${r}) & ${mask})`;
3205
3681
  }
3682
+ if (op === "-" && this._isUintWord(type))
3683
+ return `((__d) => __d < 0 ? __d + 2 ** 64 : __d)((${l}) - (${r}))`;
3206
3684
  if (op === "<<" || op === ">>") {
3207
3685
  // 64-bit semantics: operand first, then count (Go order); a negative
3208
3686
  // count panics and the result wraps to 64 bits.