gofront 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -2,21 +2,27 @@ 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,
19
+ isComplex64,
16
20
  isTypedArraySlice,
17
21
  typedArrayConstructorForElem,
18
22
  typeStr,
19
23
  } from "../../typechecker/types.js";
24
+ import { goTypeName } from "../shared/fmt-types.js";
25
+ import { jsNativeCall, NATIVE_HELPERS } from "./natives.js";
20
26
 
21
27
  // True when `node` compares the identifier `name` with nil (`l == nil`): a
22
28
  // pointer-receiver method that does so is meant to run on a nil receiver.
@@ -37,8 +43,92 @@ export function comparesToNil(node, name) {
37
43
  return someChild(node, (c) => comparesToNil(c, name));
38
44
  }
39
45
 
46
+ // True when evaluating `node` reads through the receiver `name` (`r.f`,
47
+ // `r[i]`, `*r`), which panics on a nil receiver anyway. Calling a method of
48
+ // `safe` on it is not a read: that method accepts a nil receiver. A func
49
+ // literal's body is not evaluated where it appears, so it is not searched.
50
+ export function derefsReceiver(node, name, safe) {
51
+ if (Array.isArray(node))
52
+ return node.some((n) => derefsReceiver(n, name, safe));
53
+ if (!node || typeof node !== "object" || node.kind === "FuncLit")
54
+ return false;
55
+ const isRecv = (e) => e?.kind === "Ident" && e.name === name;
56
+ if (node.kind === "SelectorExpr" && isRecv(node.expr))
57
+ return !safe.has(node.field);
58
+ if (
59
+ (node.kind === "IndexExpr" || node.kind === "SliceExpr") &&
60
+ isRecv(node.expr)
61
+ )
62
+ return true;
63
+ if (node.kind === "UnaryExpr" && node.op === "*" && isRecv(node.operand))
64
+ return true;
65
+ return someChild(node, (c) => derefsReceiver(c, name, safe));
66
+ }
67
+
68
+ // True when `stmts` (a method body) reads through the receiver `name` before
69
+ // anything could skip the read: only the statements that always run first
70
+ // count, up to the first branch, loop, return or rebinding of `name`. A
71
+ // method that does so panics on a nil receiver either way.
72
+ export function derefsReceiverFirst(stmts, name, safe) {
73
+ return receiverPrefix(stmts, name, safe) === "deref";
74
+ }
75
+
76
+ // What one statement of a method body's unconditional prefix does with the
77
+ // receiver: "deref" reads through it, "stop" may skip the rest (a branch,
78
+ // loop, return, panic or rebinding of the name), "next" runs on.
79
+ const binds = (lhs, name) =>
80
+ lhs.some((e) => e?.kind === "Ident" && e.name === name);
81
+ const branches = (reads, ...parts) => (parts.some(reads) ? "deref" : "stop");
82
+ const RECEIVER_STEP = {
83
+ Block: (s, _reads, name, safe) => receiverPrefix(s.stmts, name, safe),
84
+ IfStmt: (s, reads) => branches(reads, s.init, s.cond),
85
+ ForStmt: (s, reads) => branches(reads, s.init, s.cond),
86
+ SwitchStmt: (s, reads) => branches(reads, s.init, s.tag),
87
+ TypeSwitchStmt: (s, reads) => branches(reads, s.expr),
88
+ ReturnStmt: (s, reads) => branches(reads, s.values),
89
+ ExprStmt: (s, reads) => {
90
+ if (reads(s.expr)) return "deref";
91
+ const panics = s.expr?.kind === "CallExpr" && s.expr.func?.name === "panic";
92
+ return panics ? "stop" : "next";
93
+ },
94
+ DefineStmt: (s, reads, name) => {
95
+ if (reads(s.rhs) || reads(s.lhs.filter((e) => e?.kind !== "Ident")))
96
+ return "deref";
97
+ return binds(s.lhs, name) ? "stop" : "next";
98
+ },
99
+ IncDecStmt: (s, reads) => (reads(s.expr) ? "deref" : "next"),
100
+ VarDecl: (s, reads, name) => {
101
+ if (s.decls.some((d) => reads(d.value))) return "deref";
102
+ const named = (d) => d.names.some((n) => (n?.name ?? n) === name);
103
+ return s.decls.some(named) ? "stop" : "next";
104
+ },
105
+ };
106
+ RECEIVER_STEP.AssignStmt = RECEIVER_STEP.DefineStmt;
107
+
108
+ // "deref" when the unconditional prefix of `stmts` reads through the
109
+ // receiver, "stop" when something may skip the rest, else "next".
110
+ function receiverPrefix(stmts, name, safe) {
111
+ const reads = (n) => derefsReceiver(n, name, safe);
112
+ for (const s of stmts ?? []) {
113
+ const step = RECEIVER_STEP[s?.kind];
114
+ const r = step ? step(s, reads, name, safe) : "stop";
115
+ if (r !== "next") return r;
116
+ }
117
+ return "next";
118
+ }
119
+
40
120
  /** @typedef {import('./index.js').CodeGen} CodeGen */
41
121
 
122
+ // True for a map, slice or array type that mentions a type parameter: one
123
+ // that generic code boxes into an interface is tagged by its instantiated
124
+ // type.
125
+ export function isGenericComposite(t) {
126
+ return (
127
+ (t?.kind === "map" || t?.kind === "slice" || t?.kind === "array") &&
128
+ mentionsTypeParam(t)
129
+ );
130
+ }
131
+
42
132
  // True when type `t` mentions a type parameter anywhere.
