gofront 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -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,6 +105,15 @@ export class ExprsEmitter {
101
105
  return;
102
106
  }
103
107
 
108
+ // A defined func or map type with methods is boxed to keep its identity,
109
+ // so interface dispatch can tell it from the plain func or map type.
110
+ if (this._isMethodNamedRef(goType)) {
111
+ const box = this.mod.getIfaceBoxType(this._resolveTestTarget(goType));
112
+ this._emitRawExpr(expr, box.wType);
113
+ this._emitBoxValue(box);
114
+ return;
115
+ }
116
+
104
117
  if (
105
118
  isSliceType(goType, this.mod.checker) ||
106
119
  isArrayType(goType, this.mod.checker) ||
@@ -112,6 +125,8 @@ export class ExprsEmitter {
112
125
  // An array stored in an interface is a copy.
113
126
  if (isArrayType(goType, this.mod.checker))
114
127
  this._emitCopyIfValue(expr, goType, refWType);
128
+ if (isTaggedSliceType(goType, this.mod.checker))
129
+ this._emitTagSlice(goType, refWType);
115
130
  if (
116
131
  isFuncType(goType, this.mod.checker) ||
117
132
  isMapType(goType, this.mod.checker)
@@ -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) {
@@ -562,55 +656,7 @@ export class ExprsEmitter {
562
656
  return this._derefStructInfo(operand) ? operand : null;
563
657
  }
564
658
 
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
659
  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
660
  const structInfo = this._resolveStructInfo(expr.expr);
615
661
  if (!structInfo) {
616
662
  if (expr.expr?.kind === "Ident" && expr.expr._type?.kind === "namespace")
@@ -1296,12 +1342,14 @@ export class ExprsEmitter {
1296
1342
  constIdx < arrayLen
1297
1343
  ) {
1298
1344
  this.emitExpr(baseNode, arrWType);
1345
+ this._emitNilCheck(rawBaseType);
1299
1346
  this.pushInstruction({ op: "i32.const", value: constIdx });
1300
1347
  return;
1301
1348
  }
1302
1349
 
1303
1350
  const arrTmp = this.acquireTemp(arrWType);
1304
1351
  this.emitExpr(baseNode, arrWType);
1352
+ this._emitNilCheck(rawBaseType);
1305
1353
  this.pushInstruction({ op: "local.tee", index: arrTmp });
1306
1354
  this._emitCheckedIndex(indexNode, () => {
1307
1355
  this.pushInstruction({ op: "local.get", index: arrTmp });
@@ -1376,6 +1424,15 @@ export class ExprsEmitter {
1376
1424
  return null;
1377
1425
  }
1378
1426
 
1427
+ // True when the target wasm type is the `$complex64` struct, whether it is
1428
+ // named or only carries the struct's type index.
1429
+ _isComplex64Target(w) {
1430
+ return (
1431
+ w?.typeName === "$complex64" ||
1432
+ this._complexKindOfWType(w) === "complex64"
1433
+ );
1434
+ }
1435
+
1379
1436
  emitBasicLit(lit, targetWasmType) {
1380
1437
  const targetComplex = this._complexKindOfWType(targetWasmType);
1381
1438
  const isTargetComplex64 =
@@ -1456,7 +1513,7 @@ export class ExprsEmitter {
1456
1513
  emitImagLit(lit, targetWasmType) {
1457
1514
  const is64 =
1458
1515
  lit._type?.name === "complex64" ||
1459
- targetWasmType?.typeName === "$complex64";
1516
+ this._isComplex64Target(targetWasmType);
1460
1517
  const complexName = is64 ? "complex64" : "complex128";
1461
1518
  const info = this.mod.getComplexType(complexName);
1462
1519
  const imVal = Number(lit.value);
@@ -1681,7 +1738,7 @@ export class ExprsEmitter {
1681
1738
  const is64 =
1682
1739
  isComplex64(expr._type) ||
1683
1740
  isComplex64(operand._type) ||
1684
- targetWasmType?.typeName === "$complex64";
1741
+ this._isComplex64Target(targetWasmType);
1685
1742
  const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
1686
1743
  const cWType = {
1687
1744
  kind: "ref",
@@ -1797,6 +1854,7 @@ export class ExprsEmitter {
1797
1854
  if (!isScalarType(baseGoType, this.mod.checker, this.mod)) {
1798
1855
  const baseWType = this.toWasmType(baseGoType);
1799
1856
  this.emitExpr(operand, baseWType);
1857
+ this._emitNilCheck(operandGoType);
1800
1858
  return;
1801
1859
  }
1802
1860
  const box = this.mod.getBoxType(baseGoType);
@@ -2046,7 +2104,7 @@ export class ExprsEmitter {
2046
2104
  isComplex64(expr._type) ||
2047
2105
  isComplex64(left._type) ||
2048
2106
  isComplex64(right._type) ||
2049
- targetWasmType?.typeName === "$complex64";
2107
+ this._isComplex64Target(targetWasmType);
2050
2108
  const info = this.mod.getComplexType(is64 ? "complex64" : "complex128");
2051
2109
  const f = info.elemWType;
2052
2110
  const cWType = {
@@ -2599,6 +2657,8 @@ export class ExprsEmitter {
2599
2657
  emitNarrowIntWrap(goType) {
2600
2658
  if (!goType) return;
2601
2659
  if (goType.kind === "TypeName" || goType.kind === "Ident") {
2660
+ const declared = this.mod.checker?.types?.get(goType.name);
2661
+ if (declared?.kind === "named") return this.emitNarrowIntWrap(declared);
2602
2662
  return this.emitNarrowIntWrap({ kind: "basic", name: goType.name });
2603
2663
  }
2604
2664
  if (goType.kind === "named" && goType.underlying) {
@@ -2626,7 +2686,14 @@ export class ExprsEmitter {
2626
2686
  }
2627
2687
 
2628
2688
  emitDivRem(op, left, right, targetWasmType = null) {
2629
- const goType = left._type;
2689
+ // An untyped constant left operand takes the type of the typed right
2690
+ // one (`(1<<31) % uint32(n)` divides unsigned).
2691
+ const goType =
2692
+ left._type?.kind === "untyped" &&
2693
+ right._type &&
2694
+ right._type.kind !== "untyped"
2695
+ ? right._type
2696
+ : left._type;
2630
2697
  const wType = targetWasmType ?? toWasmType(goType, this.mod.checker);
2631
2698
 
2632
2699
  if (wType === "f32" || wType === "f64") {