gofront 1.6.1 → 1.7.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.
@@ -7,6 +7,7 @@ import {
7
7
  } from "../../lower/index.js";
8
8
  import {
9
9
  ERROR,
10
+ isAny,
10
11
  isComplex,
11
12
  isTypedArraySlice,
12
13
  typedArrayConstructorForElem,
@@ -55,6 +56,8 @@ const NAMED_ZERO_TABLE = {
55
56
  "encoding/binary.BigEndian": ["binary", "__binary_be"],
56
57
  "base64.Encoding": ["base64", "__base64_std"],
57
58
  "encoding/base64.Encoding": ["base64", "__base64_std"],
59
+ "simd.F32x4": [null, "Object.freeze([0, 0, 0, 0])"],
60
+ F32x4: [null, "Object.freeze([0, 0, 0, 0])"],
58
61
  };
59
62
 
60
63
  // Splits a generated JS lvalue at its outermost trailing access:
@@ -349,7 +352,8 @@ const EXPR_GEN_DELEGATE = {
349
352
  /** @type {ThisType<CodeGen>} */
350
353
  export const expressionGenMethods = {
351
354
  genExpr(expr) {
352
- if (expr._ifaceBox && !expr._ifaceBoxing) {
355
+ if (this._isNamedIfaceBox(expr)) return this._genNamedIfaceBoxExpr(expr);
356
+ if (expr._ifaceBox && !expr._ifaceBoxing && !this._isNamedBoxKind(expr)) {
353
357
  expr._ifaceBoxing = true;
354
358
  try {
355
359
  this.useHelper("ifaceBox");
@@ -396,6 +400,29 @@ export const expressionGenMethods = {
396
400
  }
397
401
  },
398
402
 
403
+ _isNamedBoxKind(expr) {
404
+ return expr._ifaceBox === "named" || expr._ifaceBox === "named_ptr";
405
+ },
406
+
407
+ _isNamedIfaceBox(expr) {
408
+ return (
409
+ this._isNamedBoxKind(expr) && !expr._ifaceBoxing && !expr._noNamedBox
410
+ );
411
+ },
412
+
413
+ _genNamedIfaceBoxExpr(expr) {
414
+ expr._ifaceBoxing = true;
415
+ try {
416
+ return this._genNamedIfaceBox(
417
+ this.genExpr(expr),
418
+ expr._ifaceNamedType,
419
+ expr._ifaceBox === "named_ptr",
420
+ );
421
+ } finally {
422
+ expr._ifaceBoxing = false;
423
+ }
424
+ },
425
+
399
426
  _isStructType(t) {
400
427
  if (!t) return false;
401
428
  if (t.name && this.structNames.has(t.name)) return true;
@@ -542,8 +569,13 @@ export const expressionGenMethods = {
542
569
  _emitParamCopies(params) {
543
570
  for (const p of params) {
544
571
  if (p.variadic || !p.name || p.name === "_") continue;
572
+ if (this._boxedVars.has(p.name)) {
573
+ // Address-taken parameter: rebind it to a { value } box.
574
+ this.line(`${p.name} = { value: ${p.name} };`);
575
+ continue;
576
+ }
545
577
  const t = this._typeFromNode(p.type);
546
- if (!t || this._boxedVars.has(p.name)) continue;
578
+ if (!t) continue;
547
579
  if (this._fnMutates(p.name)) {
548
580
  this.line(`${p.name} = ${this._cloneJs(t, p.name)};`);
549
581
  this._markOwnership(p.name, true);
@@ -621,8 +653,12 @@ export const expressionGenMethods = {
621
653
 
622
654
  _genSelectorExpr(expr) {
623
655
  if (expr._isMethodExpr) {
656
+ const namedExpr = this._namedMethodExprInfo(expr);
657
+ if (namedExpr) return `${namedExpr.cls}.$${expr.field}`;
624
658
  return `((recv, ...args) => recv.${expr.field}(...args))`;
625
659
  }
660
+ const namedValue = this._genNamedMethodValue(expr);
661
+ if (namedValue !== undefined) return namedValue;
626
662
  if (expr.expr.kind === "Ident") {
627
663
  const nsConst = NS_CONSTANTS[expr.expr.name]?.[expr.field];
628
664
  if (nsConst !== undefined) {
@@ -672,6 +708,81 @@ export const expressionGenMethods = {
672
708
  return f;
673
709
  },
674
710
 
711
+ // ── Named non-struct types with methods: raw values, static dispatch ──
712
+
713
+ // Boxes a raw named value (or *T) entering an interface so dynamic dispatch works.
714
+ _genNamedIfaceBox(js, type, isPtr) {
715
+ const info = this._namedBoxInfo(type);
716
+ if (!info) return js;
717
+ return isPtr ? `new ${info.cls}$Ptr(${js})` : `new ${info.cls}(${js})`;
718
+ },
719
+
720
+ // Static target of `x.M` on a named non-struct type: its class and the
721
+ // receiver argument (the { value } box for pointer receivers).
722
+ _namedMethodTarget(sel) {
723
+ const recvType = sel.expr?._type;
724
+ const viaPtr = recvType?.kind === "pointer";
725
+ const info = this._namedBoxInfo(viaPtr ? recvType.base : recvType);
726
+ const method = info?.methods.get(sel.field);
727
+ if (!method) return null;
728
+ let recv;
729
+ if (method._ptrRecv) {
730
+ recv = viaPtr ? this.genExpr(sel.expr) : this._genAddressOfExpr(sel.expr);
731
+ } else {
732
+ recv = viaPtr
733
+ ? `${this.genExpr(sel.expr)}.value`
734
+ : this.genExpr(sel.expr);
735
+ }
736
+ return { cls: info.cls, recv };
737
+ },
738
+
739
+ // `x.M` used as a value: binds the receiver now, as Go does.
740
+ _genNamedMethodValue(expr) {
741
+ if (!expr._isMethodValue || expr._callee) return undefined;
742
+ const target = this._namedMethodTarget(expr);
743
+ if (!target) return undefined;
744
+ return `((__r) => (...__a) => ${target.cls}.$${expr.field}(__r, ...__a))(${target.recv})`;
745
+ },
746
+
747
+ _genAddressOfExpr(operand) {
748
+ return this._genUnaryExpr({
749
+ kind: "UnaryExpr",
750
+ op: "&",
751
+ operand,
752
+ _type: { kind: "pointer", base: operand._type },
753
+ });
754
+ },
755
+
756
+ // `T.M` method expression on a named non-struct type.
757
+ _namedMethodExprInfo(sel) {
758
+ const t =
759
+ sel.expr?._type?.kind === "named"
760
+ ? sel.expr._type
761
+ : this.checker?.types.get(sel.expr?.name);
762
+ const info = this._namedBoxInfo(t);
763
+ return info?.methods.has(sel.field) ? info : null;
764
+ },
765
+
766
+ _genNamedMethodCall(expr) {
767
+ const sel = expr.func;
768
+ if (sel._isMethodExpr) {
769
+ const info = this._namedMethodExprInfo(sel);
770
+ if (!info) return undefined;
771
+ return `${info.cls}.$${sel.field}(${this._genCallArgs(expr)})`;
772
+ }
773
+ const target = this._namedMethodTarget(sel);
774
+ if (!target) return undefined;
775
+ const args = this._genCallArgs(expr);
776
+ return `${target.cls}.$${sel.field}(${args ? `${target.recv}, ${args}` : target.recv})`;
777
+ },
778
+
779
+ _genCallArgs(expr) {
780
+ if (expr._multiForward) return `...${this.genExpr(expr.args[0])}`;
781
+ return expr.args
782
+ .map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
783
+ .join(", ");
784
+ },
785
+
675
786
  _genPointerSelectorField(expr, base) {
676
787
  if (this._isStructPointerType(expr.expr._type)) {
677
788
  const sel = `${base}.${expr.field}`;
@@ -706,9 +817,10 @@ export const expressionGenMethods = {
706
817
  },
707
818
 
708
819
  _isStringExprType(exprType) {
820
+ const t = exprType?.kind === "named" ? exprType.underlying : exprType;
709
821
  return (
710
- (exprType?.kind === "basic" && exprType.name === "string") ||
711
- (exprType?.kind === "untyped" && exprType.base === "string")
822
+ (t?.kind === "basic" && t.name === "string") ||
823
+ (t?.kind === "untyped" && t.base === "string")
712
824
  );
713
825
  },
714
826
 
@@ -751,22 +863,104 @@ export const expressionGenMethods = {
751
863
  const val = hoist ? "__v" : src;
752
864
  const check = this._typeCheckExpr(expr.type, val);
753
865
  const wrap = (body) => (hoist ? `((__v) => ${body})(${src})` : body);
866
+ // Named non-struct values sit in interfaces as boxes; the asserted value is raw.
867
+ const out = this._isNamedBoxTypeNode(expr.type)
868
+ ? `${val}.__gfUnbox()`
869
+ : val;
754
870
  if (!expr._commaOk) {
755
871
  // plain assertion: panic if check fails (matches Go behavior)
756
872
  if (check === "true") return src; // can't check at runtime — pass through
757
873
  return wrap(
758
- `(${check} ? ${val} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
874
+ `(${check} ? ${out} : (() => { throw new Error("interface conversion: type assertion failed"); })())`,
759
875
  );
760
876
  }
761
877
  // comma-ok: emit [value-or-zero, runtimeTypeCheck]
762
878
  const zero = this.zeroValueForTypeNode(expr.type);
763
- return wrap(`(${check} ? [${val}, true] : [${zero}, false])`);
879
+ return wrap(`(${check} ? [${out}, true] : [${zero}, false])`);
880
+ },
881
+
882
+ _typeNameCheckExpr(name, val) {
883
+ if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
884
+ if (name === "string") return `typeof ${val} === "string"`;
885
+ if (name === "bool") return `typeof ${val} === "boolean"`;
886
+ if (name === "nil") return `${val} === null`;
887
+ if (name === "error")
888
+ return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
889
+ if (this.structNames.has(name))
890
+ return `(${val} instanceof ${name} && ${val}.__p !== true)`;
891
+ const named = this.checker?.types.get(name);
892
+ const namedInfo = this._namedBoxInfo(named);
893
+ if (namedInfo) return `(${val} instanceof ${namedInfo.cls})`;
894
+ // User-declared interfaces: check the dynamic value has every method.
895
+ if (!name.includes(".") && named?.underlying?.kind === "interface")
896
+ return this._ifaceMethodCheck(val, [...named.underlying.methods.keys()]);
897
+ return "true"; // unknown type — can't check at runtime
898
+ },
899
+
900
+ // Anonymous interfaces and pointers to named non-struct types; null otherwise.
901
+ _namedOrIfaceCheckExpr(typeNode, val) {
902
+ if (typeNode.kind === "InterfaceType" && !(typeNode.embeds?.length > 0))
903
+ return this._ifaceMethodCheck(
904
+ val,
905
+ (typeNode.methods ?? []).map((m) => m.name),
906
+ );
907
+ if (typeNode.kind === "PointerType" && typeNode.base?.kind === "TypeName") {
908
+ const info = this._namedBoxInfo(
909
+ this.checker?.types.get(typeNode.base.name),
910
+ );
911
+ if (info) return `(${val} instanceof ${info.cls}$Ptr)`;
912
+ }
913
+ return null;
914
+ },
915
+
916
+ _ifaceMethodCheck(val, methodNames) {
917
+ const checks = methodNames.map((m) => `typeof ${val}.${m} === "function"`);
918
+ return `(${[`${val} != null`, ...checks].join(" && ")})`;
919
+ },
920
+
921
+ _isNamedBoxTypeNode(typeNode) {
922
+ if (typeNode?.kind === "TypeName")
923
+ return Boolean(
924
+ this._namedBoxInfo(this.checker?.types.get(typeNode.name)),
925
+ );
926
+ if (typeNode?.kind === "PointerType" && typeNode.base?.kind === "TypeName")
927
+ return Boolean(
928
+ this._namedBoxInfo(this.checker?.types.get(typeNode.base.name)),
929
+ );
930
+ return false;
764
931
  },
765
932
 
766
933
  _genTypeConversionCall(expr) {
767
- const name = expr._conversionTargetType?.name;
934
+ const targetType = expr._conversionTargetType;
768
935
  const arg = this.genExpr(expr.args[0]);
769
- if (name && this.namedWrapperNames.has(name)) return `new ${name}(${arg})`;
936
+ const isRaw =
937
+ targetType?.name === "json.RawMessage" ||
938
+ targetType?.name === "encoding/json.RawMessage";
939
+ const base =
940
+ targetType?.kind === "named" ? targetType.underlying : targetType;
941
+ if (
942
+ targetType?.kind === "named" &&
943
+ base?.kind === "basic" &&
944
+ this._namedBoxInfo(targetType)
945
+ )
946
+ return this._genPrimitiveConversion(
947
+ { expr: expr.args[0] },
948
+ arg,
949
+ base.name,
950
+ );
951
+ if (base?.kind === "slice") {
952
+ const elem = base.elem?.name;
953
+ if (
954
+ (elem === "byte" || elem === "uint8") &&
955
+ this._isStringExprType(expr.args[0]?._type)
956
+ ) {
957
+ const enc = `new TextEncoder().encode(${arg})`;
958
+ return isRaw ? `((u) => { u.__raw = true; return u; })(${enc})` : enc;
959
+ }
960
+ if (isRaw) {
961
+ return `((u) => { u.__raw = true; return u; })(${arg})`;
962
+ }
963
+ }
770
964
  return arg;
771
965
  },
772
966
 
@@ -834,6 +1028,9 @@ export const expressionGenMethods = {
834
1028
  const args = expr.args.map((a) => this.genExpr(a)).join(", ");
835
1029
  return `${this.genExpr(expr.func.expr)}.subarray(${args})`;
836
1030
  }
1031
+ if (recvName === "simd.F32x4" || recvName === "F32x4") {
1032
+ return this._genSimdReceiverCall(expr);
1033
+ }
837
1034
  if (recvName === "sync.Mutex" || recvName === "sync.RWMutex") {
838
1035
  if (expr.func.field === "TryLock" || expr.func.field === "TryRLock")
839
1036
  return "true";
@@ -879,6 +1076,35 @@ export const expressionGenMethods = {
879
1076
  return undefined;
880
1077
  },
881
1078
 
1079
+ _genSimdReceiverCall(expr) {
1080
+ this.useHelper("simd");
1081
+ const method = expr.func.field;
1082
+ const recv = this.genExpr(expr.func.expr);
1083
+ const helpers = {
1084
+ Add: "__simd_add",
1085
+ Sub: "__simd_sub",
1086
+ Mul: "__simd_mul",
1087
+ Div: "__simd_div",
1088
+ Min: "__simd_min",
1089
+ Max: "__simd_max",
1090
+ Dot3: "__simd_dot3",
1091
+ Dot4: "__simd_dot4",
1092
+ };
1093
+ if (helpers[method]) {
1094
+ return `${helpers[method]}(${recv}, ${this.genExpr(expr.args[0])})`;
1095
+ }
1096
+ if (method === "Lane") {
1097
+ return `${recv}[${this.genExpr(expr.args[0])}]`;
1098
+ }
1099
+ if (method === "WithLane") {
1100
+ return `__simd_with_lane(${recv}, ${this.genExpr(expr.args[0])}, ${this.genExpr(expr.args[1])})`;
1101
+ }
1102
+ if (method === "Splat") {
1103
+ return `__simd_splat(${this.genExpr(expr.args[0])})`;
1104
+ }
1105
+ return undefined;
1106
+ },
1107
+
882
1108
  _genSelectorCall(expr) {
883
1109
  if (this._isErrorMethodCall(expr)) {
884
1110
  const recv = this.genExpr(expr.func.expr);
@@ -896,6 +1122,8 @@ export const expressionGenMethods = {
896
1122
  const recvName = this._resolveRecvName(recvType);
897
1123
  const typed = this._genReceiverTypeCall(recvName, expr);
898
1124
  if (typed !== undefined) return typed;
1125
+ const namedCall = this._genNamedMethodCall(expr);
1126
+ if (namedCall !== undefined) return namedCall;
899
1127
  if (this.strict && this._isInterfaceType(recvType)) {
900
1128
  // Go panics with a nil-dereference runtime error; JS would throw a
901
1129
  // TypeError with a different message.
@@ -1098,7 +1326,9 @@ export const expressionGenMethods = {
1098
1326
  ? this.genValueExpr(match.value ?? match)
1099
1327
  : fields[idx].zero;
1100
1328
  } else {
1101
- values[idx] = `${this.genExpr(e.value)}.${ef}`;
1329
+ const valExpr = this.genExpr(e.value);
1330
+ values[idx] =
1331
+ `(${valExpr} != null ? ${valExpr}.${ef} : ${fields[idx].zero})`;
1102
1332
  }
1103
1333
  return idx;
1104
1334
  },
@@ -1116,6 +1346,11 @@ export const expressionGenMethods = {
1116
1346
  const idx = this._fillEmbedField(fields, ef, e, values);
1117
1347
  if (idx > maxIdx) maxIdx = idx;
1118
1348
  }
1349
+ const embedIdx = fields.findIndex((f) => f.name === k);
1350
+ if (embedIdx >= 0) {
1351
+ values[embedIdx] = this.genValueExpr(e.value);
1352
+ if (embedIdx > maxIdx) maxIdx = embedIdx;
1353
+ }
1119
1354
  } else {
1120
1355
  const idx = fields.findIndex((f) => f.name === k);
1121
1356
  if (idx >= 0) {
@@ -1139,11 +1374,11 @@ export const expressionGenMethods = {
1139
1374
  const maxExplicitIdx = hasPositional
1140
1375
  ? this._fillPositionalStructElems(fields, expr, values)
1141
1376
  : this._fillKeyedStructElems(fields, expr, values);
1142
- if (maxExplicitIdx < 0) {
1143
- return `new ${typeName}()`;
1144
- }
1145
- const args = values.slice(0, maxExplicitIdx + 1);
1146
- return `new ${typeName}(${args.join(", ")})`;
1377
+ const inst =
1378
+ maxExplicitIdx < 0
1379
+ ? `new ${typeName}()`
1380
+ : `new ${typeName}(${values.slice(0, maxExplicitIdx + 1).join(", ")})`;
1381
+ return inst;
1147
1382
  },
1148
1383
 
1149
1384
  _genSliceOrArrayLit(t, expr) {
@@ -1178,6 +1413,14 @@ export const expressionGenMethods = {
1178
1413
  return "{ _buf: [] }";
1179
1414
  if (typeName === "strings.Builder" || typeName === "Builder")
1180
1415
  return '{ _buf: "" }';
1416
+ if (typeName === "simd.F32x4" || typeName === "F32x4") {
1417
+ this.useHelper("simd");
1418
+ const a = expr.elems[0] ? this.genExpr(expr.elems[0]) : "0";
1419
+ const b = expr.elems[1] ? this.genExpr(expr.elems[1]) : "0";
1420
+ const c = expr.elems[2] ? this.genExpr(expr.elems[2]) : "0";
1421
+ const d = expr.elems[3] ? this.genExpr(expr.elems[3]) : "0";
1422
+ return `__simd_f32x4(${a}, ${b}, ${c}, ${d})`;
1423
+ }
1181
1424
  if (typeName && this.structNames.has(typeName))
1182
1425
  return this._genPositionalStructLit(typeName, expr);
1183
1426
  if (typeName && this.namedWrapperNames.has(typeName))
@@ -1220,13 +1463,13 @@ export const expressionGenMethods = {
1220
1463
  const namedType = this.checker?.types.get(typeName);
1221
1464
  const u = namedType?.underlying;
1222
1465
  if (u?.kind === "map") {
1223
- return `new ${typeName}({ ${this._genMapLitEntries(expr.elems)} })`;
1466
+ return `{ ${this._genMapLitEntries(expr.elems)} }`;
1224
1467
  }
1225
1468
  // Default: slice
1226
1469
  const elems = expr.elems
1227
1470
  .map((e) => this.genValueExpr(e.kind === "KeyValueExpr" ? e.value : e))
1228
1471
  .join(", ");
1229
- return `new ${typeName}([${elems}])`;
1472
+ return `[${elems}]`;
1230
1473
  },
1231
1474
 
1232
1475
  // ── Helpers ───────────────────────────────────────────────────
@@ -1268,11 +1511,14 @@ export const expressionGenMethods = {
1268
1511
  },
1269
1512
 
1270
1513
  // Returns true if the AST node contains a function call (side-effect risk).
1271
- _hasCallExpr(node) {
1514
+ _hasCallExpr(node, seen = new Set()) {
1272
1515
  if (!node || typeof node !== "object") return false;
1516
+ if (seen.has(node)) return false;
1517
+ seen.add(node);
1273
1518
  if (node.kind === "CallExpr") return true;
1274
- for (const v of Object.values(node)) {
1275
- if (v && typeof v === "object" && this._hasCallExpr(v)) return true;
1519
+ for (const [k, v] of Object.entries(node)) {
1520
+ if (k.startsWith("_") || k === "scope") continue;
1521
+ if (v && typeof v === "object" && this._hasCallExpr(v, seen)) return true;
1276
1522
  }
1277
1523
  return false;
1278
1524
  },
@@ -1288,6 +1534,15 @@ export const expressionGenMethods = {
1288
1534
  if (entry[0]) this.useHelper(entry[0]);
1289
1535
  return entry[1];
1290
1536
  }
1537
+ const named = this.checker?.types.get(name);
1538
+ const u = named?.kind === "named" ? named.underlying : null;
1539
+ if (
1540
+ u &&
1541
+ u.kind !== "struct" &&
1542
+ u.kind !== "interface" &&
1543
+ u.kind !== "named"
1544
+ )
1545
+ return this.zeroValueForType(u);
1291
1546
  name = this._dequalify(name);
1292
1547
  if (this.structNames.has(name)) return `new ${name}()`;
1293
1548
  return "null";
@@ -1376,18 +1631,10 @@ export const expressionGenMethods = {
1376
1631
  // Emit a JS boolean expression that checks whether `val` matches type node `t`.
1377
1632
  _typeCheckExpr(typeNode, val) {
1378
1633
  if (!typeNode) return "true";
1379
- if (typeNode.kind === "TypeName") {
1380
- const name = typeNode.name;
1381
- if (TYPEOF_NUMBER_NAMES.has(name)) return `typeof ${val} === "number"`;
1382
- if (name === "string") return `typeof ${val} === "string"`;
1383
- if (name === "bool") return `typeof ${val} === "boolean"`;
1384
- if (name === "nil") return `${val} === null`;
1385
- if (name === "error")
1386
- return `(typeof ${val} === "object" && ${val} !== null && typeof ${val}.Error === "function")`;
1387
- if (this.structNames.has(name))
1388
- return `(${val} instanceof ${name} && ${val}.__p !== true)`;
1389
- return "true"; // unknown type — can't check at runtime
1390
- }
1634
+ if (typeNode.kind === "TypeName")
1635
+ return this._typeNameCheckExpr(typeNode.name, val);
1636
+ const extended = this._namedOrIfaceCheckExpr(typeNode, val);
1637
+ if (extended) return extended;
1391
1638
  if (
1392
1639
  typeNode.kind === "PointerType" &&
1393
1640
  typeNode.base?.kind === "TypeName" &&
@@ -1459,6 +1706,12 @@ export const expressionGenMethods = {
1459
1706
  return expr.op === "==" ? cmp : `!${cmp}`;
1460
1707
  }
1461
1708
 
1709
+ if (this._isIfaceEquality(expr)) {
1710
+ this.useHelper("ifeq");
1711
+ const cmp = `__ifeq(${this._genIfaceOperand(expr.left)}, ${this._genIfaceOperand(expr.right)})`;
1712
+ return expr.op === "==" ? cmp : `!${cmp}`;
1713
+ }
1714
+
1462
1715
  if (this.strict && this._needsStrictOp(expr.op, expr._type)) {
1463
1716
  const l = this.genExpr(expr.left);
1464
1717
  const r = this.genExpr(expr.right);
@@ -1493,6 +1746,31 @@ export const expressionGenMethods = {
1493
1746
  );
1494
1747
  },
1495
1748
 
1749
+ _isIfaceType(t) {
1750
+ return isAny(t) || this._isInterfaceType(t);
1751
+ },
1752
+
1753
+ // Interface comparisons go through __ifeq so boxed named values compare by
1754
+ // dynamic type and value, as in Go (`err == ErrNotFound`).
1755
+ _isIfaceEquality(expr) {
1756
+ if (expr.op !== "==" && expr.op !== "!=") return false;
1757
+ if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
1758
+ return false;
1759
+ return (
1760
+ this._isIfaceType(expr.left._type) || this._isIfaceType(expr.right._type)
1761
+ );
1762
+ },
1763
+
1764
+ // A concrete operand compared with an interface is converted to an interface first.
1765
+ _genIfaceOperand(e) {
1766
+ if (e._ifaceBox || this._isIfaceType(e._type)) return this.genExpr(e);
1767
+ const isPtr = e._type?.kind === "pointer";
1768
+ const named = isPtr ? e._type.base : e._type;
1769
+ if (this._namedBoxInfo(named))
1770
+ return this._genNamedIfaceBox(this.genExpr(e), named, isPtr);
1771
+ return this.genExpr(e);
1772
+ },
1773
+
1496
1774
  _isIntDivision(expr) {
1497
1775
  return (
1498
1776
  expr.op === "/" &&
@@ -1518,6 +1796,7 @@ export const expressionGenMethods = {
1518
1796
  )
1519
1797
  return null;
1520
1798
  if (this._isStructEquality(expr) || this._isIntDivision(expr)) return null;
1799
+ if (this._isIfaceEquality(expr)) return null;
1521
1800
  if (expr.op === "&^") return "&";
1522
1801
  if (expr.op === "==" || expr.op === "!=") {
1523
1802
  if (this._isNilLiteral(expr.left) || this._isNilLiteral(expr.right))
@@ -1542,8 +1821,13 @@ export const expressionGenMethods = {
1542
1821
  this.useHelper("error");
1543
1822
  return `__error(${inner})`;
1544
1823
  }
1545
- if (target && this.namedWrapperNames.has(target))
1546
- return `new ${target}(${inner})`;
1824
+ if (target && this.namedWrapperNames.has(target)) {
1825
+ const named = this.checker?.types.get(target);
1826
+ const u = named?.underlying;
1827
+ return u?.kind === "basic"
1828
+ ? this._genPrimitiveConversion(expr, inner, u.name)
1829
+ : inner;
1830
+ }
1547
1831
  return this._genPrimitiveConversion(expr, inner, target);
1548
1832
  },
1549
1833
 
@@ -1564,6 +1848,8 @@ export const expressionGenMethods = {
1564
1848
  if (elem === "byte" || elem === "uint8") {
1565
1849
  if (this._isStringExprType(expr.expr._type))
1566
1850
  return `new TextEncoder().encode(${inner})`;
1851
+ if (isAny(expr.expr._type) || !expr.expr._type)
1852
+ return `(typeof ${inner} === "string" ? new TextEncoder().encode(${inner}) : new Uint8Array(${inner}))`;
1567
1853
  return `new Uint8Array(${inner})`;
1568
1854
  }
1569
1855
  const typedCtor = typedArrayConstructorForElem(
@@ -1581,10 +1867,10 @@ export const expressionGenMethods = {
1581
1867
  },
1582
1868
 
1583
1869
  _genStringConversion(srcType, inner) {
1584
- if (srcType && this.isIntType(srcType))
1585
- return `String.fromCodePoint(${inner})`;
1586
- if (srcType?.kind === "slice") {
1587
- const elem = srcType.elem?.name;
1870
+ const base = srcType?.kind === "named" ? srcType.underlying : srcType;
1871
+ if (base && this.isIntType(base)) return `String.fromCodePoint(${inner})`;
1872
+ if (base?.kind === "slice") {
1873
+ const elem = base.elem?.name;
1588
1874
  if (elem === "byte" || elem === "uint8")
1589
1875
  return `new TextDecoder().decode(new Uint8Array(${inner}))`;
1590
1876
  if (elem === "rune" || elem === "int" || elem === "int32")