43
133
  export function mentionsTypeParam(t, depth = 0) {
44
134
  if (!t || depth > 16) return false;
@@ -125,36 +215,7 @@ const STRICT_OPS = new Set([
125
215
  ">>",
126
216
  ]);
127
217
 
128
- function collectFunctionLabels(node, labels = new Set()) {
129
- if (!node || typeof node !== "object") return labels;
130
- if (Array.isArray(node)) {
131
- for (const item of node) collectFunctionLabels(item, labels);
132
- return labels;
133
- }
134
- if (node.kind === "LabeledStmt" && node.label) labels.add(node.label);
135
- if (node.kind === "ForStmt" && node.label) labels.add(node.label);
136
- for (const k of Object.keys(node)) {
137
- if (k.startsWith("_")) continue;
138
- collectFunctionLabels(node[k], labels);
139
- }
140
- return labels;
141
- }
142
-
143
218
  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
219
  "simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
159
220
  F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
160
221
  "simd.Mask32x4": [null, "Object.freeze([false, false, false, false])"],
@@ -236,91 +297,13 @@ function splitLastAccess(js) {
236
297
 
237
298
  // Namespace constants: pkg.Field → JS literal
238
299
  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
300
  time: {
318
- Nanosecond: "1",
319
- Microsecond: "1000",
320
- Millisecond: "1000000",
321
- Second: "1000000000",
322
- Minute: "60000000000",
323
- Hour: "3600000000000",
301
+ Nanosecond: "1n",
302
+ Microsecond: "1000n",
303
+ Millisecond: "1000000n",
304
+ Second: "1000000000n",
305
+ Minute: "60000000000n",
306
+ Hour: "3600000000000n",
324
307
  RFC3339: '"2006-01-02T15:04:05Z07:00"',
325
308
  RFC3339Nano: '"2006-01-02T15:04:05.999999999Z07:00"',
326
309
  DateOnly: '"2006-01-02"',
@@ -348,7 +331,6 @@ const NS_CONSTANTS = {
348
331
  Friday: "5",
349
332
  Saturday: "6",
350
333
  },
351
- utf8: { RuneError: "0xFFFD", MaxRune: "0x10FFFF", UTFMax: "4" },
352
334
  };
353
335
 
354
336
  // JavaScript precedence of the operators emitted for Go binary expressions.
@@ -413,6 +395,7 @@ const SHARED_ZERO_FACTORIES = {
413
395
  0: "__z0",
414
396
  '""': "__zs",
415
397
  false: "__zf",
398
+ "0n": "__z64",
416
399
  };
417
400
 
418
401
  // Zero-value JS literals for basic Go types.
@@ -422,11 +405,11 @@ const ZERO_FOR_BASIC = {
422
405
  int8: "0",
423
406
  int16: "0",
424
407
  int32: "0",
425
- int64: "0",
408
+ int64: "0n",
426
409
  uint8: "0",
427
410
  uint16: "0",
428
411
  uint32: "0",
429
- uint64: "0",
412
+ uint64: "0n",
430
413
  uintptr: "0",
431
414
  float32: "0",
432
415
  float64: "0",
@@ -439,13 +422,18 @@ const ZERO_FOR_BASIC = {
439
422
  };
440
423
 
441
424
  // Set of all numeric type names (for type-assertion codegen).
442
- const TYPEOF_NUMBER_NAMES = new Set([...INT_TYPE_NAMES, "float32", "float64"]);
425
+ const TYPEOF_NUMBER_NAMES = new Set([
426
+ ...[...INT_TYPE_NAMES].filter((n) => n !== "int64" && n !== "uint64"),
427
+ "float32",
428
+ "float64",
429
+ ]);
443
430
 
444
431
  // Builtin function codegen — keyed by builtin name, value is (self, expr) => string.
445
432
  const BUILTIN_GEN = {
446
433
  append: (s, e) => s.genAppend(e),
447
434
  len: (s, e) => s._genBuiltinLen(e),
448
435
  cap: (s, e) => {
436
+ if (e._constLen != null) return s._genConstLen(e);
449
437
  s.useHelper("slice");
450
438
  return `__cap(${s.genExpr(e.args[0])})`;
451
439
  },
@@ -457,13 +445,11 @@ const BUILTIN_GEN = {
457
445
  copy: (s, e) => {
458
446
  const dst = s.genExpr(e.args[0]);
459
447
  const src = s._genSliceSource(e.args[1]);
460
- // Array elements are values: the copied elements must not alias src.
448
+ // Array and struct elements are values: the copied elements must not
449
+ // alias src.
461
450
  const t = e.args[1]._type;
462
451
  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";
452
+ const clone = s._aggregateBase(elem) ? s._cloneJs(elem, "__e") : "__e";
467
453
  const items =
468
454
  clone === "__e"
469
455
  ? "(__cs.slice ? __cs.slice(0, n) : __cs.subarray(0, n))"
@@ -496,15 +482,21 @@ const BUILTIN_GEN = {
496
482
  // `print` writes without a trailing newline (Go semantics); the wasm glue
497
483
  // buffers the same way, so output parity holds across backends.
498
484
  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(" "))`,
485
+ `((__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
486
  println: (s, e) =>
501
487
  `console.log(${e.args.map((a) => s.genExpr(a)).join(", ")})`,
502
- panic: (s, e) => `(() => { throw new Error(${s.genExpr(e.args[0])}); })()`,
488
+ panic: (s, e) => {
489
+ const arg = s.genExpr(e.args[0]);
490
+ s.useHelper("panicRt");
491
+ return `__gopanicv(${arg})`;
492
+ },
503
493
  recover: (s) => {
504
494
  s.useHelper("panicRt");
505
495
  return `__recover(${s._recoverOkInScope ? "__recoverOk" : "false"})`;
506
496
  },
507
497
  error: (s, e) => {
498
+ // `error(x)` of an error (or nil) is the conversion: x itself.
499
+ if (!s._isStringExprType(e.args[0]?._type)) return s.genExpr(e.args[0]);
508
500
  s.useHelper("error");
509
501
  return `__error(${s.genExpr(e.args[0])})`;
510
502
  },
@@ -521,13 +513,25 @@ const BUILTIN_GEN = {
521
513
  // A type parameter may be a string or a number: pick the form at run time
522
514
  // (Math.min/max keeps NaN propagation and -0 for floats).
523
515
  function genMinMax(s, e, fn, cmp) {
524
- const args = e.args.map((a) => s.genExpr(a));
516
+ const is64 = s._is64BitInt(e._type);
517
+ const args = e.args.map((a) => {
518
+ const js = s.genExpr(a);
519
+ if (
520
+ is64 &&
521
+ a._type?.kind === "untyped" &&
522
+ !js.endsWith("n") &&
523
+ /^-?\d+$/.test(js)
524
+ ) {
525
+ return `${js}n`;
526
+ }
527
+ return js;
528
+ });
525
529
  const fold = (xs) => `${xs}.reduce((a, b) => (b ${cmp} a ? b : a))`;
526
530
  if (args.length === 1) return args[0];
527
531
  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(", ")}]`);
532
+ return `((...v) => (typeof v[0] === "string" || typeof v[0] === "bigint" ? ${fold("v")} : Math.${fn}(...v)))(${args.join(", ")})`;
533
+ if (is64 || s._isStringExprType(e._type)) return fold(`[${args.join(", ")}]`);
534
+ return `Math.${fn}(${args.join(", ")})`;
531
535
  }
532
536
 
533
537
  // Expression kinds whose folded constant value (`_const`, attached by the
@@ -588,8 +592,51 @@ function hasTypeParamType(t) {
588
592
  ).includes('"typeParam"');
589
593
  }
590
594
 
595
+ // Complex constant arithmetic on [re, im] pairs.
596
+ const COMPLEX_CONST_OPS = {
597
+ "+": (a, b) => [a[0] + b[0], a[1] + b[1]],
598
+ "-": (a, b) => [a[0] - b[0], a[1] - b[1]],
599
+ "*": (a, b) => [a[0] * b[0] - a[1] * b[1], a[0] * b[1] + a[1] * b[0]],
600
+ "/": (a, b) => {
601
+ const d = b[0] * b[0] + b[1] * b[1];
602
+ if (d === 0) return null;
603
+ return [(a[0] * b[0] + a[1] * b[1]) / d, (a[1] * b[0] - a[0] * b[1]) / d];
604
+ },
605
+ };
606
+
607
+ // The [re, im] value of an untyped complex constant expression built from
608
+ // literals and named constants, or null.
609
+ function complexConstParts(e, depth = 0) {
610
+ if (!e || typeof e !== "object" || depth > 32) return null;
611
+ if (e.kind === "ParenExpr") return complexConstParts(e.expr, depth + 1);
612
+ // A named complex constant has no folded value: fold its initializer.
613
+ if (e._complexInit) return complexConstParts(e._complexInit, depth + 1);
614
+ if (e.kind === "ImagLit") return [0, Number(e.value)];
615
+ const val = e._const;
616
+ if (val && (val.k === "FLOAT" || val.k === "INT")) return [Number(val.v), 0];
617
+ if (e.kind === "UnaryExpr") return complexConstNeg(e, depth);
618
+ const op = e.kind === "BinaryExpr" ? COMPLEX_CONST_OPS[e.op] : null;
619
+ const a = op && complexConstParts(e.left, depth + 1);
620
+ const b = a && complexConstParts(e.right, depth + 1);
621
+ return b ? op(a, b) : null;
622
+ }
623
+
624
+ function complexConstNeg(e, depth) {
625
+ if (e.op !== "-" && e.op !== "+") return null;
626
+ const c = complexConstParts(e.operand, depth + 1);
627
+ return c && e.op === "-" ? [-c[0], -c[1]] : c;
628
+ }
629
+
591
630
  export const expressionGenMethods = {
592
631
  genExpr(expr) {
632
+ if (expr._asComplex && !expr._complexWrapping) {
633
+ expr._complexWrapping = true;
634
+ try {
635
+ return `{ re: ${this.genExpr(expr)}, im: 0 }`;
636
+ } finally {
637
+ expr._complexWrapping = false;
638
+ }
639
+ }
593
640
  if (expr._ifaceKeyDyn && !expr._ifaceKeying) {
594
641
  // A key typed by a type parameter: encode it for the instantiated type.
595
642
  const f = this._typeParamZeroFactory(expr._ifaceKeyParam);
@@ -613,21 +660,96 @@ export const expressionGenMethods = {
613
660
  expr._ifaceKeying = true;
614
661
  try {
615
662
  this.useHelper("structKey");
616
- return `__ik(${this.genExpr(expr)})`;
663
+ const tag = this._ifaceKeyTag(expr._ifaceKeyType);
664
+ return `__ik(${this.genExpr(expr)}${tag ? `, ${JSON.stringify(tag)}` : ""})`;
617
665
  } finally {
618
666
  expr._ifaceKeying = false;
619
667
  }
620
668
  }
621
669
  }
670
+ if (expr._mapKeyEnc && !expr._mapKeying) {
671
+ // Array, anonymous struct and pointer keys: see HELPER_STRUCT_KEY.
672
+ expr._mapKeying = true;
673
+ try {
674
+ this.useHelper("structKey");
675
+ const enc = expr._mapKeyEnc === "pk" ? "__pk" : "__sk";
676
+ return `${enc}(${this.genExpr(expr)})`;
677
+ } finally {
678
+ expr._mapKeying = false;
679
+ }
680
+ }
681
+ if (
682
+ expr._ifaceSrc?.kind === "pointer" &&
683
+ expr._ifaceSrc.base?.kind === "typeParam" &&
684
+ !expr._pbsWrapping
685
+ ) {
686
+ // `&v` of a type parameter's variable is a box; a struct behind it
687
+ // is its own pointer.
688
+ expr._pbsWrapping = true;
689
+ try {
690
+ this.useHelper("ifaceBox");
691
+ return `__pbs(${this.genExpr(expr)})`;
692
+ } finally {
693
+ expr._pbsWrapping = false;
694
+ }
695
+ }
696
+ if (
697
+ expr._ifaceSrc?.kind === "typeParam" &&
698
+ !expr._tpBoxing &&
699
+ !expr._ifaceKeyDyn &&
700
+ this._typeParamZeroFactory(expr._ifaceSrc.name)
701
+ ) {
702
+ // A type parameter's value entering an interface is boxed and
703
+ // tagged by the instantiated type (see `__ti`).
704
+ expr._tpBoxing = true;
705
+ try {
706
+ const f = this._typeParamZeroFactory(expr._ifaceSrc.name);
707
+ return `(${f}.b ?? ${f}.fb ?? ((__v) => __v))(${this.genExpr(expr)})`;
708
+ } finally {
709
+ expr._tpBoxing = false;
710
+ }
711
+ }
622
712
  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;
713
+ if (
714
+ expr._ifaceSrc &&
715
+ !expr._tfnTagging &&
716
+ this._isTaggedFuncType(expr._ifaceSrc)
717
+ ) {
718
+ // A value of a defined func type keeps that type in an interface.
719
+ expr._tfnTagging = true;
720
+ try {
721
+ this.useHelper("ifaceBox");
722
+ return `__tfn(${this.genExpr(expr)}, ${JSON.stringify(expr._ifaceSrc.name)})`;
723
+ } finally {
724
+ expr._tfnTagging = false;
725
+ }
726
+ }
727
+ if (isGenericComposite(expr._ifaceSrc) && !expr._gmapTagging) {
728
+ const tag = this._genericMapTag(expr._ifaceSrc);
729
+ if (tag) {
730
+ this.useHelper("ifaceBox");
731
+ expr._gmapTagging = true;
732
+ try {
733
+ // An array is copied before it is tagged: a copy made after
734
+ // `__ity` (`.map`) would drop the tag.
735
+ return `__ity(${this.genValueExpr(expr)}, ${tag})`;
736
+ } finally {
737
+ expr._gmapTagging = false;
738
+ }
739
+ }
740
+ }
741
+ {
742
+ const tag = this._ityTag(expr);
743
+ if (tag) {
744
+ this.useHelper("ifaceBox");
745
+ expr._ityTagging = true;
627
746
  try {
628
- return this._fmtDescWrap(this.genExpr(expr), desc);
747
+ const ptr = this._dynTagFlags(expr._ifaceSrc);
748
+ // An array is copied before it is tagged: the copy is the value
749
+ // the interface holds, and it must keep the tag.
750
+ return `__ity(${this.genValueExpr(expr)}, ${JSON.stringify(tag)}${ptr})`;
629
751
  } finally {
630
- expr._fmtDescing = false;
752
+ expr._ityTagging = false;
631
753
  }
632
754
  }
633
755
  }
@@ -635,29 +757,50 @@ export const expressionGenMethods = {
635
757
  expr._ifaceBoxing = true;
636
758
  try {
637
759
  this.useHelper("ifaceBox");
638
- return this._ifaceBoxWrap(
760
+ const boxed = this._ifaceBoxWrap(
639
761
  this.genExpr(expr),
640
762
  expr._ifaceBox,
641
763
  expr._ifacePtrFields,
764
+ expr.kind === "CompositeLit",
642
765
  );
766
+ return this._typedNilWrap(boxed, expr);
643
767
  } finally {
644
768
  expr._ifaceBoxing = false;
645
769
  }
646
770
  }
771
+ const f32 = this._genFloat32Const(expr);
772
+ if (f32 !== null) return f32;
773
+ const c64 = this._genComplex64Const(expr);
774
+ if (c64 !== null) return c64;
647
775
  const folded = this._genFoldedConst(expr);
648
776
  if (folded !== null) return folded;
649
777
  switch (expr.kind) {
650
778
  case "BasicLit":
651
779
  if (expr.litKind === "STRING") return JSON.stringify(expr.value);
780
+ if (
781
+ (this._is64BitInt(expr._type) || expr._is64Bit) &&
782
+ (expr.litKind === "INT" || expr._const?.k === "INT")
783
+ ) {
784
+ return `${expr.value}n`;
785
+ }
652
786
  if (expr._const?.k === "INT")
653
787
  this._warnConstPrecision(expr._const, expr);
654
788
  return expr.value; // int, float, bool, nil(null)
655
789
  case "ImagLit":
656
790
  return `{ re: 0, im: ${expr.value} }`;
657
- case "Ident":
791
+ case "Ident": {
658
792
  if (this._boxedVars.has(expr.name) && !expr._isAddressOf)
659
793
  return `${expr.name}.value`;
660
- return expr.name;
794
+ // A named untyped constant used as an int64 operand: its
795
+ // declaration holds a Number, so emit the exact BigInt.
796
+ const lit64 =
797
+ expr._is64Bit &&
798
+ expr._const &&
799
+ (expr._type?.kind === "untyped" || this._is64BitInt(expr._type))
800
+ ? this._gen64BitFoldedConst(expr, expr._const)
801
+ : null;
802
+ return lit64 ?? expr.name;
803
+ }
661
804
  case "AwaitExpr":
662
805
  return `await ${this.genExpr(expr.expr)}`;
663
806
  case "InstantiationExpr":
@@ -672,6 +815,42 @@ export const expressionGenMethods = {
672
815
  }
673
816
  },
674
817
 
818
+ _gen64BitFoldedConst(expr, val) {
819
+ if (
820
+ (this._is64BitInt(expr._type) || expr._is64Bit) &&
821
+ (val.k === "INT" || expr._intConst)
822
+ ) {
823
+ const n = constIntValue(val);
824
+ if (n !== null) return n < 0n ? `(${n}n)` : `${n}n`;
825
+ }
826
+ return null;
827
+ },
828
+
829
+ // An untyped constant converted to float32 (`var c float32 = 0.01`) is
830
+ // emitted as its float32 value, or null when that changes nothing.
831
+ _genFloat32Const(expr) {
832
+ const val = expr._const;
833
+ if (!expr._f32Const || !val || (val.k !== "FLOAT" && val.k !== "INT"))
834
+ return null;
835
+ const n = Number(val.v);
836
+ if (!Number.isFinite(n)) return null;
837
+ const r = Math.fround(n);
838
+ if (r === n) return null;
839
+ return CONST_LITS.FLOAT(r);
840
+ },
841
+
842
+ // An untyped complex constant that takes type complex64
843
+ // (`var c complex64 = 1 + 0.01i`) is emitted as its folded value with
844
+ // float32 components, or null when it cannot be folded here.
845
+ _genComplex64Const(expr) {
846
+ if (!expr._c64Const) return null;
847
+ const c = complexConstParts(expr);
848
+ if (!c) return null;
849
+ const re = CONST_LITS.FLOAT(Math.fround(c[0]));
850
+ const im = CONST_LITS.FLOAT(Math.fround(c[1]));
851
+ return re === null || im === null ? null : `{ re: ${re}, im: ${im} }`;
852
+ },
853
+
675
854
  // The literal for a folded constant expression (`1 << 40` →
676
855
  // `1099511627776`), or null. Integers outside ±2⁵³ emit the nearest
677
856
  // Number and a precision warning.
@@ -686,6 +865,8 @@ export const expressionGenMethods = {
686
865
  return null;
687
866
  }
688
867
  if (!FOLDED_CONST_KINDS.has(expr.kind) || isSignedLit(expr)) return null;
868
+ const lit64 = this._gen64BitFoldedConst(expr, val);
869
+ if (lit64 !== null) return lit64;
689
870
  if (val.k === "INT" || expr._intConst) this._warnConstPrecision(val, expr);
690
871
  return CONST_LITS[val.k]?.(val.v) ?? null;
691
872
  },
@@ -694,6 +875,8 @@ export const expressionGenMethods = {
694
875
  // per node per generate run: some forms generate the same node more than
695
876
  // once, and cached ASTs are generated again by later compiles).
696
877
  _warnConstPrecision(val, expr) {
878
+ if (this._is64BitInt(expr?._type) || expr?._is64Bit || expr?._floatConst)
879
+ return;
697
880
  const i = constIntValue(val);
698
881
  if (i === null || (i <= JS_SAFE_INT && i >= -JS_SAFE_INT)) return;
699
882
  if (BigInt(Number(i)) !== i && !this._precisionWarned.has(expr)) {
@@ -703,7 +886,9 @@ export const expressionGenMethods = {
703
886
  },
704
887
 
705
888
  // `js` boxed as `box` (a checker _ifaceBoxMeta kind) for an interface.
706
- _ifaceBoxWrap(js, box, ptrFields) {
889
+ // `fresh` marks a value nothing else refers to (a composite literal), which
890
+ // a struct box then takes over instead of copying.
891
+ _ifaceBoxWrap(js, box, ptrFields, fresh = false) {
707
892
  this.useHelper("ifaceBox");
708
893
  const fn =
709
894
  {
@@ -713,14 +898,22 @@ export const expressionGenMethods = {
713
898
  map: "__ifm",
714
899
  slice: "__ifs",
715
900
  }[box] ?? "__ifv";
716
- const extra = ptrFields?.length ? `, ${JSON.stringify(ptrFields)}` : "";
717
- return `${fn}(${js}${extra})`;
901
+ const fields = ptrFields?.length ? JSON.stringify(ptrFields) : "";
902
+ if (fn === "__ifv" && fresh)
903
+ return `${fn}(${js}, ${fields || "null"}, true)`;
904
+ return `${fn}(${js}${fields ? `, ${fields}` : ""})`;
718
905
  },
719
906
 
720
- // `js` tagged with the fmt descriptor source `desc` (see _fmtDesc).
721
- _fmtDescWrap(js, desc) {
722
- this.useHelper("ifd");
723
- return `__ifd(${js}, ${desc})`;
907
+ // A nil `*T` entering an interface is a typed nil: it keeps T's methods
908
+ // (callable with a nil receiver) and differs from the nil interface.
909
+ _typedNilWrap(js, expr) {
910
+ if (expr._ifaceBox !== "ptr") return js;
911
+ const src = expr._ifaceSrc;
912
+ const base = src?.kind === "pointer" ? src.base : null;
913
+ if (base?.kind !== "named" || base.underlying?.kind !== "struct") return js;
914
+ const cls = this._dequalify(base.name).replace(/\[.*$/, "");
915
+ if (!this.structNames.has(cls)) return js;
916
+ return `__ifn(${js}, ${cls})`;
724
917
  },
725
918
 
726
919
  _isNamedBoxKind(expr) {
@@ -819,8 +1012,12 @@ export const expressionGenMethods = {
819
1012
  // Pseudo type object for an AST type node (used where no checker type is available).
820
1013
  _typeFromNode(node) {
821
1014
  if (!node) return null;
822
- if (node.kind === "TypeName" && this.structNames.has(node.name))
823
- return { kind: "named", name: node.name, underlying: { kind: "struct" } };
1015
+ if (node.kind === "TypeName" || node.kind === "SelectorExpr") {
1016
+ // `pkg.S` of a bundled package names the class `S`.
1017
+ const name = this.getTypeName(node);
1018
+ if (name && this.structNames.has(name))
1019
+ return { kind: "named", name, underlying: { kind: "struct" } };
1020
+ }
824
1021
  if (node.kind === "ArrayType")
825
1022
  return { kind: "array", elem: this._typeFromNode(node.elem) };
826
1023
  return null;
@@ -857,7 +1054,9 @@ export const expressionGenMethods = {
857
1054
  _isAddressableExpr(e) {
858
1055
  switch (e?.kind) {
859
1056
  case "Ident":
860
- return e.name !== "_" && !e._isTypeRef;
1057
+ return (
1058
+ e.name !== "_" && (!e._isTypeRef || e._type?.kind === "typeParam")
1059
+ );
861
1060
  case "SelectorExpr":
862
1061
  return !e._isMethodValue && !e._isMethodExpr;
863
1062
  case "IndexExpr":
@@ -871,10 +1070,22 @@ export const expressionGenMethods = {
871
1070
  }
872
1071
  },
873
1072
 
1073
+ // The type tag `expr` takes on entering an interface, or null.
1074
+ _ityTag(expr) {
1075
+ if (!expr._ifaceSrc || expr._ityTagging || this._isNamedBoxKind(expr))
1076
+ return null;
1077
+ return this._dynTypeTag(expr._ifaceSrc);
1078
+ },
1079
+
874
1080
  // Emits `expr` as a Go value: struct/array operands that alias existing storage are copied.
875
1081
  genValueExpr(expr) {
1082
+ const tagged =
1083
+ Boolean(this._ityTag(expr)) ||
1084
+ (isGenericComposite(expr._ifaceSrc) &&
1085
+ !expr._gmapTagging &&
1086
+ Boolean(this._genericMapTag(expr._ifaceSrc)));
876
1087
  const js = this.genExpr(expr);
877
- if (expr._ifaceBox) return js;
1088
+ if (expr._ifaceBox || tagged) return js;
878
1089
  if (
879
1090
  !(
880
1091
  this._aggregateBase(expr._type) || this._typeParamClone(expr._type, js)
@@ -889,11 +1100,6 @@ export const expressionGenMethods = {
889
1100
  return rootIdentName(e);
890
1101
  },
891
1102
 
892
- // True when `node` itself (not its children) writes to variable `name`.
893
- _nodeWritesVar(node, name) {
894
- return nodeWritesVar(node, name);
895
- },
896
-
897
1103
  // True when `node` may modify (or take the address of) the value held by variable `name`.
898
1104
  _nodeMutatesVar(node, name, addrOnly = false) {
899
1105
  return nodeMutatesVar(node, name, addrOnly);
@@ -905,12 +1111,10 @@ export const expressionGenMethods = {
905
1111
 
906
1112
  _withFnCtx(body, fn) {
907
1113
  const prev = this._fnCtx;
908
- const prevLabels = this._functionLabels;
909
- const labels = collectFunctionLabels(
910
- body,
911
- new Set(this._functionLabels ?? []),
912
- );
913
- this._functionLabels = Array.from(labels);
1114
+ // A function starts with no JS labels in scope (see
1115
+ // `_emitIterTargetDispatch`).
1116
+ const prevLabels = this._jsLabels;
1117
+ this._jsLabels = [];
914
1118
  this._fnCtx = {
915
1119
  body,
916
1120
  mut: new Map(),
@@ -924,7 +1128,7 @@ export const expressionGenMethods = {
924
1128
  fn();
925
1129
  } finally {
926
1130
  this._fnCtx = prev;
927
- this._functionLabels = prevLabels;
1131
+ this._jsLabels = prevLabels;
928
1132
  }
929
1133
  },
930
1134
 
@@ -1031,7 +1235,9 @@ export const expressionGenMethods = {
1031
1235
  // Dereference: *p
1032
1236
  if (op === "*") {
1033
1237
  if (this._isStructPointerType(expr.operand._type)) {
1034
- return this._genNilChecked(this.genExpr(expr.operand));
1238
+ return expr._nonNil
1239
+ ? this.genExpr(expr.operand)
1240
+ : this._genNilChecked(this.genExpr(expr.operand));
1035
1241
  }
1036
1242
  if (expr.operand._type?.base?.kind === "typeParam") {
1037
1243
  if (expr._derefOpTarget) return `${this.genExpr(expr.operand)}.value`;
@@ -1046,6 +1252,17 @@ export const expressionGenMethods = {
1046
1252
  return inner;
1047
1253
  }
1048
1254
  const code = this.genExpr(expr.operand);
1255
+ if (this._is64BitInt(expr._type) || expr._is64Bit) {
1256
+ let operandCode = code;
1257
+ if (!operandCode.endsWith("n") && /^-?\d+$/.test(operandCode)) {
1258
+ operandCode = `${operandCode}n`;
1259
+ }
1260
+ if (op === "+") return operandCode;
1261
+ const wrap = this._is64BitUnsigned(expr._type)
1262
+ ? "BigInt.asUintN"
1263
+ : "BigInt.asIntN";
1264
+ if (op === "-" || op === "~") return `${wrap}(64, ${op}(${operandCode}))`;
1265
+ }
1049
1266
  const wrap = this.strict
1050
1267
  ? SMALL_UNSIGNED_WRAP[this._typeName(expr._type)]
1051
1268
  : null;
@@ -1064,11 +1281,38 @@ export const expressionGenMethods = {
1064
1281
 
1065
1282
  _genSelectorExpr(expr) {
1066
1283
  // Go-source stdlib member: bundled under its mangled top-level name.
1067
- if (expr._stdName) return expr._stdName;
1284
+ if (expr._stdName) {
1285
+ // An untyped constant used as an int64 operand: its declaration
1286
+ // holds a Number, so emit the exact BigInt (as for an Ident).
1287
+ const lit64 =
1288
+ expr._is64Bit && expr._type?.kind === "untyped" && expr._const
1289
+ ? this._gen64BitFoldedConst(expr, expr._const)
1290
+ : null;
1291
+ return lit64 ?? expr._stdName;
1292
+ }
1068
1293
  if (this._isStdlibFuncValue(expr)) return this._genStdlibFuncValue(expr);
1069
1294
  if (expr._isMethodExpr) {
1070
1295
  const namedExpr = this._namedMethodExprInfo(expr);
1071
1296
  if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
1297
+ const rt = expr._methodExprRecv;
1298
+ // `(*MyInt).M` for a pointer-receiver method of a named non-struct
1299
+ // type: the static method takes the pointer, nil included.
1300
+ if (rt?.kind === "pointer") {
1301
+ const info = this._namedBoxInfo(rt.base);
1302
+ const decl = rt.base?.methods?.get(expr.field)?._methodDecl;
1303
+ if (info?.methods.has(expr.field) && decl?.recvPointer)
1304
+ return `${info.cls}.$${expr.field}`;
1305
+ }
1306
+ const protoCls = this._ptrMethodProtoClass(rt, expr.field);
1307
+ if (protoCls) {
1308
+ // A value-receiver method reads `*recv`, which panics on nil.
1309
+ let recv = "recv";
1310
+ if (rt.base.methods.get(expr.field)?._ptrRecv === false) {
1311
+ this.useHelper("nilCall");
1312
+ recv = "__nilcall(recv)";
1313
+ }
1314
+ return `((recv, ...args) => ${protoCls}.prototype.${expr.field}.call(${recv}, ...args))`;
1315
+ }
1072
1316
  return `((recv, ...args) => recv.${expr.field}(...args))`;
1073
1317
  }
1074
1318
  const namedValue = this._genNamedMethodValue(expr);
@@ -1076,26 +1320,6 @@ export const expressionGenMethods = {
1076
1320
  if (expr.expr.kind === "Ident") {
1077
1321
  const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
1078
1322
  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
1323
  return nsConst;
1100
1324
  }
1101
1325
  }
@@ -1178,7 +1402,7 @@ export const expressionGenMethods = {
1178
1402
  const inner = sf.type?.kind === "pointer" ? sf.type.base : sf.type;
1179
1403
  if (inner?.kind !== "named" || !this.structNames.has(inner.name))
1180
1404
  continue;
1181
- const name = inner.name;
1405
+ const name = embeddedFieldName(sf);
1182
1406
  if (name === field) return null;
1183
1407
  const ist = inner.underlying;
1184
1408
  if (!ist?.fields?.has(field)) continue;
@@ -1299,21 +1523,54 @@ export const expressionGenMethods = {
1299
1523
 
1300
1524
  _genCallArgs(expr) {
1301
1525
  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)
1526
+ return this._genCallArgList(expr).join(", ");
1527
+ },
1528
+
1529
+ _genCallArgList(expr) {
1530
+ return expr.args.map((a, i) =>
1531
+ a._spread
1532
+ ? this._genSpreadArg(expr, a)
1533
+ : a._boxTypeParam
1534
+ ? this._genTypeParamBox(a)
1306
1535
  : this._callerCopiesArg(expr, i)
1307
1536
  ? this.genValueExpr(a)
1308
1537
  : this.genExpr(a),
1309
- )
1310
- .join(", ");
1538
+ );
1539
+ },
1540
+
1541
+ // A type parameter operand of a Print-family fmt call (see
1542
+ // src/lower/fmt-formatters.js): a JS value does not know its Go type, so
1543
+ // the type argument boxes it as converting it to an `any` does.
1544
+ _genTypeParamBox(a) {
1545
+ // Boxed below: skip the boxing of a value entering an interface.
1546
+ const wasBoxing = a._tpBoxing;
1547
+ a._tpBoxing = true;
1548
+ const js = this.genExpr(a);
1549
+ a._tpBoxing = wasBoxing;
1550
+ const tpf = this._typeParamZeroFactory(a._type.name);
1551
+ return tpf ? `(${tpf}.b ?? ${tpf}.fb ?? ((__v) => __v))(${js})` : js;
1552
+ },
1553
+
1554
+ // A direct call of a `//gofront:native` function: host code from the
1555
+ // registry, or null (declined, natives off, spread arguments) to call the
1556
+ // Go body.
1557
+ _genNativeCall(expr) {
1558
+ if (!this.natives || !expr._native) return null;
1559
+ if (expr._multiForward || expr.args.some((a) => a._spread)) return null;
1560
+ const typeArgs = expr._typeArgs ?? expr.func._typeArgs ?? [];
1561
+ const js = jsNativeCall(expr._native, this._genCallArgList(expr), typeArgs);
1562
+ if (js !== null && NATIVE_HELPERS.has(expr._native))
1563
+ this.useHelper(NATIVE_HELPERS.get(expr._native));
1564
+ return js;
1311
1565
  },
1312
1566
 
1313
1567
  // `f(xs...)`: a Go-compiled variadic callee receives the slice itself in a
1314
1568
  // marker (its prologue unwraps it); other callees get a JS spread.
1315
1569
  _genSpreadArg(call, arg) {
1316
1570
  const js = this.genExpr(arg);
1571
+ // A variadic function of a Go-source package (`fmt.Println`): a nil
1572
+ // slice spreads to no operands.
1573
+ if (call.func._stdName) return `...(${js} ?? [])`;
1317
1574
  return this._calleeIsGoVariadic(call) ? `{ __sp: ${js} }` : `...${js}`;
1318
1575
  },
1319
1576
 
@@ -1355,7 +1612,9 @@ export const expressionGenMethods = {
1355
1612
  if (this._isStructPointerType(expr.expr._type)) {
1356
1613
  // Strict mode: a field of a nil struct pointer panics as in Go.
1357
1614
  const recv =
1358
- this.strict && !expr._isMethodValue ? this._genNilChecked(base) : base;
1615
+ this.strict && !expr._isMethodValue && !expr._nonNil
1616
+ ? this._genNilChecked(base)
1617
+ : base;
1359
1618
  const sel = `${recv}${this._promotedPath(expr)}.${expr.field}`;
1360
1619
  if (expr._isMethodValue && !expr._callee) {
1361
1620
  // `p.M` with a value receiver binds a copy of `*p` now.
@@ -1366,6 +1625,15 @@ export const expressionGenMethods = {
1366
1625
  );
1367
1626
  return `((__r) => __r.${expr.field}.bind(__r))(${copy})`;
1368
1627
  }
1628
+ // A pointer receiver may be nil: bind through the prototype so
1629
+ // `s.M` on a nil pointer does not read `.M` off null.
1630
+ const protoCls = this._ptrMethodProtoClass(
1631
+ expr.expr._type,
1632
+ expr.field,
1633
+ expr,
1634
+ );
1635
+ if (protoCls)
1636
+ return `${protoCls}.prototype.${expr.field}.bind(${base})`;
1369
1637
  return `${sel}.bind(${base})`;
1370
1638
  }
1371
1639
  return sel;
@@ -1382,11 +1650,12 @@ export const expressionGenMethods = {
1382
1650
  const base = wrapField
1383
1651
  ? `${this.genExpr(expr.expr)}.${wrapField}`
1384
1652
  : this.genExpr(expr.expr);
1385
- const idx = this.genExpr(expr.index);
1653
+ let idx = this.genExpr(expr.index);
1386
1654
  if (expr._mapValueType) {
1387
1655
  if (!expr._lvalue) return this._genMapIndexExpr(expr, base, idx);
1388
1656
  return `${this._genMapWriteBase(base)}[${idx}]`;
1389
1657
  }
1658
+ idx = this._asIndexNumber(idx, expr.index?._type);
1390
1659
  const checked = this._genStrictIndex(expr, base, idx);
1391
1660
  if (checked) return checked;
1392
1661
  if (this._isStringExprType(exprType) && !expr._lvalue)
@@ -1428,8 +1697,8 @@ export const expressionGenMethods = {
1428
1697
  _genMapIndexExpr(expr, base, idx) {
1429
1698
  const zero = this.zeroValueForType(expr._mapValueType);
1430
1699
  if (this._hasCallExpr(expr.expr) || this._hasCallExpr(expr.index))
1431
- return `((__m, __k) => __m[__k] ?? ${zero})(${base}, ${idx})`;
1432
- return `(${base}[${idx}] ?? ${zero})`;
1700
+ return `((__m, __k) => __m?.[__k] ?? ${zero})(${base}, ${idx})`;
1701
+ return `(${base}?.[${idx}] ?? ${zero})`;
1433
1702
  },
1434
1703
 
1435
1704
  _isStringExprType(exprType) {
@@ -1442,8 +1711,12 @@ export const expressionGenMethods = {
1442
1711
 
1443
1712
  _genSliceExpr(expr) {
1444
1713
  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";
1714
+ const lo = expr.low
1715
+ ? this._asIndexNumber(this.genExpr(expr.low), expr.low._type)
1716
+ : "0";
1717
+ const hi = expr.high
1718
+ ? this._asIndexNumber(this.genExpr(expr.high), expr.high._type)
1719
+ : "undefined";
1447
1720
  if (this._isStringExprType(expr.expr._type))
1448
1721
  return expr.high ? `${base}.slice(${lo}, ${hi})` : `${base}.slice(${lo})`;
1449
1722
  // `b[:]`, `b[0:]` and `a[:]` have the operand's length and capacity:
@@ -1456,7 +1729,9 @@ export const expressionGenMethods = {
1456
1729
  // the result stays within `len(x)`; typed arrays always share.
1457
1730
  if (expr._selfReslice && !isTypedArraySlice(expr.expr._type))
1458
1731
  return `__reslice(${base}, ${lo}, ${hi})`;
1459
- const max = expr.max ? `, ${this.genExpr(expr.max)}` : "";
1732
+ const max = expr.max
1733
+ ? `, ${this._asIndexNumber(this.genExpr(expr.max), expr.max._type)}`
1734
+ : "";
1460
1735
  return `__slice(${base}, ${lo}, ${hi}${max})`;
1461
1736
  },
1462
1737
 
@@ -1471,7 +1746,7 @@ export const expressionGenMethods = {
1471
1746
  this._withFnCtx(expr.body, () =>
1472
1747
  this.indented(() => {
1473
1748
  this._emitParamCopies(expr.params);
1474
- this._genBody(expr.body, expr);
1749
+ this._withNamedReturns(expr, () => this._genBody(expr.body, expr));
1475
1750
  }),
1476
1751
  );
1477
1752
  const body = this.out.join("\n");
@@ -1493,18 +1768,38 @@ export const expressionGenMethods = {
1493
1768
  if (!expr._commaOk) {
1494
1769
  // plain assertion: panic if check fails (matches Go behavior)
1495
1770
  if (check === "true") return src; // can't check at runtime — pass through
1771
+ this.useHelper("ifaceBox");
1496
1772
  return wrap(
1497
- `(${check} ? ${out} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
1773
+ `(${check} ? ${out} : (() => { throw ${this._typeAssertFailJs(expr, val)}; })())`,
1498
1774
  );
1499
1775
  }
1500
- // comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted array
1501
- // is a copy of the boxed value.
1776
+ // comma-ok: emit [value-or-zero, runtimeTypeCheck]; an asserted struct
1777
+ // or array is a copy of the boxed value.
1502
1778
  const zero = this.zeroValueForTypeNode(expr.type);
1503
- const arrT = this._typeFromNode(expr.type);
1504
- const okOut = arrT?.kind === "array" ? this._cloneJs(arrT, out) : out;
1779
+ const valT = this._typeFromNode(expr.type);
1780
+ const okOut = this._aggregateBase(valT) ? this._cloneJs(valT, out) : out;
1505
1781
  return wrap(`(${check} ? [${okOut}, true] : [${zero}, false])`);
1506
1782
  },
1507
1783
 
1784
+ // The error a failed `x.(T)` throws: Go's `interface conversion: ...` text
1785
+ // naming the static interface type, the dynamic type and the target.
1786
+ _typeAssertFailJs(expr, val) {
1787
+ const target = expr._type;
1788
+ const u = this._underlyingOf(target);
1789
+ const methods =
1790
+ u?.kind === "interface" ? [...(u.methods?.keys() ?? [])] : [];
1791
+ const tName = this._assertTypeName(target) ?? goTypeName(expr.type);
1792
+ const src = this._assertTypeName(expr.expr?._type) ?? "interface {}";
1793
+ return `__tafail(${JSON.stringify(src)}, ${val}, ${JSON.stringify(tName)}, ${methods.length ? JSON.stringify(methods.sort()) : "null"})`;
1794
+ },
1795
+
1796
+ // Go's spelling of the static type in a failed assertion message; null for
1797
+ // `any` (`interface {}`).
1798
+ _assertTypeName(t) {
1799
+ if (!t || isAny(t)) return null;
1800
+ return t.name === "error" ? "error" : goTypeName(t);
1801
+ },
1802
+
1508
1803
  // `csv.ParseError` names the class of the bundled Go-source package type.
1509
1804
  _qualifiedStructName(name) {
1510
1805
  if (!name.includes(".") || this.structNames.has(name)) return name;
@@ -1512,20 +1807,37 @@ export const expressionGenMethods = {
1512
1807
  return cls && this.structNames.has(cls) ? cls : name;
1513
1808
  },
1514
1809
 
1810
+ _simdCheckExpr(name, val) {
1811
+ if (name === "simd.F32x4" || name === "F32x4")
1812
+ return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "number")`;
1813
+ if (name === "simd.Mask32x4" || name === "Mask32x4")
1814
+ return `(Array.isArray(${val}) && ${val}.length === 4 && typeof ${val}[0] === "boolean")`;
1815
+ return null;
1816
+ },
1817
+
1515
1818
  _typeNameCheckExpr(typeName, val) {
1516
1819
  // A type parameter: the instantiated type's test travels with its factory.
1517
1820
  const factory = this._typeParamZeroFactory(typeName);
1518
1821
  if (factory) return `(${factory}?.is?.(${val}) ?? true)`;
1519
1822
  const name = this._qualifiedStructName(typeName);
1823
+ // int64 and uint64 share the BigInt: a value out of int64's range can
1824
+ // only be a uint64.
1825
+ if (name === "int64")
1826
+ return `(typeof ${val} === "bigint" && ${val} >= -9223372036854775808n && ${val} <= 9223372036854775807n)`;
1827
+ if (name === "uint64")
1828
+ return `(typeof ${val} === "bigint" && ${val} >= 0n)`;
1829
+ // Integer types share the JS number with the floats: only a whole
1830
+ // number is one, so a fraction or -0 in an `any` falls through to float64.
1831
+ if (INT_TYPE_NAMES.has(name))
1832
+ return `(Number.isSafeInteger(${val}) && !Object.is(${val}, -0))`;
1520
1833
  if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
1834
+ if (name === "complex128" || name === "complex64")
1835
+ return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.re === "number" && typeof ${val}.im === "number")`;
1521
1836
  if (name === "string") return `typeof ${val} === "string"`;
1522
1837
  if (name === "bool") return `typeof ${val} === "boolean"`;
1523
1838
  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")`;
1839
+ const simd = this._simdCheckExpr(name, val);
1840
+ if (simd) return simd;
1529
1841
  if (name === "error")
1530
1842
  return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
1531
1843
  if (this.structNames.has(name))
@@ -1534,12 +1846,20 @@ export const expressionGenMethods = {
1534
1846
  const namedInfo = this._namedBoxInfo(named);
1535
1847
  if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
1536
1848
  // User-declared interfaces: check the dynamic value has every method.
1537
- if (!name.includes(".") && named?.underlying?.kind === "interface")
1849
+ if (named?.underlying?.kind === "interface")
1538
1850
  return this._ifaceMethodCheck(
1539
1851
  val,
1540
1852
  [...named.underlying.methods.keys()],
1541
1853
  this._unwrapIsMulti(named.underlying.methods.get("Unwrap")),
1542
1854
  );
1855
+ if (this._isTaggedFuncType(named)) {
1856
+ this.useHelper("ifaceBox");
1857
+ return `__isty(${val}, ${JSON.stringify(name)}, "namedfunc")`;
1858
+ }
1859
+ if (this._isNamedComposite(named)) {
1860
+ this.useHelper("ifaceBox");
1861
+ return `__isty(${val}, ${JSON.stringify(name)}, "named")`;
1862
+ }
1543
1863
  return "true"; // unknown type — can't check at runtime
1544
1864
  },
1545
1865
 
@@ -1573,11 +1893,14 @@ export const expressionGenMethods = {
1573
1893
  },
1574
1894
 
1575
1895
  _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
- );
1896
+ const checks = methodNames.map((m) => {
1897
+ const base =
1898
+ m === "Unwrap" && multiUnwrap !== null
1899
+ ? `typeof ${val}.${m} === "function" && (${val}.${m}.__multi === true) === ${multiUnwrap}`
1900
+ : `typeof ${val}.${m} === "function"`;
1901
+ // A struct value's method set lacks the pointer-receiver methods.
1902
+ return `${base} && !(${val}.__v === true && ${val}.constructor.__pm?.includes(${JSON.stringify(m)}))`;
1903
+ });
1581
1904
  return `(${[`${val} != null`, ...checks].join(" && ")})`;
1582
1905
  },
1583
1906
 
@@ -1586,6 +1909,21 @@ export const expressionGenMethods = {
1586
1909
  // for a type parameter).
1587
1910
  _assertedValueJs(typeNode, val) {
1588
1911
  if (this._isNamedBoxTypeNode(typeNode)) return `${val}.__gfUnbox()`;
1912
+ if (
1913
+ this._isStructPtrTypeNode(typeNode) ||
1914
+ (typeNode?.kind === "PointerType" && typeNode._assertType)
1915
+ ) {
1916
+ this.useHelper("ifaceBox");
1917
+ return `__unnil(${val})`;
1918
+ }
1919
+ if (
1920
+ typeNode?._assertType?.kind === "slice" ||
1921
+ this._isNamedSliceTypeNode(typeNode)
1922
+ ) {
1923
+ // A nil slice in an interface is a tagged empty array.
1924
+ this.useHelper("ifaceBox");
1925
+ return `__unnil(${val})`;
1926
+ }
1589
1927
  const factory =
1590
1928
  typeNode?.kind === "TypeName"
1591
1929
  ? this._typeParamZeroFactory(typeNode.name)
@@ -1593,6 +1931,29 @@ export const expressionGenMethods = {
1593
1931
  return factory ? `(${factory}?.u ? ${val}.__gfUnbox() : ${val})` : val;
1594
1932
  },
1595
1933
 
1934
+ // A type name that denotes a defined slice type (`type S []int`).
1935
+ _isNamedSliceTypeNode(typeNode) {
1936
+ if (typeNode?.kind !== "TypeName") return false;
1937
+ const named = this.checker?.types.get(typeNode.name);
1938
+ return (
1939
+ this._isNamedComposite(named) &&
1940
+ this._underlyingOf(named)?.kind === "slice"
1941
+ );
1942
+ },
1943
+
1944
+ _isStructPtrTypeNode(typeNode) {
1945
+ const base = typeNode?.kind === "PointerType" ? typeNode.base : null;
1946
+ const name =
1947
+ base?.kind === "TypeName"
1948
+ ? base.name
1949
+ : base?.kind === "GenericTypeName"
1950
+ ? this._dequalify(base.name)
1951
+ : null;
1952
+ return (
1953
+ name !== null && this.structNames.has(this._qualifiedStructName(name))
1954
+ );
1955
+ },
1956
+
1596
1957
  _isNamedBoxTypeNode(typeNode) {
1597
1958
  if (typeNode?.kind === "TypeName")
1598
1959
  return Boolean(
@@ -1630,11 +1991,14 @@ export const expressionGenMethods = {
1630
1991
  arg,
1631
1992
  );
1632
1993
  if (structConv) return structConv;
1633
- const isRaw =
1634
- targetType?.name === "json.RawMessage" ||
1635
- targetType?.name === "encoding/json.RawMessage";
1636
1994
  const base =
1637
1995
  targetType?.kind === "named" ? targetType.underlying : targetType;
1996
+ if (base?.kind === "map" && this._isNilLiteral(expr.args[0]))
1997
+ return this._nilMapZero();
1998
+ if (this._isTaggedFuncType(targetType)) {
1999
+ this.useHelper("ifaceBox");
2000
+ return `__tfn(${arg}, ${JSON.stringify(targetType.name)})`;
2001
+ }
1638
2002
  if (
1639
2003
  targetType?.kind === "named" &&
1640
2004
  base?.kind === "basic" &&
@@ -1651,19 +2015,29 @@ export const expressionGenMethods = {
1651
2015
  (elem === "byte" || elem === "uint8") &&
1652
2016
  this._isStringExprType(expr.args[0]?._type)
1653
2017
  ) {
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})`;
2018
+ this.useHelper("u8enc");
2019
+ return `__u8enc(${arg})`;
1659
2020
  }
1660
2021
  }
1661
2022
  return arg;
1662
2023
  },
1663
2024
 
2025
+ // `json.Marshal(v)` / `enc.Encode(v)` call the generated codec entry
2026
+ // (see src/lower/json-codecs.js); a stream method takes its receiver first.
2027
+ _jsonEntryCall(expr) {
2028
+ const sel = expr.func;
2029
+ const func = { kind: "Ident", name: sel._genEntry, _type: sel._type };
2030
+ const args = sel._jsonStream ? [sel.expr, ...expr.args] : expr.args;
2031
+ return { ...expr, func, args };
2032
+ },
2033
+
1664
2034
  genCall(expr) {
2035
+ if (expr.func._genEntry) return this.genCall(this._jsonEntryCall(expr));
1665
2036
  if (expr._isTypeConversion) return this._genTypeConversionCall(expr);
1666
2037
 
2038
+ const native = this._genNativeCall(expr);
2039
+ if (native !== null) return native;
2040
+
1667
2041
  const funcExpr =
1668
2042
  expr.func.kind === "InstantiationExpr" ? expr.func.expr : expr.func;
1669
2043
  if (funcExpr.kind === "Ident") {
@@ -1671,18 +2045,30 @@ export const expressionGenMethods = {
1671
2045
  if (builtin !== undefined) return builtin;
1672
2046
  }
1673
2047
 
2048
+ const viaBox = this._genTypeParamMethodCall(expr);
2049
+ if (viaBox !== null) return viaBox;
2050
+
1674
2051
  if (expr.func.kind === "SelectorExpr") {
1675
2052
  const result = this._genSelectorCall(expr);
1676
2053
  if (result !== undefined) return result;
1677
2054
  }
1678
2055
 
2056
+ const converted = this._convertedPointerMethodCall(expr);
2057
+ if (converted) return converted;
2058
+
1679
2059
  const nilSafe = this._nilSafeMethodClass(expr.func);
1680
2060
  if (nilSafe) {
1681
2061
  // The method tests its receiver for nil: call it through the
1682
2062
  // prototype so a nil `this` reaches it instead of a TypeError.
1683
2063
  const recv = this.genExpr(expr.func.expr);
1684
2064
  const rest = this._genCallArgs(expr);
1685
- return `${nilSafe}.prototype.${expr.func.field}.call(${rest ? `${recv}, ${rest}` : recv})`;
2065
+ const m = expr.func.field;
2066
+ const viaProto = (r) =>
2067
+ `${nilSafe}.prototype.${m}.call(${rest ? `${r}, ${rest}` : r})`;
2068
+ // A non-nil variable keeps the direct (faster) call.
2069
+ if (/^[\w$]+$/.test(recv))
2070
+ return `(${recv} != null ? ${recv}.${m}(${rest}) : ${viaProto(recv)})`;
2071
+ return viaProto(recv);
1686
2072
  }
1687
2073
 
1688
2074
  expr.func._callee = true;
@@ -1694,15 +2080,91 @@ export const expressionGenMethods = {
1694
2080
  return `${fn}(${args ? `${zeros}, ${args}` : zeros})`;
1695
2081
  },
1696
2082
 
2083
+ // `x.M(...)` on a type-parameter value: a raw value of a named non-struct
2084
+ // type argument is boxed (by the type's factory) to reach its methods.
2085
+ _genTypeParamMethodCall(expr) {
2086
+ if (!this._isTypeParamMethodCall(expr)) return null;
2087
+ const factory = this._typeParamZeroFactory(expr.func.expr._type.name);
2088
+ if (!factory) return null;
2089
+ this.useHelper("generics");
2090
+ let recv = `__tbox(${factory}, ${this.genExpr(expr.func.expr)})`;
2091
+ // `P(&v).M()` with `P interface{ *T; M() }`: a pointer to a boxed
2092
+ // type-parameter variable is the box, so reach the value it holds.
2093
+ if (this._hasPointerTypeSet(expr.func.expr._type))
2094
+ recv = `((__r) => typeof __r?.${expr.func.field} === "function" ? __r : __r?.value)(${recv})`;
2095
+ const args = this._genCallArgs(expr);
2096
+ return `${recv}.${expr.func.field}(${args})`;
2097
+ },
2098
+
2099
+ // Whether type parameter `tp` is constrained to a single `*T` type-set term.
2100
+ _hasPointerTypeSet(tp) {
2101
+ const c = tp.constraint;
2102
+ const iface = c?.kind === "named" ? c.underlying : c;
2103
+ const terms = iface?.unionConstraint?.terms;
2104
+ return (
2105
+ terms?.length === 1 &&
2106
+ (terms[0].resolved?.kind === "pointer" ||
2107
+ terms[0].type?.kind === "PointerType")
2108
+ );
2109
+ },
2110
+
2111
+ // `(*S)(p).M()` where `p` points at another struct type: the JS object keeps
2112
+ // its own class, so call S's prototype method on it.
2113
+ _convertedPointerMethodCall(expr) {
2114
+ const sel = expr.func;
2115
+ if (sel?.kind !== "SelectorExpr" || sel._isMethodExpr) return null;
2116
+ let recv = sel.expr;
2117
+ while (recv?.kind === "ParenExpr") recv = recv.expr;
2118
+ if (recv?._isTypeConversion !== true) return null;
2119
+ const target = recv._conversionTargetType;
2120
+ const from = recv.args?.[0]?._type;
2121
+ if (target?.kind !== "pointer" || target.base?.kind !== "named")
2122
+ return null;
2123
+ if (from?.kind !== "pointer" || from.base === target.base) return null;
2124
+ if (target.base.underlying?.kind !== "struct") return null;
2125
+ const cls = target.base.name;
2126
+ if (!target.base.methods?.has(sel.field)) return null;
2127
+ const rest = this._genCallArgs(expr);
2128
+ const obj = this.genExpr(recv.args[0]);
2129
+ return `${cls}.prototype.${sel.field}.call(${rest ? `${obj}, ${rest}` : obj})`;
2130
+ },
2131
+
2132
+ // The class whose prototype holds pointer-receiver method `field`, for a
2133
+ // pointer to a struct type that declares it directly (not promoted), so a
2134
+ // method value or expression can reach it with a nil receiver; else null.
2135
+ _ptrMethodProtoClass(ptr, field, sel) {
2136
+ if (ptr?.kind !== "pointer" || ptr.base?.kind !== "named") return null;
2137
+ if (ptr.base.underlying?.kind !== "struct") return null;
2138
+ if (sel && this._promotedPath(sel)) return null;
2139
+ if (!ptr.base.methods?.has(field)) return null;
2140
+ return ptr.base.name;
2141
+ },
2142
+
1697
2143
  // The class of a pointer-receiver struct method that accepts a nil receiver
1698
2144
  // when `func` selects it on a pointer of exactly that struct, else null.
1699
2145
  _nilSafeMethodClass(func) {
1700
2146
  if (func?.kind !== "SelectorExpr" || func._isMethodExpr) return null;
1701
2147
  const t = func.expr?._type;
1702
2148
  if (t?.kind !== "pointer" || t.base?.kind !== "named") return null;
1703
- const cls = t.base.name;
1704
- if (t.base.typeArgs?.length) return null;
1705
- return this.nilSafeMethods?.has(`${cls}.${func.field}`) ? cls : null;
2149
+ // An instance of a generic struct (`Gen[int]`) shares its class.
2150
+ const cls = t.base.typeArgs?.length
2151
+ ? this._structClassName(t.base)
2152
+ : t.base.name;
2153
+ if (!cls) return null;
2154
+ if (
2155
+ t.base.underlying?.kind === "struct" &&
2156
+ !this._promotedPath(func) &&
2157
+ this.nilSafeMethods?.has(`${cls}.${func.field}`)
2158
+ )
2159
+ return cls;
2160
+ // A Go-source stdlib type (`Buffer$$bytes`) is declared outside this
2161
+ // package: its method decl tells whether it tests the receiver for nil.
2162
+ const decl = cls.includes("$$")
2163
+ ? t.base.methods?.get(func.field)?._methodDecl
2164
+ : null;
2165
+ return decl?.recvPointer && comparesToNil(decl.body, decl.recvName)
2166
+ ? cls
2167
+ : null;
1706
2168
  },
1707
2169
 
1708
2170
  // The leading zero-value factories of a call to a generic function
@@ -1741,79 +2203,7 @@ export const expressionGenMethods = {
1741
2203
  return recvType?.name ?? recvType?.base?.name;
1742
2204
  },
1743
2205
 
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
2206
  _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
2207
  if (recvName?.startsWith("shared.") && expr.func.field === "Subarray") {
1818
2208
  const args = expr.args.map((a) => this.genExpr(a)).join(", ");
1819
2209
  return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
@@ -1842,28 +2232,6 @@ export const expressionGenMethods = {
1842
2232
  }
1843
2233
  if (expr.func.field === "Wait") return "(void 0)";
1844
2234
  }
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
2235
  return undefined;
1868
2236
  },
1869
2237
 
@@ -1889,12 +2257,44 @@ export const expressionGenMethods = {
1889
2257
  const ns = expr.func.expr.name;
1890
2258
  const fn = expr.func.field;
1891
2259
  const result = this._genStdlibCall(ns, fn, expr);
1892
- if (result !== undefined) return result;
2260
+ if (result !== undefined) {
2261
+ if (this._is64BitInt(expr._type)) {
2262
+ return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${result})`;
2263
+ }
2264
+ if (
2265
+ expr._type?.kind === "tuple" &&
2266
+ expr._type.types.some((t) => this._is64BitInt(t))
2267
+ ) {
2268
+ const casts = expr._type.types.map((t, i) =>
2269
+ this._is64BitInt(t)
2270
+ ? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
2271
+ : `__r[${i}]`,
2272
+ );
2273
+ return `((__r) => [${casts.join(", ")}])(${result})`;
2274
+ }
2275
+ return result;
2276
+ }
1893
2277
  }
1894
2278
  const recvType = expr.func.expr._type;
1895
2279
  const recvName = this._resolveRecvName(recvType);
1896
2280
  const typed = this._genReceiverTypeCall(recvName, expr);
1897
- if (typed !== undefined) return typed;
2281
+ if (typed !== undefined) {
2282
+ if (this._is64BitInt(expr._type)) {
2283
+ return `((__r) => typeof __r === "number" ? BigInt(__r) : __r)(${typed})`;
2284
+ }
2285
+ if (
2286
+ expr._type?.kind === "tuple" &&
2287
+ expr._type.types.some((t) => this._is64BitInt(t))
2288
+ ) {
2289
+ const casts = expr._type.types.map((t, i) =>
2290
+ this._is64BitInt(t)
2291
+ ? `typeof __r[${i}] === "number" ? BigInt(__r[${i}]) : __r[${i}]`
2292
+ : `__r[${i}]`,
2293
+ );
2294
+ return `((__r) => [${casts.join(", ")}])(${typed})`;
2295
+ }
2296
+ return typed;
2297
+ }
1898
2298
  const namedCall = this._genNamedMethodCall(expr);
1899
2299
  if (namedCall !== undefined) return namedCall;
1900
2300
  if (this.strict && this._isInterfaceType(recvType)) {
@@ -1902,14 +2302,22 @@ export const expressionGenMethods = {
1902
2302
  // TypeError with a different message.
1903
2303
  this.useHelper("nilCall");
1904
2304
  const recv = this.genExpr(expr.func.expr);
1905
- const args = expr.args
1906
- .map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
1907
- .join(", ");
2305
+ const args = this._genCallArgs(expr);
1908
2306
  return `__nilcall(${recv}).${expr.func.field}(${args})`;
1909
2307
  }
1910
2308
  return undefined;
1911
2309
  },
1912
2310
 
2311
+ // `len`/`cap` of an array or array pointer is a constant, but an operand
2312
+ // containing a call or receive is still evaluated (and a nil pointer is
2313
+ // not dereferenced).
2314
+ _genConstLen(expr) {
2315
+ const arg = expr.args[0];
2316
+ if (arg && hasCallOrReceive(arg))
2317
+ return `(${this.genExpr(arg)}, ${expr._constLen})`;
2318
+ return String(expr._constLen);
2319
+ },
2320
+
1913
2321
  _isInterfaceType(t) {
1914
2322
  if (!t) return false;
1915
2323
  if (t.kind === "interface") return true;
@@ -1917,13 +2325,13 @@ export const expressionGenMethods = {
1917
2325
  },
1918
2326
 
1919
2327
  _genBuiltinLen(expr) {
1920
- if (expr._constLen != null) return String(expr._constLen);
2328
+ if (expr._constLen != null) return this._genConstLen(expr);
1921
2329
  const arg = expr.args[0];
1922
2330
  const t = arg?._type;
1923
2331
  const wrapField = this._namedWrapperField(t, arg);
1924
2332
  if (wrapField) return `${this.genExpr(arg)}.${wrapField}.length`;
1925
2333
  const js = this.genExpr(arg);
1926
- if (t?.kind === "map") return `Object.keys(${js}).length`;
2334
+ if (t?.kind === "map") return `Object.keys(${js} ?? {}).length`;
1927
2335
  this.useHelper("len");
1928
2336
  return `__len(${js})`;
1929
2337
  },
@@ -1954,9 +2362,9 @@ export const expressionGenMethods = {
1954
2362
  // contributes its UTF-8 bytes.
1955
2363
  _genSliceSource(node) {
1956
2364
  const js = this.genExpr(node);
1957
- return this._isStringExprType(node._type)
1958
- ? `new TextEncoder().encode(${js})`
1959
- : js;
2365
+ if (!this._isStringExprType(node._type)) return js;
2366
+ this.useHelper("u8enc");
2367
+ return `__u8enc(${js})`;
1960
2368
  },
1961
2369
 
1962
2370
  genAppend(expr) {
@@ -1981,12 +2389,23 @@ export const expressionGenMethods = {
1981
2389
  const st = expr._type ?? expr.args[0]._type;
1982
2390
  const elemT = (st?.kind === "named" ? st.underlying : st)?.elem;
1983
2391
  const spreadOf = (a) =>
1984
- this._aggregateBase(elemT)?.kind === "array"
2392
+ this._aggregateBase(elemT)
1985
2393
  ? `...(${this.genExpr(a)} ?? []).map((__e) => ${this._cloneJs(elemT, "__e")})`
1986
2394
  : `...(${this._genSliceSource(a)} ?? [])`;
1987
- const elems = expr.args
1988
- .slice(1)
1989
- .map((a) => (a._spread ? spreadOf(a) : this.genValueExpr(a)));
2395
+ const is64Elem = this._is64BitInt(elemT);
2396
+ const elems = expr.args.slice(1).map((a) => {
2397
+ if (a._spread) return spreadOf(a);
2398
+ const val = this.genValueExpr(a);
2399
+ if (
2400
+ is64Elem &&
2401
+ a._type?.kind === "untyped" &&
2402
+ !val.endsWith("n") &&
2403
+ /^-?\d+$/.test(val)
2404
+ ) {
2405
+ return `${val}n`;
2406
+ }
2407
+ return val;
2408
+ });
1990
2409
  if (elems.length === 0) return slice;
1991
2410
  this.useHelper("append");
1992
2411
  const sliceType = expr._type ?? expr.args[0]._type;
@@ -1995,11 +2414,35 @@ export const expressionGenMethods = {
1995
2414
  (sliceType.kind === "named" ? sliceType.underlying : sliceType).elem,
1996
2415
  )
1997
2416
  : null;
1998
- if (typedCtor)
1999
- return `__append((${slice}) ?? new ${typedCtor}(0), ${elems.join(", ")})`;
2417
+ if (typedCtor) {
2418
+ const single = expr.args.length === 2 && expr.args[1]._spread;
2419
+ // One source slice or string: append it as it is, not spread.
2420
+ if (single)
2421
+ return `__appendv((${slice}) ?? __tn(${typedCtor}), ${this._genSliceSource(expr.args[1])} ?? [])`;
2422
+ return `__append((${slice}) ?? __tn(${typedCtor}), ${elems.join(", ")})`;
2423
+ }
2424
+ // A json codec grows a slice it has just made and no one else holds:
2425
+ // pushing in place is O(1), where __append copies the whole slice.
2426
+ if (
2427
+ this._inJsonCodecFile() &&
2428
+ expr.args.length === 2 &&
2429
+ expr.args[0].kind === "Ident" &&
2430
+ !expr.args[1]._spread
2431
+ ) {
2432
+ this.useHelper("pushs");
2433
+ return `__pushs(${slice}, ${elems[0]})`;
2434
+ }
2000
2435
  return `__append(${slice}, ${elems.join(", ")})`;
2001
2436
  },
2002
2437
 
2438
+ // A `make` length or capacity expression wrapped in its negative-value
2439
+ // check (a literal needs none).
2440
+ _guardMakeSize(code, guard) {
2441
+ if (/^\d+$/.test(code)) return code;
2442
+ this.useHelper("mklen");
2443
+ return `${guard}(${code})`;
2444
+ },
2445
+
2003
2446
  genMake(expr) {
2004
2447
  // make([]T, n) or make([]T, n, cap) → new Array(n).fill(zero)
2005
2448
  // make(map[K]V) → {}
@@ -2007,12 +2450,16 @@ export const expressionGenMethods = {
2007
2450
  const typeNode = typeArg.kind === "TypeExpr" ? typeArg.type : typeArg;
2008
2451
  // A defined type (`type S []int`) makes like its underlying type.
2009
2452
  const madeType =
2010
- typeArg._type?.kind === "named"
2453
+ typeArg._coreType ??
2454
+ (typeArg._type?.kind === "named"
2011
2455
  ? typeArg._type.underlying
2012
- : typeArg._type;
2456
+ : typeArg._type);
2013
2457
  const resolvedKind = madeType?.kind;
2014
2458
  if (typeNode.kind === "SliceType" || resolvedKind === "slice") {
2015
- const n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
2459
+ let n = expr.args[1] ? this.genExpr(expr.args[1]) : "0";
2460
+ if (expr.args[1]) n = this._asIndexNumber(n, expr.args[1]._type);
2461
+ // A negative length or capacity panics with Go's message.
2462
+ n = this._guardMakeSize(n, "__mkn");
2016
2463
  const elemNode = typeNode.kind === "SliceType" ? typeNode.elem : null;
2017
2464
  const elemResolved = madeType?.kind === "slice" ? madeType.elem : null;
2018
2465
  const typedCtor =
@@ -2025,8 +2472,13 @@ export const expressionGenMethods = {
2025
2472
  : null);
2026
2473
  // make([]T, n, cap) allocates cap elements and returns a view of
2027
2474
  // 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");
2475
+ let capArg = expr.args[2] ? this.genExpr(expr.args[2]) : null;
2476
+ if (capArg && expr.args[2])
2477
+ capArg = this._asIndexNumber(capArg, expr.args[2]._type);
2478
+ if (capArg) {
2479
+ this.useHelper("slice");
2480
+ capArg = this._guardMakeSize(capArg, "__mkc");
2481
+ }
2030
2482
  const withCap = (alloc) =>
2031
2483
  capArg ? `__mkslice(${n}, ${alloc(capArg)})` : alloc(n);
2032
2484
  if (typedCtor) {
@@ -2045,24 +2497,18 @@ export const expressionGenMethods = {
2045
2497
  }
2046
2498
  return withCap((k) => `new Array(${k}).fill(${zero})`);
2047
2499
  }
2048
- // map or fallback
2500
+ // map or fallback. The json codecs' maps hold decoded keys, among them
2501
+ // "__proto__": without a prototype that is an ordinary key.
2502
+ if (
2503
+ this._inJsonCodecFile() &&
2504
+ (resolvedKind === "map" || typeNode.kind === "MapType")
2505
+ )
2506
+ return "Object.create(null)";
2049
2507
  return "{}";
2050
2508
  },
2051
2509
 
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(", ");
2510
+ _inJsonCodecFile() {
2511
+ return this._jsonCodecFileIdx === (this._currentSrcFileIdx ?? 0);
2066
2512
  },
2067
2513
 
2068
2514
  _isSliceOrArrayType(t) {
@@ -2322,10 +2768,6 @@ export const expressionGenMethods = {
2322
2768
  const t = expr.typeExpr;
2323
2769
  if (t === null) return this._genImplicitCompositeLit(expr);
2324
2770
  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
2771
  if (typeName === "simd.Mask32x4" || typeName === "Mask32x4")
2330
2772
  return "Object.freeze([false, false, false, false])";
2331
2773
  if (typeName === "simd.F32x4" || typeName === "F32x4") {
@@ -2445,26 +2887,44 @@ export const expressionGenMethods = {
2445
2887
  // ── Helpers ───────────────────────────────────────────────────
2446
2888
 
2447
2889
  _genComplexOperand(expr) {
2448
- if (isComplex(expr._type)) return this.genExpr(expr);
2890
+ if (isComplex(expr._type) || expr._asComplex) return this.genExpr(expr);
2449
2891
  return `{ re: ${this.genExpr(expr)}, im: 0 }`;
2450
2892
  },
2451
2893
 
2452
2894
  getTypeName(typeNode) {
2453
2895
  if (!typeNode) return null;
2454
2896
  if (typeNode._stdName) return typeNode._stdName;
2455
- if (typeNode.kind === "TypeName") return this._dequalify(typeNode.name);
2897
+ if (typeNode.kind === "TypeName")
2898
+ return this._aliasTarget(this._dequalify(typeNode.name));
2456
2899
  if (typeNode.kind === "GenericTypeName") return typeNode.name;
2457
- if (typeNode.kind === "Ident") return typeNode.name;
2900
+ if (typeNode.kind === "Ident") return this._aliasTarget(typeNode.name);
2458
2901
  if (typeNode.kind === "InstantiationExpr")
2459
2902
  return this.getTypeName(typeNode.expr);
2460
2903
  if (typeNode.kind === "SelectorExpr") {
2461
2904
  const base = this.getTypeName(typeNode.expr);
2462
- if (this.bundledPackages.has(base)) return typeNode.field;
2905
+ if (this.bundledPackages.has(base)) {
2906
+ // `p.A` for `type A = Base` in p: no class is emitted for A.
2907
+ const t = this.checker?.types.get(`${base}.${typeNode.field}`);
2908
+ return t?.kind === "named" && t.name && !t.name.includes("$$")
2909
+ ? t.name
2910
+ : typeNode.field;
2911
+ }
2463
2912
  return `${base}.${typeNode.field}`;
2464
2913
  }
2465
2914
  return null;
2466
2915
  },
2467
2916
 
2917
+ // `type A = Base`: no class is emitted for A, so it names Base's.
2918
+ _aliasTarget(name) {
2919
+ const t = this.checker?.types.get(name);
2920
+ return t?.kind === "named" &&
2921
+ t.name &&
2922
+ t.name !== name &&
2923
+ !name.includes(".")
2924
+ ? t.name
2925
+ : name;
2926
+ },
2927
+
2468
2928
  // Bundled GoFront packages are inlined, so `pkg.T` names the class `T`.
2469
2929
  _dequalify(name) {
2470
2930
  const dot = name.indexOf(".");
@@ -2514,6 +2974,16 @@ export const expressionGenMethods = {
2514
2974
  },
2515
2975
 
2516
2976
  // Runs `fn` with type parameters `names` bound to the factories `$z_<name>`.
2977
+ _withoutNatives(fn) {
2978
+ const saved = this.natives;
2979
+ this.natives = false;
2980
+ try {
2981
+ return fn();
2982
+ } finally {
2983
+ this.natives = saved;
2984
+ }
2985
+ },
2986
+
2517
2987
  _withTypeParamZeros(names, fn) {
2518
2988
  const prev = this._typeParamZeros;
2519
2989
  this._typeParamZeros = names
@@ -2534,6 +3004,7 @@ export const expressionGenMethods = {
2534
3004
  if (bound) return bound;
2535
3005
  }
2536
3006
  this.useHelper("generics");
3007
+ if (this._is64BitUnsigned(t)) return "__zu64";
2537
3008
  const zero = this.zeroValueForType(t);
2538
3009
  return Object.hasOwn(SHARED_ZERO_FACTORIES, zero)
2539
3010
  ? SHARED_ZERO_FACTORIES[zero]
@@ -2722,6 +3193,8 @@ export const expressionGenMethods = {
2722
3193
  return this._typeNameCheckExpr(typeNode.name, val);
2723
3194
  const generic = this._genericInstanceCheck(typeNode, val);
2724
3195
  if (generic) return generic;
3196
+ const dynamic = this._dynTypeCheck(typeNode, val);
3197
+ if (dynamic) return dynamic;
2725
3198
  const extended = this._namedOrIfaceCheckExpr(typeNode, val);
2726
3199
  if (extended) return extended;
2727
3200
  if (
@@ -2785,20 +3258,45 @@ export const expressionGenMethods = {
2785
3258
  const r = this._genComplexOperand(expr.right);
2786
3259
  switch (expr.op) {
2787
3260
  case "+":
2788
- return `{ re: ${l}.re + ${r}.re, im: ${l}.im + ${r}.im }`;
2789
3261
  case "-":
2790
- return `{ re: ${l}.re - ${r}.re, im: ${l}.im - ${r}.im }`;
2791
3262
  case "*":
2792
- this.useHelper("cmul");
2793
- return `__cmul(${l}, ${r})`;
2794
3263
  case "/":
2795
- this.useHelper("cdiv");
2796
- return `__cdiv(${l}, ${r})`;
3264
+ return this._genComplexArith(expr.op, l, r, isComplex64(expr._type));
2797
3265
  case "==":
2798
- return `(${l}.re === ${r}.re && ${l}.im === ${r}.im)`;
3266
+ return this._genComplexEquality("===", "&&", l, r);
2799
3267
  case "!=":
2800
- return `(${l}.re !== ${r}.re || ${l}.im !== ${r}.im)`;
3268
+ return this._genComplexEquality("!==", "||", l, r);
3269
+ }
3270
+ },
3271
+
3272
+ // `l == r` / `l != r` on complex operands: each component reads both
3273
+ // operands, so an operand that calls a function is bound once first.
3274
+ _genComplexEquality(cmp, join, l, r) {
3275
+ const pure = (js) => !js.includes("(");
3276
+ if (pure(l) && pure(r))
3277
+ return `(${l}.re ${cmp} ${r}.re ${join} ${l}.im ${cmp} ${r}.im)`;
3278
+ return `((__a, __b) => __a.re ${cmp} __b.re ${join} __a.im ${cmp} __b.im)(${l}, ${r})`;
3279
+ },
3280
+
3281
+ // `l op r` for complex operands. A complex64 result has float32
3282
+ // components, as in Go (where the arithmetic runs in float64 and the
3283
+ // result is narrowed).
3284
+ _genComplexArith(op, l, r, is64) {
3285
+ let re;
3286
+ let im;
3287
+ let call = null;
3288
+ if (op === "+" || op === "-") {
3289
+ re = `${l}.re ${op} ${r}.re`;
3290
+ im = `${l}.im ${op} ${r}.im`;
3291
+ } else {
3292
+ const helper = op === "*" ? "cmul" : "cdiv";
3293
+ this.useHelper(helper);
3294
+ call = `__${helper}(${l}, ${r})`;
2801
3295
  }
3296
+ if (!is64) return call ?? `{ re: ${re}, im: ${im} }`;
3297
+ if (call)
3298
+ return `((__z) => ({ re: Math.fround(__z.re), im: Math.fround(__z.im) }))(${call})`;
3299
+ return `{ re: Math.fround(${re}), im: Math.fround(${im}) }`;
2802
3300
  },
2803
3301
 
2804
3302
  _genBinaryExpr(expr) {
@@ -2821,6 +3319,9 @@ export const expressionGenMethods = {
2821
3319
  const nilMap = this._genNilMapCompare(expr);
2822
3320
  if (nilMap) return nilMap;
2823
3321
 
3322
+ const anyNil = this._genAnyNilCompare(expr);
3323
+ if (anyNil) return anyNil;
3324
+
2824
3325
  const tpEq = this._genTypeParamEquality(expr);
2825
3326
  if (tpEq) return tpEq;
2826
3327
 
@@ -2833,12 +3334,85 @@ export const expressionGenMethods = {
2833
3334
  const ptrCmp = this._genPointerEquality(expr);
2834
3335
  if (ptrCmp !== null) return ptrCmp;
2835
3336
 
2836
- if (this.strict && this._needsStrictOp(expr.op, expr._type)) {
3337
+ if (this._is64BitInt(expr._type)) return this._gen64BitBinaryExpr(expr);
3338
+
3339
+ if (
3340
+ (this.strict || this._isFloat32(expr._type)) &&
3341
+ this._needsStrictOp(expr.op, expr._type)
3342
+ ) {
2837
3343
  const l = this.genExpr(expr.left);
2838
3344
  const r = this.genExpr(expr.right);
2839
- return this._genStrictBinary(expr.op, l, r, expr._type);
3345
+ return this._genStrictBinary(expr.op, l, r, expr._type, expr.right._type);
3346
+ }
3347
+
3348
+ return this._genStandardBinaryExpr(expr);
3349
+ },
3350
+
3351
+ _gen64BitBinaryExpr(expr) {
3352
+ let l = this.genExpr(expr.left);
3353
+ let r = this.genExpr(expr.right);
3354
+ // An untyped operand is a literal (`3` -> `3n`) or a named constant,
3355
+ // which holds a Number.
3356
+ const lit = (js) => (/^-?\d+$/.test(js) ? `${js}n` : `BigInt(${js})`);
3357
+ if (expr.left._type?.kind === "untyped" && !l.endsWith("n")) l = lit(l);
3358
+ else if (!this._is64BitInt(expr.left?._type)) l = `BigInt(${l})`;
3359
+ if (expr.right._type?.kind === "untyped" && !r.endsWith("n")) r = lit(r);
3360
+ else if (!this._is64BitInt(expr.right?._type)) r = `BigInt(${r})`;
3361
+ return this._gen64BitBinary(expr.op, l, r, expr._type);
3362
+ },
3363
+
3364
+ _tconvBinaryOperands(expr, l, r) {
3365
+ if (
3366
+ expr.left._type?.kind === "typeParam" &&
3367
+ expr.right._type?.kind === "untyped"
3368
+ ) {
3369
+ const factory = this._typeParamZeroFactory(expr.left._type.name);
3370
+ if (factory) {
3371
+ this.useHelper("generics");
3372
+ r = `__tconv(${factory}, ${r})`;
3373
+ }
3374
+ } else if (
3375
+ expr.right._type?.kind === "typeParam" &&
3376
+ expr.left._type?.kind === "untyped"
3377
+ ) {
3378
+ const factory = this._typeParamZeroFactory(expr.right._type.name);
3379
+ if (factory) {
3380
+ this.useHelper("generics");
3381
+ l = `__tconv(${factory}, ${l})`;
3382
+ }
3383
+ }
3384
+ return [l, r];
3385
+ },
3386
+
3387
+ _coerceUntyped64(expr, l, r, isLeft64, isRight64) {
3388
+ if (isLeft64 && expr.right?._type?.kind === "untyped") {
3389
+ if (!r.endsWith("n") && /^-?\d+$/.test(r)) return [l, `${r}n`];
3390
+ } else if (isRight64 && expr.left?._type?.kind === "untyped") {
3391
+ if (!l.endsWith("n") && /^-?\d+$/.test(l)) return [`${l}n`, r];
2840
3392
  }
3393
+ return [l, r];
3394
+ },
3395
+
3396
+ _coerceMixed64(expr, l, r, isLeft64, isRight64) {
3397
+ const isShift = expr.op === "<<" || expr.op === ">>";
3398
+ if (isLeft64 && !isRight64 && !isShift) r = `BigInt(${r})`;
3399
+ else if (isRight64 && !isLeft64 && !isShift) l = `BigInt(${l})`;
3400
+ if (isShift && isRight64) r = `Number(${r})`;
3401
+ return [l, r];
3402
+ },
3403
+
3404
+ _wrapBinaryResult(expr, res, isLeft64, isRight64) {
3405
+ const isCmp =
3406
+ expr.op === "==" ||
3407
+ expr.op === "!=" ||
3408
+ expr.op === "<" ||
3409
+ expr.op === "<=" ||
3410
+ expr.op === ">" ||
3411
+ expr.op === ">=";
3412
+ return (isLeft64 || isRight64) && !isCmp ? `Number(${res})` : res;
3413
+ },
2841
3414
 
3415
+ _genStandardBinaryExpr(expr) {
2842
3416
  const jsOp = this._emittedBinaryOp(expr) ?? "/";
2843
3417
  const operand = (child, isRight) =>
2844
3418
  wrapForJsOp(
@@ -2847,17 +3421,29 @@ export const expressionGenMethods = {
2847
3421
  jsOp,
2848
3422
  isRight,
2849
3423
  );
2850
- const l = operand(expr.left, false);
3424
+ let [l, r] = this._tconvBinaryOperands(
3425
+ expr,
3426
+ operand(expr.left, false),
3427
+ operand(expr.right, true),
3428
+ );
3429
+ const isLeft64 = this._is64BitInt(expr.left?._type);
3430
+ const isRight64 = this._is64BitInt(expr.right?._type);
3431
+ [l, r] = this._coerceUntyped64(expr, l, r, isLeft64, isRight64);
3432
+ [l, r] = this._coerceMixed64(expr, l, r, isLeft64, isRight64);
2851
3433
 
2852
3434
  if (expr.op === "&^") {
2853
3435
  const rCode = this.genExpr(expr.right);
2854
- const r =
3436
+ const rP =
2855
3437
  this._emittedBinaryOp(expr.right) !== null ? `(${rCode})` : rCode;
2856
- return `${l} & ~${r}`;
3438
+ return this._wrapBinaryResult(expr, `${l} & ~${rP}`, isLeft64, isRight64);
2857
3439
  }
2858
- const r = operand(expr.right, true);
2859
3440
  if (this._isIntDivision(expr)) return `Math.trunc(${l} / ${r})`;
2860
- return `${l} ${jsOp} ${r}`;
3441
+ return this._wrapBinaryResult(
3442
+ expr,
3443
+ `${l} ${jsOp} ${r}`,
3444
+ isLeft64,
3445
+ isRight64,
3446
+ );
2861
3447
  },
2862
3448
 
2863
3449
  // `m == nil` / `m != nil` on a map: true for a nil map (null, the strict
@@ -2872,7 +3458,7 @@ export const expressionGenMethods = {
2872
3458
  if (!other) return null;
2873
3459
  const t =
2874
3460
  other._type?.kind === "named" ? other._type.underlying : other._type;
2875
- return t?.kind === "map" ? other : null;
3461
+ return t?.kind === "map" || other._coreType?.kind === "map" ? other : null;
2876
3462
  },
2877
3463
 
2878
3464
  _genNilMapCompare(expr) {
@@ -2937,6 +3523,25 @@ export const expressionGenMethods = {
2937
3523
  );
2938
3524
  },
2939
3525
 
3526
+ // The `any` operand of `x == nil` / `x != nil` (JS documents a nil slice
3527
+ // stored in an `any` as equal to nil, though it keeps a type tag), or null.
3528
+ _anyNilOperand(expr) {
3529
+ if (expr.op !== "==" && expr.op !== "!=") return null;
3530
+ const operand = this._isNilLiteral(expr.right)
3531
+ ? expr.left
3532
+ : this._isNilLiteral(expr.left)
3533
+ ? expr.right
3534
+ : null;
3535
+ return operand && this._isIfaceType(operand._type) ? operand : null;
3536
+ },
3537
+
3538
+ _genAnyNilCompare(expr) {
3539
+ const operand = this._anyNilOperand(expr);
3540
+ if (!operand) return null;
3541
+ const test = `((__a) => __a == null || __a.__nils === true)(${this.genExpr(operand)})`;
3542
+ return expr.op === "==" ? test : `!${test}`;
3543
+ },
3544
+
2940
3545
  _isNilLiteral(expr) {
2941
3546
  return (
2942
3547
  (expr.kind === "Ident" && expr.name === "nil") ||
@@ -2955,6 +3560,7 @@ export const expressionGenMethods = {
2955
3560
  return null;
2956
3561
  if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
2957
3562
  if (this._nilMapOperand(expr)) return null;
3563
+ if (this._anyNilOperand(expr)) return null;
2958
3564
  if (this._isIfaceEquality(expr)) return null;
2959
3565
  if (expr.op === "&^") return "&";
2960
3566
  if (expr.op === "==" || expr.op === "!=") {
@@ -2985,6 +3591,10 @@ export const expressionGenMethods = {
2985
3591
  return this._genArrayTypeConversion(expr, inner, t);
2986
3592
  if (t?.kind === "SliceType")
2987
3593
  return this._genSliceTypeConversion(expr, inner, t);
3594
+ // `map[K]V(nil)` is a nil map value, which stays distinct from the nil
3595
+ // interface once boxed in an `any`.
3596
+ if (t?.kind === "MapType" && this._isNilLiteral(expr.expr))
3597
+ return this._nilMapZero();
2988
3598
  const target = t?.name;
2989
3599
  const structConv = this._genStructClassConversion(
2990
3600
  target,
@@ -2995,6 +3605,7 @@ export const expressionGenMethods = {
2995
3605
  if (target === "error") {
2996
3606
  // A boxed named value that implements error is already an error.
2997
3607
  if (this._isNamedBoxKind(expr.expr)) return inner;
3608
+ if (!this._isStringExprType(expr.expr._type)) return inner;
2998
3609
  this.useHelper("error");
2999
3610
  return `__error(${inner})`;
3000
3611
  }
@@ -3025,11 +3636,25 @@ export const expressionGenMethods = {
3025
3636
  // `[]T(nil)` is the nil slice, not a copy of anything.
3026
3637
  if (this._isNilLiteral(expr.expr)) return "null";
3027
3638
  const elem = t.elem?.name;
3639
+ // Converting a nil slice yields a nil slice.
3640
+ const src = this._underlyingOf(expr.expr._type);
3641
+ if (src?.kind === "slice" && !this._sliceConvNilSafe) {
3642
+ this._sliceConvNilSafe = true;
3643
+ try {
3644
+ return `((__s) => __s == null ? null : ${this._genSliceTypeConversion(expr, "__s", t)})(${inner})`;
3645
+ } finally {
3646
+ this._sliceConvNilSafe = false;
3647
+ }
3648
+ }
3028
3649
  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}))`;
3650
+ if (this._isStringExprType(expr.expr._type)) {
3651
+ this.useHelper("u8enc");
3652
+ return `__u8enc(${inner})`;
3653
+ }
3654
+ if (isAny(expr.expr._type) || !expr.expr._type) {
3655
+ this.useHelper("u8enc");
3656
+ return `(typeof ${inner} === "string" ? __u8enc(${inner}) : new Uint8Array(${inner}))`;
3657
+ }
3033
3658
  return `new Uint8Array(${inner})`;
3034
3659
  }
3035
3660
  const typedCtor = typedArrayConstructorForElem(
@@ -3048,41 +3673,71 @@ export const expressionGenMethods = {
3048
3673
 
3049
3674
  _genStringConversion(srcType, inner) {
3050
3675
  const base = srcType?.kind === "named" ? srcType.underlying : srcType;
3051
- if (base && this.isIntType(base)) return `String.fromCodePoint(${inner})`;
3676
+ if (base && this.isIntType(base)) {
3677
+ this.useHelper("runestr");
3678
+ return `__rstr(${inner})`;
3679
+ }
3052
3680
  if (base?.kind === "slice") {
3053
3681
  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("")`;
3682
+ if (elem === "byte" || elem === "uint8") {
3683
+ this.useHelper("u8dec");
3684
+ return `__u8dec(${inner})`;
3685
+ }
3686
+ if (elem === "rune" || elem === "int" || elem === "int32") {
3687
+ this.useHelper("runestr");
3688
+ return `Array.from(${inner}, c => __rstr(c)).join("")`;
3689
+ }
3058
3690
  }
3059
3691
  return `String(${inner})`;
3060
3692
  },
3061
3693
 
3062
3694
  _genPrimitiveConversion(expr, inner, target) {
3695
+ const srcType = expr.expr?._type;
3696
+ const isSrc64 = this._is64BitInt(srcType);
3063
3697
  switch (target) {
3064
3698
  case "string":
3065
- return this._genStringConversion(expr.expr._type, inner);
3699
+ return this._genStringConversion(
3700
+ srcType,
3701
+ isSrc64 ? `Number(${inner})` : inner,
3702
+ );
3703
+ case "int64": {
3704
+ this.useHelper("to64");
3705
+ return `__to_i64(${inner})`;
3706
+ }
3707
+ case "uint64": {
3708
+ this.useHelper("to64");
3709
+ return `__to_u64(${inner})`;
3710
+ }
3066
3711
  case "int":
3067
- case "int64":
3068
3712
  case "uint":
3069
- case "uint64":
3070
3713
  case "uintptr":
3071
- return `Math.trunc(Number(${inner}))`;
3714
+ return isSrc64 ? `Number(${inner})` : `Math.trunc(Number(${inner}))`;
3072
3715
  case "int8":
3073
- return `(Number(${inner}) << 24 >> 24)`;
3716
+ return isSrc64
3717
+ ? `Number(BigInt.asIntN(8, ${inner}))`
3718
+ : `(Number(${inner}) << 24 >> 24)`;
3074
3719
  case "int16":
3075
- return `(Number(${inner}) << 16 >> 16)`;
3720
+ return isSrc64
3721
+ ? `Number(BigInt.asIntN(16, ${inner}))`
3722
+ : `(Number(${inner}) << 16 >> 16)`;
3076
3723
  case "int32":
3077
3724
  case "rune":
3078
- return `(Number(${inner}) | 0)`;
3725
+ return isSrc64
3726
+ ? `Number(BigInt.asIntN(32, ${inner}))`
3727
+ : `(Number(${inner}) | 0)`;
3079
3728
  case "uint8":
3080
3729
  case "byte":
3081
- return `(Number(${inner}) & 0xFF)`;
3730
+ return isSrc64
3731
+ ? `Number(BigInt.asUintN(8, ${inner}))`
3732
+ : `(Number(${inner}) & 0xFF)`;
3082
3733
  case "uint16":
3083
- return `(Number(${inner}) & 0xFFFF)`;
3734
+ return isSrc64
3735
+ ? `Number(BigInt.asUintN(16, ${inner}))`
3736
+ : `(Number(${inner}) & 0xFFFF)`;
3084
3737
  case "uint32":
3085
- return `(Number(${inner}) >>> 0)`;
3738
+ return isSrc64
3739
+ ? `Number(BigInt.asUintN(32, ${inner}))`
3740
+ : `(Number(${inner}) >>> 0)`;
3086
3741
  case "float32":
3087
3742
  return `Math.fround(Number(${inner}))`;
3088
3743
  case "float64":
@@ -3106,9 +3761,75 @@ export const expressionGenMethods = {
3106
3761
  return STRICT_NUMERIC_TYPES.has(typeName);
3107
3762
  },
3108
3763
 
3764
+ _is64BitInt(t) {
3765
+ return is64BitInt(t);
3766
+ },
3767
+
3768
+ // Whether a value of type `t` can be a BigInt at run time: a 64-bit
3769
+ // integer, or a type parameter whose type set holds one (`~int | ~int64`).
3770
+ _mayBeBigInt(t) {
3771
+ if (t?.kind !== "typeParam") return is64BitInt(t);
3772
+ const terms = this.checker?._constraintTerms(t.constraint, new Set());
3773
+ return terms?.some(is64BitInt) === true;
3774
+ },
3775
+
3776
+ // `code` as a JS number for an index, slice bound or `make` size.
3777
+ _asIndexNumber(code, t) {
3778
+ return this._mayBeBigInt(t) ? `Number(${code})` : code;
3779
+ },
3780
+
3781
+ // float32 arithmetic rounds through Math.fround and arithmetic on a type
3782
+ // parameter wraps for the instantiated type (`__tar`) in every package kind.
3783
+ _isFloat32(t) {
3784
+ return (
3785
+ this._typeName(t) === "float32" ||
3786
+ (t?.kind === "typeParam" && this._typeParamZeroFactory(t.name) != null)
3787
+ );
3788
+ },
3789
+
3790
+ _is64BitUnsigned(t) {
3791
+ return is64BitUnsigned(t);
3792
+ },
3793
+
3794
+ _gen64BitBinary(op, l, r, type) {
3795
+ const unsigned = this._is64BitUnsigned(type);
3796
+ const wrap = unsigned ? "BigInt.asUintN" : "BigInt.asIntN";
3797
+ const divZero =
3798
+ '(() => { throw new Error("runtime error: integer divide by zero"); })()';
3799
+ if (op === "/" || op === "%") {
3800
+ // The divisor is read twice (zero check, division): bind both
3801
+ // operands once, left first, unless they are plain names or literals.
3802
+ const simple = /^[A-Za-z_$][\w$.]*$|^-?\d+n$/;
3803
+ if ((simple.test(l) && simple.test(r)) || /\b(await|yield)\b/.test(l + r))
3804
+ return `((${r}) === 0n ? ${divZero} : ${wrap}(64, (${l}) ${op} (${r})))`;
3805
+ return `((__a, __b) => __b === 0n ? ${divZero} : ${wrap}(64, __a ${op} __b))(${l}, ${r})`;
3806
+ }
3807
+ if (op === "<<" || op === ">>") {
3808
+ this.useHelper("shift");
3809
+ return op === "<<"
3810
+ ? `__shl64(${l}, ${r}, ${unsigned})`
3811
+ : `__shr64(${l}, ${r}, ${unsigned})`;
3812
+ }
3813
+ if (op === "&^") return `${wrap}(64, (${l}) & ~(${r}))`;
3814
+ return `${wrap}(64, (${l}) ${op} (${r}))`;
3815
+ },
3816
+
3817
+ // `uint` / `uintptr`: 64-bit unsigned values held as JS numbers.
3818
+ _isUintWord(type) {
3819
+ const name = this._typeName(type);
3820
+ return name === "uint" || name === "uintptr";
3821
+ },
3822
+
3109
3823
  _needsStrictOp(op, type) {
3824
+ if (type?.kind === "typeParam")
3825
+ return (
3826
+ STRICT_OPS.has(op) && this._typeParamZeroFactory(type.name) != null
3827
+ );
3828
+ if (this._is64BitInt(type))
3829
+ return STRICT_OPS.has(op) || op === "<<" || op === ">>" || op === "&^";
3110
3830
  if (!STRICT_OPS.has(op)) return false;
3111
3831
  if (this._isStrictAtomicOp(op, this._typeName(type))) return true;
3832
+ if (op === "-" && this._isUintWord(type)) return true;
3112
3833
  if (op === "<<" || op === ">>")
3113
3834
  return WIDE_INT_NAMES.has(this._typeName(type));
3114
3835
  return this.isIntType(type) && (op === "/" || op === "%");
@@ -3116,7 +3837,14 @@ export const expressionGenMethods = {
3116
3837
 
3117
3838
  // Emits `lhs = strict(lhs op rhs)` for any lvalue shape; returns false when not applicable.
3118
3839
  // Indexed / nested targets are split into object + key temps so sub-expressions run once.
3119
- _genStrictCompound(lhsNode, op, rhsJs, srcLine = null, lhsJs = null) {
3840
+ _genStrictCompound(
3841
+ lhsNode,
3842
+ op,
3843
+ rhsJs,
3844
+ srcLine = null,
3845
+ lhsJs = null,
3846
+ countType = null,
3847
+ ) {
3120
3848
  const type = lhsNode._type;
3121
3849
  if (!this._needsStrictOp(op, type)) return false;
3122
3850
  if (lhsNode.kind === "IndexExpr" && lhsNode.expr?._type?.kind === "map")
@@ -3124,7 +3852,7 @@ export const expressionGenMethods = {
3124
3852
  const l = lhsJs ?? this._genAssignLhsExpr(lhsNode);
3125
3853
  if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/.test(l)) {
3126
3854
  this.line(
3127
- `${l} = ${this._genStrictBinary(op, l, rhsJs, type)};`,
3855
+ `${l} = ${this._genStrictBinary(op, l, rhsJs, type, countType)};`,
3128
3856
  srcLine,
3129
3857
  );
3130
3858
  return true;
@@ -3137,21 +3865,40 @@ export const expressionGenMethods = {
3137
3865
  const k = `__k${this._tmpCounter}`;
3138
3866
  const target = `${o}[${k}]`;
3139
3867
  this.line(
3140
- `{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
3868
+ `{ const ${o} = ${split.obj}, ${k} = ${split.key}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
3141
3869
  srcLine,
3142
3870
  );
3143
3871
  } else {
3144
3872
  const target = `${o}.${split.prop}`;
3145
3873
  this.line(
3146
- `{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type)}; }`,
3874
+ `{ const ${o} = ${split.obj}; ${target} = ${this._genStrictBinary(op, target, rhsJs, type, countType)}; }`,
3147
3875
  srcLine,
3148
3876
  );
3149
3877
  }
3150
3878
  return true;
3151
3879
  },
3152
3880
 
3153
- _genStrictBinary(op, l, r, type) {
3881
+ // A non-constant signed shift count on a narrow integer is checked for a
3882
+ // negative value (Go panics); 64-bit counts arrive as BigInt.
3883
+ _guardShiftCount(r, countType) {
3884
+ if (!countType || countType.kind === "untyped") return r;
3885
+ const name = this._typeName(countType);
3886
+ if (!name || name.startsWith("u")) return r;
3887
+ this.useHelper("shift");
3888
+ return `__shc(${this._is64BitInt(countType) ? `Number(${r})` : r})`;
3889
+ },
3890
+
3891
+ _genStrictBinary(op, l, r, type, countType = null) {
3892
+ if (type?.kind === "typeParam") {
3893
+ this.useHelper("generics");
3894
+ return `__tar(${this._typeParamZeroFactory(type.name)}, "${op}", ${l}, ${r})`;
3895
+ }
3896
+ if (this._is64BitInt(type)) {
3897
+ return this._gen64BitBinary(op, l, r, type);
3898
+ }
3154
3899
  const tName = this._typeName(type);
3900
+ const isShift = op === "<<" || op === ">>";
3901
+ const rc = isShift ? this._guardShiftCount(r, countType) : r;
3155
3902
  if (tName === "float32") {
3156
3903
  return `Math.fround(${l} ${op} ${r})`;
3157
3904
  }
@@ -3171,7 +3918,7 @@ export const expressionGenMethods = {
3171
3918
  if (op === "<<" || op === ">>") {
3172
3919
  const ovf = signed && op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0";
3173
3920
  const jsOp = !signed && op === ">>" ? ">>>" : op;
3174
- return `(((${r}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
3921
+ return `(((${rc}) >= 32 ? ${ovf} : ((${l}) ${jsOp} (${r}))) ${cast})`;
3175
3922
  }
3176
3923
  if (op === "&^") return `(((${l}) & ~(${r})) ${cast})`;
3177
3924
  return `((${l} ${op} ${r}) ${cast})`;
@@ -3184,7 +3931,7 @@ export const expressionGenMethods = {
3184
3931
  if (op === "%")
3185
3932
  return `((${r}) === 0 ? ${divZero} : (((((${l}) % (${r})) << ${sh}) >> ${sh})))`;
3186
3933
  if (op === "<<" || op === ">>") {
3187
- return `((${r}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
3934
+ return `((${rc}) >= ${bits} ? (${op === ">>" ? `((${l}) < 0 ? -1 : 0)` : "0"}) : (((${l}) ${op} (${r})) << ${sh} >> ${sh}))`;
3188
3935
  }
3189
3936
  if (op === "&^") return `((((${l}) & ~(${r})) << ${sh}) >> ${sh})`;
3190
3937
  return `((((${l} ${op} ${r})) << ${sh}) >> ${sh})`;
@@ -3198,11 +3945,13 @@ export const expressionGenMethods = {
3198
3945
  return `((${r}) === 0 ? ${divZero} : (((${l}) % (${r})) & ${mask}))`;
3199
3946
  if (op === "<<" || op === ">>") {
3200
3947
  const jsOp = op === ">>" ? ">>>" : "<<";
3201
- return `((${r}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
3948
+ return `((${rc}) >= ${bits} ? 0 : (((${l}) ${jsOp} (${r})) & ${mask}))`;
3202
3949
  }
3203
3950
  if (op === "&^") return `(((${l}) & ~(${r})) & ${mask})`;
3204
3951
  return `((${l} ${op} ${r}) & ${mask})`;
3205
3952
  }
3953
+ if (op === "-" && this._isUintWord(type))
3954
+ return `((__d) => __d < 0 ? __d + 2 ** 64 : __d)((${l}) - (${r}))`;
3206
3955
  if (op === "<<" || op === ">>") {
3207
3956
  // 64-bit semantics: operand first, then count (Go order); a negative
3208
3957
  // count panics and the result wraps to 64 bits.