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.
- package/README.md +35 -39
- package/package.json +37 -4
- package/src/backend/js/expressions.js +323 -37
- package/src/backend/js/index.js +137 -49
- package/src/backend/js/runtime.js +570 -6
- package/src/backend/js/statements.js +36 -6
- package/src/backend/js/stdlib/errors.js +5 -0
- package/src/backend/js/stdlib/index.js +12 -0
- package/src/backend/js/stdlib/json.js +972 -0
- package/src/backend/js/stdlib/simd.js +24 -0
- package/src/backend/wasm/boundary.js +18 -0
- package/src/backend/wasm/emit-builtins.js +17 -0
- package/src/backend/wasm/emit-exprs.js +11 -0
- package/src/backend/wasm/emit.js +9 -0
- package/src/backend/wasm/encode.js +85 -0
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/stdlib/index.js +3 -0
- package/src/backend/wasm/stdlib/simd.js +234 -0
- package/src/backend/wasm/types.js +21 -0
- package/src/backend/wasm/wat.js +37 -0
- package/src/dts-parser.js +71 -14
- package/src/lower/boxing.js +1 -6
- package/src/lower/embedding.js +2 -1
- package/src/parser/struct-tag.js +144 -0
- package/src/parser/types.js +63 -6
- package/src/typechecker/assignability.js +45 -0
- package/src/typechecker/expressions.js +66 -14
- package/src/typechecker/index.js +35 -8
- package/src/typechecker/resolve.js +151 -2
- package/src/typechecker/statements.js +68 -9
- package/src/typechecker/stdlib/extended.js +37 -0
- package/src/typechecker/stdlib/simd.js +73 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +18 -3
- package/src/vendor.js +86 -11
|
@@ -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
|
|
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
|
|
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
|
-
(
|
|
711
|
-
(
|
|
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} ? ${
|
|
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} ? [${
|
|
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
|
|
934
|
+
const targetType = expr._conversionTargetType;
|
|
768
935
|
const arg = this.genExpr(expr.args[0]);
|
|
769
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
return
|
|
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 `
|
|
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 `
|
|
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.
|
|
1275
|
-
if (
|
|
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
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1585
|
-
|
|
1586
|
-
if (
|
|
1587
|
-
const elem =
|
|
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")
|