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
@@ -11,7 +11,10 @@ import {
11
11
  compositeLitIndices,
12
12
  compositeLitMaxIndex,
13
13
  } from "../../typechecker/types.js";
14
+ import { goTypeName } from "../shared/fmt-types.js";
15
+ import { emitWasmNative } from "./natives.js";
14
16
  import {
17
+ getInterfaceMethodList,
15
18
  getMapKeyValTypes,
16
19
  isAnyType,
17
20
  isArrayType,
@@ -19,6 +22,7 @@ import {
19
22
  isComplex64,
20
23
  isFuncType,
21
24
  isMapType,
25
+ isNonEmptyInterface,
22
26
  isPointerToStruct,
23
27
  isRuneSlice,
24
28
  isScalarType,
@@ -26,7 +30,7 @@ import {
26
30
  isSliceType,
27
31
  isStringType,
28
32
  isStructType,
29
- isTestingT,
33
+ isTaggedSliceType,
30
34
  toWasmType,
31
35
  } from "./types.js";
32
36
 
@@ -101,27 +105,38 @@ export class ExprsEmitter {
101
105
  return;
102
106
  }
103
107
 
108
+ // A defined func type, and every array and map type, is boxed to keep
109
+ // its identity: arrays and maps share one wasm type across Go types
110
+ // (`[2]int` and `[2]int64`), and a defined func type has methods that
111
+ // interface dispatch must tell from the plain func type.
112
+ if (this._isMethodNamedRef(goType)) {
113
+ const box = this.mod.getIfaceBoxType(this._resolveTestTarget(goType));
114
+ this._emitRawExpr(expr, box.wType);
115
+ // An array stored in an interface is a copy.
116
+ if (isArrayType(box.goType, this.mod.checker))
117
+ this._emitCopyIfValue(expr, goType, box.wType);
118
+ this._emitBoxValue(box);
119
+ return;
120
+ }
121
+
104
122
  if (
105
123
  isSliceType(goType, this.mod.checker) ||
106
- isArrayType(goType, this.mod.checker) ||
107
- isFuncType(goType, this.mod.checker) ||
108
- isMapType(goType, this.mod.checker)
124
+ isFuncType(goType, this.mod.checker)
109
125
  ) {
110
126
  const refWType = this.toWasmType(goType);
111
127
  this._emitRawExpr(expr, refWType);
112
- // An array stored in an interface is a copy.
113
- if (isArrayType(goType, this.mod.checker))
114
- this._emitCopyIfValue(expr, goType, refWType);
115
- if (
116
- isFuncType(goType, this.mod.checker) ||
117
- isMapType(goType, this.mod.checker)
118
- )
128
+ if (isTaggedSliceType(goType, this.mod.checker))
129
+ this._emitTagSlice(goType, refWType);
130
+ if (isFuncType(goType, this.mod.checker))
119
131
  this._emitNilAwareAny(goType, expr);
120
132
  return;
121
133
  }
122
134
 
123
135
  // Scalar type -> box into struct
124
- const scalarType = goType ?? { kind: "basic", name: "int" };
136
+ // An untyped rune constant boxes as `rune` (int32), like Go.
137
+ const scalarType = goType?.rune
138
+ ? { kind: "basic", name: "int32" }
139
+ : (goType ?? { kind: "basic", name: "int" });
125
140
  const box = this.mod.getIfaceBoxType(scalarType);
126
141
  this._emitRawExpr(expr, box.wType);
127
142
  this._emitBoxValue(box);
@@ -131,6 +146,85 @@ export class ExprsEmitter {
131
146
  this._emitRawExpr(expr, targetWasmType);
132
147
  }
133
148
 
149
+ // Panics with Go's failed type assertion message: the static interface
150
+ // type, the dynamic type of the value in `anyTmp` and the target type.
151
+ _emitTypeAssertPanic(expr, anyTmp) {
152
+ const checker = this.mod.checker;
153
+ const target = this._resolveTestTarget(expr._type ?? expr.type);
154
+ const iface = isNonEmptyInterface(target, checker);
155
+ this.pushInstruction({ op: "local.get", index: anyTmp });
156
+ if (iface) {
157
+ const names = getInterfaceMethodList(target.underlying ?? target)
158
+ .map((m) => m.name)
159
+ .sort();
160
+ this._pushStringConst(names[0] ?? "");
161
+ } else {
162
+ this._pushStringConst(this._assertSrcName(expr.expr?._type));
163
+ }
164
+ this._pushStringConst(
165
+ target.name === "error" ? "error" : goTypeName(target),
166
+ );
167
+ this.pushInstruction({ op: "i32.const", value: iface ? 1 : 0 });
168
+ this.pushInstruction({
169
+ op: "call",
170
+ funcIndex: this.mod.resolveFuncIndex("__ta_fail"),
171
+ });
172
+ this.pushInstruction("unreachable");
173
+ }
174
+
175
+ // The static interface type of an assertion operand as Go names it.
176
+ _assertSrcName(srcType) {
177
+ if (!srcType || !isNonEmptyInterface(srcType, this.mod.checker))
178
+ return "interface {}";
179
+ return srcType.name === "error" ? "error" : goTypeName(srcType);
180
+ }
181
+
182
+ // Pushes the interned string `text` as an externref.
183
+ _pushStringConst(text) {
184
+ this.pushInstruction({
185
+ op: "i32.const",
186
+ value: this.mod.internString(text),
187
+ });
188
+ this.pushInstruction({
189
+ op: "call",
190
+ funcIndex: this.mod.getStringImportIndex(),
191
+ });
192
+ }
193
+
194
+ // The slice on the stack becomes the tagged subtype of its Go type, so a
195
+ // type test can tell `[]byte` from `[]bool` (see getTaggedSliceType). The
196
+ // copy shares the backing array; slice headers are immutable.
197
+ _emitTagSlice(goType, refWType) {
198
+ const tagged = this.mod.getTaggedSliceType(
199
+ goType,
200
+ this._getSliceElemType(goType),
201
+ );
202
+ const base = tagged.base;
203
+ const tmp = this.acquireTemp(refWType);
204
+ this.pushInstruction({ op: "local.tee", index: tmp });
205
+ this.pushInstruction("ref.is_null");
206
+ this.pushInstruction({
207
+ op: "if",
208
+ blockType: { kind: "ref", nullable: true, typeIndex: base.typeIndex },
209
+ });
210
+ this.pushInstruction({ op: "global.get", index: base.emptyGlobalIndex });
211
+ this.pushInstruction("else");
212
+ this.pushInstruction({ op: "local.get", index: tmp });
213
+ this.pushInstruction("end");
214
+ this.pushInstruction({ op: "local.set", index: tmp });
215
+ for (let i = 0; i < 4; i++) {
216
+ this.pushInstruction({ op: "local.get", index: tmp });
217
+ this.pushInstruction({
218
+ op: "struct.get",
219
+ typeIndex: base.typeIndex,
220
+ fieldIndex: i,
221
+ });
222
+ }
223
+ this.pushInstruction({ op: "ref.null", heapType: tagged.tagTypeIndex });
224
+ this.pushInstruction({ op: "struct.new", typeIndex: tagged.typeIndex });
225
+ this.releaseTemp(tmp, refWType);
226
+ }
227
+
134
228
  // The nullable ref on the stack becomes an interface value: a nil map, func
135
229
  // or pointer turns into the marker of its type (never a nil interface).
136
230
  _emitNilAwareAny(goType, expr) {
@@ -240,7 +334,8 @@ export class ExprsEmitter {
240
334
  break;
241
335
 
242
336
  case "CallExpr":
243
- this.emitCallExpr(expr, targetWasmType);
337
+ if (!emitWasmNative(this, expr, targetWasmType))
338
+ this.emitCallExpr(expr, targetWasmType);
244
339
  break;
245
340
 
246
341
  case "TypeConversion":
@@ -254,7 +349,6 @@ export class ExprsEmitter {
254
349
  case "SelectorExpr": {
255
350
  const deq = this._dequalify(expr);
256
351
  if (deq) this.emitIdent(deq, targetWasmType);
257
- else if (this._emitPkgConst(expr, targetWasmType)) break;
258
352
  else this.emitSelectorExpr(expr);
259
353
  break;
260
354
  }
@@ -286,7 +380,7 @@ export class ExprsEmitter {
286
380
  this._emitTypeTest(targetGoType);
287
381
  this.pushInstruction("i32.eqz");
288
382
  this.pushInstruction({ op: "if", blockType: "void" });
289
- this.emitPanic("interface conversion: type assertion failed");
383
+ this._emitTypeAssertPanic(expr, anyTmp);
290
384
  this.pushInstruction("end");
291
385
 
292
386
  this.pushInstruction({ op: "local.get", index: anyTmp });
@@ -316,7 +410,7 @@ export class ExprsEmitter {
316
410
  if (capturedNames.length === 0) {
317
411
  this.pushInstruction({ op: "ref.null", heapType: "any" });
318
412
  } else {
319
- const envFields = this._buildEnvFields(capturedNames);
413
+ const envFields = this._buildEnvFields(capturedNames, funcLit);
320
414
  const envInfo = this.mod.getEnvType(envFields);
321
415
 
322
416
  for (const field of envFields) {
@@ -327,6 +421,10 @@ export class ExprsEmitter {
327
421
  );
328
422
  }
329
423
  this.pushInstruction({ op: "local.get", index: local.index });
424
+ // A non-escaping `range n` index lives in an i32 local, but a
425
+ // closure (such as a range-over-func body) sees a Go `int`.
426
+ if (!field.isBoxed && local.type === "i32" && field.wType === "i64")
427
+ this.pushInstruction("i64.extend_i32_s");
330
428
  }
331
429
 
332
430
  this.pushInstruction({
@@ -562,55 +660,7 @@ export class ExprsEmitter {
562
660
  return this._derefStructInfo(operand) ? operand : null;
563
661
  }
564
662
 
565
- _emitPkgConst(expr, targetWasmType) {
566
- if (this._isMathConst(expr)) {
567
- this.emitMathConst(expr.field, targetWasmType);
568
- return true;
569
- }
570
- if (this._isUtf8Const(expr)) {
571
- this.emitUtf8Const(expr.field, targetWasmType);
572
- return true;
573
- }
574
- if (this._isStrconvConst(expr)) {
575
- this.emitStrconvConst(expr.field, targetWasmType);
576
- return true;
577
- }
578
- if (this._isPathConst(expr)) {
579
- this.emitPathConst(expr.field, targetWasmType);
580
- return true;
581
- }
582
- if (this._isIoConst(expr)) {
583
- this.emitIoConst(expr.field, targetWasmType);
584
- return true;
585
- }
586
- if (this._isOsConst?.(expr)) {
587
- this.emitOsConst(expr.field, targetWasmType);
588
- return true;
589
- }
590
- if (this._isTimeConst(expr)) {
591
- this.emitTimeConst(expr.field, targetWasmType);
592
- return true;
593
- }
594
- if (this._isBitsConst?.(expr)) {
595
- this.emitBitsConst(expr.field, targetWasmType);
596
- return true;
597
- }
598
- if (this._isExtendedPkgConst?.(expr)) {
599
- this.emitExtendedPkgConst(expr, targetWasmType);
600
- return true;
601
- }
602
- return false;
603
- }
604
-
605
663
  emitSelectorExpr(expr) {
606
- if (isTestingT(expr.expr?._type) && expr.field === "N") {
607
- this.emitExpr(expr.expr, "externref");
608
- this.pushInstruction({
609
- op: "call",
610
- funcIndex: this.mod.getStdlibImportIndex("testing_n"),
611
- });
612
- return;
613
- }
614
664
  const structInfo = this._resolveStructInfo(expr.expr);
615
665
  if (!structInfo) {
616
666
  if (expr.expr?.kind === "Ident" && expr.expr._type?.kind === "namespace")
@@ -1296,12 +1346,14 @@ export class ExprsEmitter {
1296
1346
  constIdx < arrayLen
1297
1347
  ) {
1298
1348
  this.emitExpr(baseNode, arrWType);
1349
+ this._emitNilCheck(rawBaseType);
1299
1350
  this.pushInstruction({ op: "i32.const", value: constIdx });
1300
1351
  return;
1301
1352
  }
1302
1353
 
1303
1354
  const arrTmp = this.acquireTemp(arrWType);
1304
1355
  this.emitExpr(baseNode, arrWType);
1356
+ this._emitNilCheck(rawBaseType);
1305
1357
  this.pushInstruction({ op: "local.tee", index: arrTmp });
1306
1358
  this._emitCheckedIndex(indexNode, () => {
1307
1359
  this.pushInstruction({ op: "local.get", index: arrTmp });
@@ -1376,6 +1428,15 @@ export class ExprsEmitter {
1376
1428
  return null;
1377
1429
  }
1378
1430
 
1431
+ // True when the target wasm type is the `$complex64` struct, whether it is
1432
+ // named or only carries the struct's type index.
1433
+ _isComplex64Target(w) {
1434
+ return (
1435
+ w?.typeName === "$complex64" ||
1436
+ this._complexKindOfWType(w) === "complex64"
1437
+ );
1438
+ }
1439
+
1379
1440
  emitBasicLit(lit, targetWasmType) {
1380
1441
  const targetComplex = this._complexKindOfWType(targetWasmType);
1381
1442
  const isTargetComplex64 =
@@ -1456,7 +1517,7 @@ export class ExprsEmitter {
1456
1517
  emitImagLit(lit, targetWasmType) {
1457
1518
  const is64 =
1458
1519
  lit._type?.name === "complex64" ||
1459
- targetWasmType?.typeName === "$complex64";
1520
+ this._isComplex64Target(targetWasmType);
1460
1521
  const complexName = is64 ? "complex64" : "complex128";
1461
1522
  const info = this.mod.getComplexType(complexName);
1462
1523
  const imVal = Number(lit.value);
@@ -1518,6 +1579,13 @@ export class ExprsEmitter {
1518
1579
  this._emitNilValue(ident._type, targetWasmType);
1519
1580
  return;
1520
1581
  }
1582
+ // A named untyped complex constant that takes type complex64 is built
1583
+ // from its initializer, like a complex literal, not read from its
1584
+ // complex128 global.
1585
+ if (ident._complexInit && this._isComplex64Target(targetWasmType)) {
1586
+ this.emitExpr(ident._complexInit, targetWasmType);
1587
+ return;
1588
+ }
1521
1589
 
1522
1590
  const localInfo = this.resolveLocal(ident.name);
1523
1591
  if (localInfo) {
@@ -1589,10 +1657,17 @@ export class ExprsEmitter {
1589
1657
  throw new Error(`Unresolved identifier: ${ident.name}`);
1590
1658
  }
1591
1659
 
1660
+ // Unary arithmetic on a complex value; `&c` and `*p` of one only move
1661
+ // its pointer.
1662
+ _isComplexArith(expr, op, operand) {
1663
+ if (op === "&" || op === "*") return false;
1664
+ return isComplex(expr._type) || isComplex(operand._type);
1665
+ }
1666
+
1592
1667
  emitUnaryExpr(expr, targetWasmType = null) {
1593
1668
  const { op, operand } = expr;
1594
1669
 
1595
- if (isComplex(expr._type) || isComplex(operand._type)) {
1670
+ if (this._isComplexArith(expr, op, operand)) {
1596
1671
  this._emitComplexUnary(expr, op, operand, targetWasmType);
1597
1672
  return;
1598
1673
  }
@@ -1681,7 +1756,7 @@ export class ExprsEmitter {
1681
1756
  const is64 =
1682
1757
  isComplex64(expr._type) ||
1683
1758
  isComplex64(operand._type) ||
1684
- targetWasmType?.typeName === "$complex64";
1759
+ this._isComplex64Target(targetWasmType);
1685
1760
  const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
1686
1761
  const cWType = {
1687
1762
  kind: "ref",
@@ -1713,19 +1788,25 @@ export class ExprsEmitter {
1713
1788
  }
1714
1789
  }
1715
1790
 
1791
+ // True when a pointer to a `t` variable is the value's own reference (an
1792
+ // array or struct) rather than a box around it (scalars and complex).
1793
+ _isOwnPointerValue(t) {
1794
+ if (this._isValueArray(t)) return true;
1795
+ return (
1796
+ t?.kind !== "pointer" &&
1797
+ !isComplex(t) &&
1798
+ isStructType(t, this.mod.checker, this.mod) &&
1799
+ !isPointerToStruct(t, this.mod.checker, this.mod)
1800
+ );
1801
+ }
1802
+
1716
1803
  _emitAddressOf(operand) {
1717
1804
  if (operand.kind === "Ident") {
1718
1805
  const loc = this.resolveLocal(operand.name);
1719
1806
  if (loc?.isBoxed) {
1720
1807
  this.pushInstruction({ op: "local.get", index: loc.index });
1721
1808
  // A pointer to an array or struct is its reference itself.
1722
- const t = loc.goType;
1723
- if (
1724
- this._isValueArray(t) ||
1725
- (t?.kind !== "pointer" &&
1726
- isStructType(t, this.mod.checker, this.mod) &&
1727
- !isPointerToStruct(t, this.mod.checker, this.mod))
1728
- )
1809
+ if (this._isOwnPointerValue(loc.goType))
1729
1810
  this.pushInstruction({
1730
1811
  op: "struct.get",
1731
1812
  typeIndex: loc.boxInfo.typeIndex,
@@ -1774,7 +1855,8 @@ export class ExprsEmitter {
1774
1855
  // The struct a `*operand` reads or writes, or null when the operand points
1775
1856
  // at something else: `*pp` with `pp **T` loads a pointer, not a struct.
1776
1857
  _derefStructInfo(operand) {
1777
- if (this._isPointerGoType(this._ptrBaseType(operand._type))) return null;
1858
+ const base = this._ptrBaseType(operand._type);
1859
+ if (this._isPointerGoType(base) || isComplex(base)) return null;
1778
1860
  return this._resolveStructInfo(operand);
1779
1861
  }
1780
1862
 
@@ -1797,6 +1879,7 @@ export class ExprsEmitter {
1797
1879
  if (!isScalarType(baseGoType, this.mod.checker, this.mod)) {
1798
1880
  const baseWType = this.toWasmType(baseGoType);
1799
1881
  this.emitExpr(operand, baseWType);
1882
+ this._emitNilCheck(operandGoType);
1800
1883
  return;
1801
1884
  }
1802
1885
  const box = this.mod.getBoxType(baseGoType);
@@ -2046,7 +2129,7 @@ export class ExprsEmitter {
2046
2129
  isComplex64(expr._type) ||
2047
2130
  isComplex64(left._type) ||
2048
2131
  isComplex64(right._type) ||
2049
- targetWasmType?.typeName === "$complex64";
2132
+ this._isComplex64Target(targetWasmType);
2050
2133
  const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
2051
2134
  const f = info.elemWType;
2052
2135
  const cWType = {
@@ -2433,10 +2516,26 @@ export class ExprsEmitter {
2433
2516
  this.releaseTemp(bTmp, arrWType);
2434
2517
  }
2435
2518
 
2519
+ // Equality of two complex values on the stack -> i32.
2520
+ _emitComplexElemEq(goType, wType) {
2521
+ const info = this.mod.getComplexType(
2522
+ isComplex64(goType) ? "complex64" : "complex128",
2523
+ );
2524
+ const bTmp = this.acquireTemp(wType);
2525
+ const aTmp = this.acquireTemp(wType);
2526
+ this.pushInstruction({ op: "local.set", index: bTmp });
2527
+ this.pushInstruction({ op: "local.set", index: aTmp });
2528
+ this._emitComplexEq(aTmp, bTmp, info, info.elemWType, "==");
2529
+ this.releaseTemp(aTmp, wType);
2530
+ this.releaseTemp(bTmp, wType);
2531
+ }
2532
+
2436
2533
  // Equality of two array elements on the stack -> i32.
2437
2534
  _emitElemEq(goType, wType) {
2438
2535
  if (this._isValueArray(goType)) {
2439
2536
  this._emitArrayEq(goType);
2537
+ } else if (isComplex(goType)) {
2538
+ this._emitComplexElemEq(goType, wType);
2440
2539
  } else if (
2441
2540
  isStructType(goType, this.mod.checker, this.mod) &&
2442
2541
  !isPointerToStruct(goType, this.mod.checker, this.mod)
@@ -2599,6 +2698,8 @@ export class ExprsEmitter {
2599
2698
  emitNarrowIntWrap(goType) {
2600
2699
  if (!goType) return;
2601
2700
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
2701
+ const declared = this.mod.checker?.types?.get(goType.name);
2702
+ if (declared?.kind === "named") return this.emitNarrowIntWrap(declared);
2602
2703
  return this.emitNarrowIntWrap({ kind: "basic", name: goType.name });
2603
2704
  }
2604
2705
  if (goType.kind === "named" && goType.underlying) {
@@ -2626,7 +2727,14 @@ export class ExprsEmitter {
2626
2727
  }
2627
2728
 
2628
2729
  emitDivRem(op, left, right, targetWasmType = null) {
2629
- const goType = left._type;
2730
+ // An untyped constant left operand takes the type of the typed right
2731
+ // one (`(1<<31) % uint32(n)` divides unsigned).
2732
+ const goType =
2733
+ left._type?.kind === "untyped" &&
2734
+ right._type &&
2735
+ right._type.kind !== "untyped"
2736
+ ? right._type
2737
+ : left._type;
2630
2738
  const wType = targetWasmType ?? toWasmType(goType, this.mod.checker);
2631
2739
 
2632
2740
  if (wType === "f32" || wType === "f64") {