gofront 1.7.0 → 1.8.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 (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +10 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +512 -77
  34. package/src/backend/wasm/encode.js +43 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -3,10 +3,16 @@
3
3
  // call expression dispatch (static, method, closure, package).
4
4
 
5
5
  import {
6
+ anonIfaceName,
6
7
  getMapKeyValTypes,
7
8
  isArrayType,
9
+ isBoolGoType,
10
+ isComplex,
11
+ isComplex64,
8
12
  isMapType,
9
13
  isNonEmptyInterface,
14
+ isScalarType,
15
+ isSigned,
10
16
  isSliceType,
11
17
  isStringType,
12
18
  isStructType,
@@ -228,7 +234,12 @@ export class BuiltinsEmitter {
228
234
  this.emitPanic("runtime error: makeslice: len out of range");
229
235
  this.pushInstruction("end");
230
236
 
231
- if (isStructType(elemGoType, this.mod.checker, this.mod)) {
237
+ if (
238
+ isStructType(elemGoType, this.mod.checker, this.mod) ||
239
+ isComplex(elemGoType) ||
240
+ (elemGoType?.kind !== "pointer" &&
241
+ isArrayType(elemGoType, this.mod.checker))
242
+ ) {
232
243
  const arrTmp = this.acquireTemp({
233
244
  kind: "ref",
234
245
  nullable: true,
@@ -302,6 +313,49 @@ export class BuiltinsEmitter {
302
313
  }
303
314
  }
304
315
 
316
+ _emitBuiltinNew(call) {
317
+ const targetType =
318
+ call._type?.base ??
319
+ (call.args[0]?.kind === "TypeExpr"
320
+ ? call.args[0].type
321
+ : call.args[0]?._type) ??
322
+ call.args[0];
323
+ if (isStructType(targetType, this.mod.checker, this.mod)) {
324
+ let structInfo = this._resolveStructInfo({ _type: targetType });
325
+ if (!structInfo) {
326
+ const typeName =
327
+ targetType.kind === "named" ? targetType.name : targetType.name;
328
+ if (typeName) structInfo = this.mod.getStructType(typeName);
329
+ }
330
+ if (!structInfo) {
331
+ throw new Error(
332
+ `Unknown struct in new: ${targetType.name ?? targetType}`,
333
+ );
334
+ }
335
+ for (const f of structInfo.fields) {
336
+ this.emitZeroValue(f.goType, f.wType);
337
+ }
338
+ this.pushInstruction({
339
+ op: "struct.new",
340
+ typeIndex: structInfo.typeIndex,
341
+ });
342
+ return;
343
+ }
344
+ if (isArrayType(targetType, this.mod.checker)) {
345
+ const wType = this.toWasmType(targetType);
346
+ this.emitZeroValue(targetType, wType);
347
+ return;
348
+ }
349
+ const box = this.mod.getBoxType(targetType);
350
+ const valWType = this.toWasmType(targetType);
351
+ this.emitZeroValue(targetType, valWType);
352
+ this.pushInstruction({
353
+ op: "struct.new",
354
+ typeIndex: box.typeIndex,
355
+ typeName: box.name,
356
+ });
357
+ }
358
+
305
359
  // With `newLen` already computed: when it exceeds the old capacity, allocate
306
360
  // a doubled (min 2, at least newLen) backing array and copy the old elements
307
361
  // into it at offset 0; otherwise reuse the old array, offset and capacity.
@@ -374,6 +428,17 @@ export class BuiltinsEmitter {
374
428
  this.pushInstruction("end");
375
429
  }
376
430
 
431
+ // The source operand of `append(b, x...)` and `copy(b, x)`: a string
432
+ // (the only non-slice form Go allows) is converted to a fresh byte slice.
433
+ _emitSliceOperand(node, elemGoType, sliceWType) {
434
+ if (isStringType(node._type, this.mod.checker)) {
435
+ this.emitExpr(node, "externref");
436
+ this._emitStringToByteSlice({ kind: "slice", elem: elemGoType });
437
+ return;
438
+ }
439
+ this.emitExpr(node, sliceWType);
440
+ }
441
+
377
442
  _emitBuiltinAppend(call) {
378
443
  const { args } = call;
379
444
  const sNode = args[0];
@@ -428,7 +493,7 @@ export class BuiltinsEmitter {
428
493
 
429
494
  if (args.length === 2 && args[1]._spread) {
430
495
  const s2Tmp = this.acquireTemp(sliceWType);
431
- this.emitExpr(args[1], sliceWType);
496
+ this._emitSliceOperand(args[1], elemGoType, sliceWType);
432
497
  this.pushInstruction({ op: "local.set", index: s2Tmp });
433
498
 
434
499
  const s2ArrTmp = this.acquireTemp(arrWType);
@@ -487,7 +552,7 @@ export class BuiltinsEmitter {
487
552
  this.pushInstruction("i32.add");
488
553
  }
489
554
  this.emitExpr(elemNode, arrInfo.elemWType);
490
- this._emitCopyIfValueStruct(elemNode, elemGoType, arrInfo.elemWType);
555
+ this._emitCopyIfValue(elemNode, elemGoType, arrInfo.elemWType);
491
556
  this.pushInstruction({
492
557
  op: "array.set",
493
558
  typeIndex: arrInfo.typeIndex,
@@ -538,7 +603,7 @@ export class BuiltinsEmitter {
538
603
  this.pushInstruction({ op: "local.set", index: dstTmp });
539
604
 
540
605
  const srcTmp = this.acquireTemp(sliceWType);
541
- this.emitExpr(srcNode, sliceWType);
606
+ this._emitSliceOperand(srcNode, elemGoType, sliceWType);
542
607
  this.pushInstruction({ op: "local.set", index: srcTmp });
543
608
 
544
609
  const nTmp = this.acquireTemp("i32");
@@ -667,7 +732,7 @@ export class BuiltinsEmitter {
667
732
  const arg = args[i];
668
733
  const isLast = i === args.length - 1 && isPrintln;
669
734
  const isBool =
670
- arg._type?.name === "bool" ||
735
+ isBoolGoType(arg._type) ||
671
736
  (arg.kind === "BasicLit" && arg.litKind === "BOOL");
672
737
  const wType = toWasmType(arg._type, this.mod.checker);
673
738
  this.emitExpr(arg, wType);
@@ -795,6 +860,136 @@ export class BuiltinsEmitter {
795
860
  }
796
861
  }
797
862
 
863
+ _emitBuiltinComplex(call, targetWasmType) {
864
+ const arg0 = call.args[0];
865
+ const arg1 = call.args[1];
866
+ const callType = call._type;
867
+ const is64 =
868
+ callType?.name === "complex64" ||
869
+ arg0._type?.name === "float32" ||
870
+ arg1._type?.name === "float32" ||
871
+ targetWasmType?.typeName === "$complex64";
872
+ const complexName = is64 ? "complex64" : "complex128";
873
+ const info = this.mod.getComplexType(complexName);
874
+ this.emitExpr(arg0, info.elemWType);
875
+ this.emitExpr(arg1, info.elemWType);
876
+ this.pushInstruction({
877
+ op: "struct.new",
878
+ typeIndex: info.typeIndex,
879
+ typeName: info.name,
880
+ });
881
+ }
882
+
883
+ _emitBuiltinReal(call, targetWasmType) {
884
+ const arg = call.args[0];
885
+ const argType = arg._type ?? this._resolveExprGoType(arg);
886
+ const is64 = isComplex64(argType);
887
+ const complexName = is64 ? "complex64" : "complex128";
888
+ const info = this.mod.getComplexType(complexName);
889
+ const complexWType = {
890
+ kind: "ref",
891
+ nullable: true,
892
+ typeIndex: info.typeIndex,
893
+ typeName: info.name,
894
+ };
895
+ this.emitExpr(arg, complexWType);
896
+ this.pushInstruction({
897
+ op: "struct.get",
898
+ typeIndex: info.typeIndex,
899
+ typeName: info.name,
900
+ fieldIndex: 0,
901
+ });
902
+ if (targetWasmType && targetWasmType !== info.elemWType) {
903
+ if (info.elemWType === "f32" && targetWasmType === "f64") {
904
+ this.pushInstruction("f64.promote_f32");
905
+ } else if (info.elemWType === "f64" && targetWasmType === "f32") {
906
+ this.pushInstruction("f32.demote_f64");
907
+ }
908
+ }
909
+ }
910
+
911
+ _emitBuiltinImag(call, targetWasmType) {
912
+ const arg = call.args[0];
913
+ const argType = arg._type ?? this._resolveExprGoType(arg);
914
+ const is64 = isComplex64(argType);
915
+ const complexName = is64 ? "complex64" : "complex128";
916
+ const info = this.mod.getComplexType(complexName);
917
+ const complexWType = {
918
+ kind: "ref",
919
+ nullable: true,
920
+ typeIndex: info.typeIndex,
921
+ typeName: info.name,
922
+ };
923
+ this.emitExpr(arg, complexWType);
924
+ this.pushInstruction({
925
+ op: "struct.get",
926
+ typeIndex: info.typeIndex,
927
+ typeName: info.name,
928
+ fieldIndex: 1,
929
+ });
930
+ if (targetWasmType && targetWasmType !== info.elemWType) {
931
+ if (info.elemWType === "f32" && targetWasmType === "f64") {
932
+ this.pushInstruction("f64.promote_f32");
933
+ } else if (info.elemWType === "f64" && targetWasmType === "f32") {
934
+ this.pushInstruction("f32.demote_f64");
935
+ }
936
+ }
937
+ }
938
+
939
+ // min/max fold left over the operands: floats use f32/f64.min/max (NaN
940
+ // propagates, -0 < +0), integers `select` on lt/gt, strings compare via the
941
+ // string import. A single operand is the result itself.
942
+ _emitBuiltinMinMax(call, targetWasmType) {
943
+ const isMin = call.func.name === "min";
944
+ const goType = call._type ?? this._resolveExprGoType(call.args[0]);
945
+ const wType =
946
+ goType?.kind === "untyped" && typeof targetWasmType === "string"
947
+ ? targetWasmType
948
+ : toWasmType(goType, this.mod.checker);
949
+ const [first, ...rest] = call.args;
950
+ this.emitExpr(first, wType);
951
+ if (rest.length === 0) return;
952
+ const acc = this.acquireTemp(wType);
953
+ const next = this.acquireTemp(wType);
954
+ const isStr = isStringType(goType, this.mod.checker);
955
+ const isFloatType = wType === "f32" || wType === "f64";
956
+ const cmp = isMin ? "lt" : "gt";
957
+ for (const arg of rest) {
958
+ this.pushInstruction({ op: "local.set", index: acc });
959
+ this.emitExpr(arg, wType);
960
+ this.pushInstruction({ op: "local.set", index: next });
961
+ if (isFloatType) {
962
+ this.pushInstruction({ op: "local.get", index: acc });
963
+ this.pushInstruction({ op: "local.get", index: next });
964
+ this.pushInstruction(`${wType}.${isMin ? "min" : "max"}`);
965
+ continue;
966
+ }
967
+ if (isStr) {
968
+ // next < acc (min) / next > acc (max) ? next : acc
969
+ this.pushInstruction({ op: "local.get", index: next });
970
+ this.pushInstruction({ op: "local.get", index: acc });
971
+ this.pushInstruction({
972
+ op: "call",
973
+ funcIndex: this.mod.getStringCmpImportIndex(isMin ? "<" : ">"),
974
+ });
975
+ this.pushInstruction({ op: "if", blockType: wType });
976
+ this.pushInstruction({ op: "local.get", index: next });
977
+ this.pushInstruction("else");
978
+ this.pushInstruction({ op: "local.get", index: acc });
979
+ this.pushInstruction("end");
980
+ continue;
981
+ }
982
+ this.pushInstruction({ op: "local.get", index: next });
983
+ this.pushInstruction({ op: "local.get", index: acc });
984
+ this.pushInstruction({ op: "local.get", index: next });
985
+ this.pushInstruction({ op: "local.get", index: acc });
986
+ this.pushInstruction(`${wType}.${cmp}_${isSigned(goType) ? "s" : "u"}`);
987
+ this.pushInstruction("select");
988
+ }
989
+ this.releaseTemp(next, wType);
990
+ this.releaseTemp(acc, wType);
991
+ }
992
+
798
993
  _emitBuiltinCall(call, targetWasmType) {
799
994
  const { func } = call;
800
995
  if (func.kind !== "Ident") return false;
@@ -808,6 +1003,9 @@ export class BuiltinsEmitter {
808
1003
  case "cap":
809
1004
  this._emitBuiltinCap(call, targetWasmType);
810
1005
  return true;
1006
+ case "new":
1007
+ this._emitBuiltinNew(call);
1008
+ return true;
811
1009
  case "make":
812
1010
  this._emitBuiltinMake(call);
813
1011
  return true;
@@ -833,6 +1031,19 @@ export class BuiltinsEmitter {
833
1031
  case "recover":
834
1032
  this._emitBuiltinRecover(call, targetWasmType);
835
1033
  return true;
1034
+ case "complex":
1035
+ this._emitBuiltinComplex(call, targetWasmType);
1036
+ return true;
1037
+ case "real":
1038
+ this._emitBuiltinReal(call, targetWasmType);
1039
+ return true;
1040
+ case "imag":
1041
+ this._emitBuiltinImag(call, targetWasmType);
1042
+ return true;
1043
+ case "min":
1044
+ case "max":
1045
+ this._emitBuiltinMinMax(call, targetWasmType);
1046
+ return true;
836
1047
  default:
837
1048
  return false;
838
1049
  }
@@ -845,6 +1056,12 @@ export class BuiltinsEmitter {
845
1056
  this.emitTestingCall(func, args);
846
1057
  return true;
847
1058
  }
1059
+ if (
1060
+ func.expr?.name &&
1061
+ (this.resolveLocal(func.expr.name) ||
1062
+ this.mod.resolveGlobal(func.expr.name))
1063
+ )
1064
+ return false;
848
1065
  const pkgName = func.expr?.name;
849
1066
  switch (pkgName) {
850
1067
  case "math":
@@ -903,6 +1120,14 @@ export class BuiltinsEmitter {
903
1120
  this.emitBytesNewReader(call, targetWasmType);
904
1121
  return true;
905
1122
  }
1123
+ if (func.field === "NewBuffer") {
1124
+ this.emitBytesNewBuffer(call, targetWasmType);
1125
+ return true;
1126
+ }
1127
+ if (func.field === "NewBufferString") {
1128
+ this.emitBytesNewBufferString(call, targetWasmType);
1129
+ return true;
1130
+ }
906
1131
  this.emitBytesCall(func.field, args, targetWasmType);
907
1132
  return true;
908
1133
  case "path":
@@ -961,6 +1186,9 @@ export class BuiltinsEmitter {
961
1186
  func.expr,
962
1187
  args,
963
1188
  targetWasmType,
1189
+ recvTypeName === "simd.Mask32x4" ||
1190
+ recvType?.underlying?.simd === "Mask32x4" ||
1191
+ recvType?.simd === "Mask32x4",
964
1192
  );
965
1193
  }
966
1194
  if (
@@ -983,7 +1211,7 @@ export class BuiltinsEmitter {
983
1211
  return true;
984
1212
  }
985
1213
  if (isNonEmptyInterface(recvType, this.mod.checker)) {
986
- const ifaceName = recvTypeName ?? "anon";
1214
+ const ifaceName = recvTypeName ?? anonIfaceName(recvType);
987
1215
  const dispatchName = `__dispatch_${ifaceName}_${func.field}`;
988
1216
  const targetFuncIdx = this.mod.resolveFuncIndex(dispatchName);
989
1217
  if (targetFuncIdx !== null) {
@@ -994,7 +1222,7 @@ export class BuiltinsEmitter {
994
1222
  const pType = targetParamTypes[i + 1] ?? null;
995
1223
  const pGoType = paramGoTypes[i + 1] ?? null;
996
1224
  this.emitExpr(args[i], pType);
997
- this._emitCopyIfValueStruct(args[i], pGoType, pType);
1225
+ this._emitCopyIfValue(args[i], pGoType, pType);
998
1226
  }
999
1227
  this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
1000
1228
  return true;
@@ -1016,14 +1244,20 @@ export class BuiltinsEmitter {
1016
1244
  if (targetFuncIdx !== null) {
1017
1245
  const recvWType = targetParamTypes[0] ?? null;
1018
1246
  const recvGoType = this.mod.getFuncParamGoTypes(methodName)[0] ?? null;
1019
- this.emitExpr(func.expr, recvWType);
1020
- this._emitCopyIfValueStruct(func.expr, recvGoType, recvWType);
1247
+ this.emitExpr(this._recvOperand(func, recvGoType), recvWType);
1248
+ // `p.M()` with a value receiver reads `*p`: a nil `p` panics here.
1249
+ if (
1250
+ recvGoType?.kind !== "pointer" &&
1251
+ isStructType(recvGoType, this.mod.checker, this.mod)
1252
+ )
1253
+ this._emitNilCheck(func.expr._type);
1254
+ this._emitCopyIfValue(func.expr, recvGoType, recvWType);
1021
1255
  const paramGoTypes = this.mod.getFuncParamGoTypes(methodName);
1022
1256
  for (let i = 0; i < args.length; i++) {
1023
1257
  const pType = targetParamTypes[i + 1] ?? null;
1024
1258
  const pGoType = paramGoTypes[i + 1] ?? null;
1025
1259
  this.emitExpr(args[i], pType);
1026
- this._emitCopyIfValueStruct(args[i], pGoType, pType);
1260
+ this._emitCopyIfValue(args[i], pGoType, pType);
1027
1261
  }
1028
1262
  this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
1029
1263
  return true;
@@ -1032,6 +1266,43 @@ export class BuiltinsEmitter {
1032
1266
  return false;
1033
1267
  }
1034
1268
 
1269
+ // The receiver operand of a method call: `p.Get()` with a value receiver
1270
+ // and `p *N` (a named scalar or slice) passes `*p`.
1271
+ _recvOperand(func, recvGoType) {
1272
+ const t = func.expr._type;
1273
+ // A pointer receiver on `*p` gets `p` itself, so the method mutates *p.
1274
+ if (recvGoType?.kind === "pointer") {
1275
+ const pointer = this._structDerefPointer(func.expr);
1276
+ if (pointer) return pointer;
1277
+ }
1278
+ if (
1279
+ t?.kind === "pointer" &&
1280
+ recvGoType?.kind !== "pointer" &&
1281
+ (isScalarType(t.base, this.mod.checker, this.mod) ||
1282
+ isSliceType(t.base, this.mod.checker))
1283
+ )
1284
+ return { kind: "UnaryExpr", op: "*", operand: func.expr, _type: t.base };
1285
+ return this._ptrRecvOperand(func);
1286
+ }
1287
+
1288
+ // `x.M()` with a pointer receiver on a named scalar value passes `&x`
1289
+ // (the typechecker boxed `x`).
1290
+ _ptrRecvOperand(func) {
1291
+ const t = func.expr._type;
1292
+ if (!func._type?._ptrRecv || t?.kind !== "named") return func.expr;
1293
+ if (
1294
+ !isScalarType(t, this.mod.checker, this.mod) &&
1295
+ !isSliceType(t, this.mod.checker)
1296
+ )
1297
+ return func.expr;
1298
+ return {
1299
+ kind: "UnaryExpr",
1300
+ op: "&",
1301
+ operand: func.expr,
1302
+ _type: { kind: "pointer", base: t },
1303
+ };
1304
+ }
1305
+
1035
1306
  _emitStdlibReceiverMethod(
1036
1307
  recvTypeName,
1037
1308
  field,
@@ -1067,11 +1338,28 @@ export class BuiltinsEmitter {
1067
1338
  this.emitDurationMethodCall(field, expr, args, targetWasmType);
1068
1339
  return true;
1069
1340
  }
1341
+ if (
1342
+ recvTypeName === "time.Month" ||
1343
+ recvTypeName === "time.Weekday" ||
1344
+ recvTypeName === "time.Location"
1345
+ ) {
1346
+ this.emitTimeAuxMethodCall(
1347
+ recvTypeName,
1348
+ field,
1349
+ expr,
1350
+ args,
1351
+ targetWasmType,
1352
+ );
1353
+ return true;
1354
+ }
1070
1355
  if (recvTypeName === "io.LimitedReader") {
1071
1356
  this.emitIoLimitedReaderMethodCall(field, expr, args, targetWasmType);
1072
1357
  return true;
1073
1358
  }
1074
- if (recvTypeName === "sync.Mutex" || recvTypeName === "sync.RWMutex") {
1359
+ if (
1360
+ (recvTypeName === "sync.Mutex" || recvTypeName === "sync.RWMutex") &&
1361
+ field !== "RLocker"
1362
+ ) {
1075
1363
  this.emitSyncMutexMethodCall(field, expr, args, targetWasmType);
1076
1364
  return true;
1077
1365
  }
@@ -1154,7 +1442,7 @@ export class BuiltinsEmitter {
1154
1442
  const pType = paramTypes[i] ?? null;
1155
1443
  const pGoType = paramGoTypes[i] ?? null;
1156
1444
  this.emitExpr(arg, pType);
1157
- this._emitCopyIfValueStruct(arg, pGoType, pType);
1445
+ this._emitCopyIfValue(arg, pGoType, pType);
1158
1446
  }
1159
1447
  this.pushInstruction({ op: "call", funcIndex: targetFuncIdx });
1160
1448
  return true;
@@ -1175,6 +1463,8 @@ export class BuiltinsEmitter {
1175
1463
 
1176
1464
  const closureTmp = this.acquireTemp(closureWType);
1177
1465
  this.emitExpr(func, closureWType);
1466
+ // Calling a nil func value panics like a nil pointer dereference.
1467
+ this._emitNilBranch();
1178
1468
  this.pushInstruction({ op: "local.set", index: closureTmp });
1179
1469
 
1180
1470
  // Push param 0: env (anyref)
@@ -1191,7 +1481,7 @@ export class BuiltinsEmitter {
1191
1481
  const pWType = closureInfo.paramWTypes[i] ?? null;
1192
1482
  const pGoType = closureInfo.sig.params[i] ?? null;
1193
1483
  this.emitExpr(arg, pWType);
1194
- this._emitCopyIfValueStruct(arg, pGoType, pWType);
1484
+ this._emitCopyIfValue(arg, pGoType, pWType);
1195
1485
  }
1196
1486
 
1197
1487
  // Push funcref